Skip to content

SergiusTheBest/ScopeExit

Introduction

ScopeExit library provides an efficient and convenient way to execute statements when execution flow leaves current scope. It implements a so-called scope guard idiom and defines 3 type of guards:

  • SCOPE_EXIT - statements are always executed on scope exit
  • SCOPE_SUCCESS - statements are executed on scope exit when no exceptions have been thrown
  • SCOPE_FAILURE - statements are executed when scope is leaving due to an exception

Competing C++ scope exit/guard libraries

Other resources

Sample

Here is a simple sample that prints htlm code. Scope exit blocks make sure that html tags are properly closed:

#include <iostream>
#include <ScopeExit/ScopeExit.h>

using namespace std;

int main()
{
    cout << "<html>" << endl;
    SCOPE_EXIT{ cout << "</html>" << endl; };

    {
        cout << "<head>" << endl;
        SCOPE_EXIT{ cout << "</head>" << endl; };

        cout << "<title>Hello</title>" << endl;
    }

    cout << "<body>" << endl;
    SCOPE_EXIT{ cout << "</body>" << endl; };

    cout << "<h1>Hello World!</h1>" << endl;

    return 0;
}

NOTE: 输出如下:

<html>
<head>
<title>Hello</title>
</head>
<body>
</body>
<h1>Hello World!</h1>
</html>

Read code

ScopeExit/include/ScopeExit/ScopeExit.h

它的实现原理是在object的destructor中调用call back。

#pragma once

//
// Usage:
//
// SCOPE_EXIT{ cout << "hello"; }; // will be called at the scope exit
//

#define SCOPE_EXIT_CAT2(x, y) x##y
#define SCOPE_EXIT_CAT(x, y) SCOPE_EXIT_CAT2(x, y)
#define SCOPE_EXIT const auto SCOPE_EXIT_CAT(scopeExit_, __COUNTER__) = ScopeExit::MakeScopeExit() += [&]

namespace ScopeExit
{
    template<typename F>
    class ScopeExit 
    {
    public:
        explicit ScopeExit(F&& fn) : m_fn(fn)
        {
        }

        ~ScopeExit()
        { 
            m_fn();
        }

        ScopeExit(ScopeExit&& other) : m_fn(std::move(other.m_fn))
        {
        }

    private:
        ScopeExit(const ScopeExit&);
        ScopeExit& operator=(const ScopeExit&);

    private:
        F m_fn;
    };

    struct MakeScopeExit
    {
        template<typename F>
        ScopeExit<F> operator+=(F&& fn)
        {
            return ScopeExit<F>(std::move(fn));
        }
    };
}

需要结合usage来理解SCOPE_EXIT的实现:

SCOPE_EXIT{ cout << "hello"; } 会被替换为:

const auto SCOPE_EXIT_CAT(scopeExit_, __COUNTER__) =  ScopeExit::MakeScopeExit() += [&]{ cout << "hello"; }

SCOPE_EXIT_CAT 用于生成object的name。

ScopeExit/include/ScopeExit/ScopeFailure.h

ScopeExit 类似,所不同的是在destructor中,会通过调用 std::uncaught_exception() 来判断是否抛出了exception。

#pragma once
#include <exception>

//
// Usage:
//
// SCOPE_FAILURE{ cout << "hello"; }; // will be called at the scope exit in case of failure (exception is thrown)
//

#define SCOPE_FAILURE_CAT2(x, y) x##y
#define SCOPE_FAILURE_CAT(x, y) SCOPE_FAILURE_CAT2(x, y)
#define SCOPE_FAILURE const auto SCOPE_FAILURE_CAT(scopeFailure_, __COUNTER__) = ScopeExit::MakeScopeFailure() += [&]

namespace ScopeExit
{
    template<typename F>
    class ScopeFailure 
    {
    public:
        explicit ScopeFailure(F&& fn) : m_fn(fn)
        {
        }

        ~ScopeFailure()
        { 
            if (std::uncaught_exception())
            {
                m_fn();
            }
        }

        ScopeFailure(ScopeFailure&& other) : m_fn(std::move(other.m_fn))
        {
        }

    private:
        ScopeFailure(const ScopeFailure&);
        ScopeFailure& operator=(const ScopeFailure&);

    private:
        F m_fn;
    };

    struct MakeScopeFailure
    {
        template<typename F>
        ScopeFailure<F> operator+=(F&& fn)
        {
            return ScopeFailure<F>(std::move(fn));
        }
    };
}

ScopeExit/include/ScopeExit/ScopeSuccess.h

#pragma once
#include <exception>

//
// Usage:
//
// SCOPE_SUCCESS{ cout << "hello"; }; // will be called at the scope success in case of success (no exception is thrown)
//

#define SCOPE_SUCCESS_CAT2(x, y) x##y
#define SCOPE_SUCCESS_CAT(x, y) SCOPE_SUCCESS_CAT2(x, y)
#define SCOPE_SUCCESS const auto SCOPE_SUCCESS_CAT(scopeSuccess_, __COUNTER__) = ScopeExit::MakeScopeSuccess() += [&]

namespace ScopeExit
{
    template<typename F>
    class ScopeSuccess 
    {
    public:
        explicit ScopeSuccess(F&& fn) : m_fn(fn)
        {
        }

        ~ScopeSuccess()
        { 
            if (!std::uncaught_exception())
            {
                m_fn();
            }
        }

        ScopeSuccess(ScopeSuccess&& other) : m_fn(std::move(other.m_fn))
        {
        }

    private:
        ScopeSuccess(const ScopeSuccess&);
        ScopeSuccess& operator=(const ScopeSuccess&);

    private:
        F m_fn;
    };

    struct MakeScopeSuccess
    {
        template<typename F>
        ScopeSuccess<F> operator+=(F&& fn)
        {
            return ScopeSuccess<F>(std::move(fn));
        }
    };
}