| Home | Libraries | People | FAQ | More

## Tutorial

This section illustrates how to use this library.

### Capturing Variables

Imagine that we want to make many modifications to data members of some `world` class in its `world::add_person` member function. We start with adding a new `person` object to a vector of persons:

    void world::add_person(person const& a_person) {
        bool commit = false;

        persons_.push_back(a_person);           // (1) direct action
        ...

Some operations down the road may throw an exception and all changes to involved objects should be rolled back. This all-or-nothing semantic is also known as strong guarantee.

In particular, the last added person must be deleted from `persons_` if the function throws. All we need is to define a delayed action (release of a resource) right after the direct action (resource acquisition). For example (see also `world.cpp`):

    void world::add_person(person const& a_person) {
        bool commit = false;

        persons_.push_back(a_person);           // (1) direct action
        // Following block is executed when the enclosing scope exits.
        BOOST_SCOPE_EXIT(&commit, &persons_) {
            if(!commit) persons_.pop_back();    // (2) rollback action
        } BOOST_SCOPE_EXIT_END

        // ...                                  // (3) other operations

        commit = true;                          // (4) disable rollback actions
    }

The block below point `(1)` is a Boost.ScopeExit declaration. Unlike point `(1)`, an execution of the Boost.ScopeExit body will be delayed until the end of the current scope. In this case it will be executed either after point `(4)` or on any exception. (On various versions of the GCC compiler, it is necessary to use `BOOST_SCOPE_EXIT_TPL` instead of `BOOST_SCOPE_EXIT` within templates, see later in this section for details.)

The Boost.ScopeExit declaration starts with the `BOOST_SCOPE_EXIT` macro invocation which accepts a comma-separated list of captured variables (a Boost.Preprocessor sequence is also accepted for compilers that do not support variadic macros and for backward compatibility with older versions of this library, see the No Variadic Macros section). If a capture starts with the ampersand sign `&`, a reference to the captured variable will be available inside the Boost.ScopeExit body; otherwise, a copy of the variable will be made after the Boost.ScopeExit declaration at point `(1)` and only the copy will be available inside the body (in this case, the captured variable's type must be `CopyConstructible`).

In the example above, the variables `commit` and `persons_` are captured by reference because the final value of the `commit` variable should be used to determine whether to execute rollback actions or not, and the action should modify the `persons_` object, not its copy. This is the most common case but passing a variable by value is sometimes useful as well.

Finally, the end of the Boost.ScopeExit body must be marked by the `BOOST_SCOPE_EXIT_END` macro which must follow the closing curly bracket `}` of the Boost.ScopeExit body.

| Important
In order to comply with the STL exception safety requirements, the Boost.ScopeExit body must never throw (because the library implementation executes the body within a destructor call). This is true for all Boost.ScopeExit macros (including `BOOST_SCOPE_EXIT_TPL` and `BOOST_SCOPE_EXIT_ALL` seen below) on both C++03 and C++11.

Consider a more complex example where `world::add_person` can save intermediate states at some point and roll back to the last saved state. We use `person::evolution_` to store a version of the changes and increment it to cancel all rollback actions associated with those changes. If we pass a current value of `evolution_` stored in the `checkpoint` variable by value, it remains unchanged within the Boost.ScopeExit body so we can compare it with the final value of `evolution_`. If the latter was not incremented since we saved it, the rollback action inside the Boost.ScopeExit body should be executed. For example (see also `world_checkpoint.cpp`):

    void world::add_person(person const& a_person) {
        persons_.push_back(a_person);

        // This block must be no-throw.
        person& p = persons_.back();
        person::evolution_t checkpoint = p.evolution;
        BOOST_SCOPE_EXIT(checkpoint, &p, &persons_) {
            if(checkpoint == p.evolution) persons_.pop_back();
        } BOOST_SCOPE_EXIT_END

        // ...

        checkpoint = ++p.evolution;

        // Assign new identifier to the person.
        person::id_t const prev_id = p.id;
        p.id = next_id_++;
        BOOST_SCOPE_EXIT(checkpoint, &p, &next_id_, prev_id) {
            if(checkpoint == p.evolution) {
                next_id_ = p.id;
                p.id = prev_id;
            }
        } BOOST_SCOPE_EXIT_END

        // ...

        checkpoint = ++p.evolution;
    }

When multiple Boost.ScopeExit blocks are declared within the same enclosing scope, the Boost.ScopeExit bodies are executed in the reversed order of their declarations.

### Capturing The Object `this`

Within a member function, it is also possible to capture the object `this`. However, the special symbol `this_` must be used instead of `this` in the Boost.ScopeExit declaration and body to capture and access the object. For example (see also `world_this.cpp`):

    BOOST_SCOPE_EXIT(&commit, this_) { // Capture object `this_`.
        if(!commit) this_->persons_.pop_back();
    } BOOST_SCOPE_EXIT_END

It is not possible to capture the object `this_` by reference because C++ does not allow to take a reference to `this`. If the enclosing member function is constant then the captured object will also be constant, otherwise the captured object will be mutable.

### Capturing No Variable

A Boost.ScopeExit declaration can also capture no variable. In this case, the list of captured variables is replaced by the `void` keyword (similarly to the C++ syntax that allows to declare a function with no parameter using `result-type function-name``(void)`). [3] For example, this can be useful when the Boost.ScopeExit body only needs to access global variables (see also `world_void.cpp`):

    struct world_t {
        std::vector<person> persons;
        bool commit;
    } world; // Global variable.

    void add_person(person const& a_person) {
        world.commit = false;
        world.persons.push_back(a_person);

        BOOST_SCOPE_EXIT(void) { // No captures.
            if(!world.commit) world.persons.pop_back();
        } BOOST_SCOPE_EXIT_END

        // ...

        world.commit = true;
    }

(Both compilers with and without variadic macros use this same syntax for capturing no variable, see the No Variadic Macros section for more information.)

### Capturing All Variables (C++11 Only)

On C++11 compliers, it is also possible to capture all the variables in scope without naming them one-by-one using the special macro `BOOST_SCOPE_EXIT_ALL` instead of `BOOST_SCOPE_EXIT`. [4]

Following the same syntax adopted by C++11 lambda functions, the `BOOST_SCOPE_EXIT_ALL` macro accepts a comma-separated list of captures which must start with either `&` or `=` to capture all variables in scope respectively by reference or by value (note that no variable name is specified by these leading captures). Additional captures of specific variables can follow the leading `&` or `=` and they will override the default reference or value captures. For example (see also `world_checkpoint_all.cpp`):

    void world::add_person(person const& a_person) {
        persons_.push_back(a_person);

        // This block must be no-throw.
        person& p = persons_.back();
        person::evolution_t checkpoint = p.evolution;
        // Capture all by reference `&`, but `checkpoint` and `this` (C++11 only).
        BOOST_SCOPE_EXIT_ALL(&, checkpoint, this) { // Use `this` (not `this_`).
            if(checkpoint == p.evolution) this->persons_.pop_back();
        }; // Use `;` (not `SCOPE_EXIT_END`).

        // ...

        checkpoint = ++p.evolution;

        // Assign new identifier to the person.
        person::id_t const prev_id = p.id;
        p.id = next_id_++;
        // Capture all by value `=`, but `p` (C++11 only).
        BOOST_SCOPE_EXIT_ALL(=, &p) {
            if(checkpoint == p.evolution) {
                this->next_id_ = p.id;
                p.id = prev_id;
            }
        };

        // ...

        checkpoint = ++p.evolution;
    }

The first Boost.ScopeExit declaration captures all variables in scope by reference but the variable `checkpoint` and the object `this` which are explicitly captured by value (in particular, `p` and `persons_` are implicitly captured by reference here). The second Boost.ScopeExit declaration instead captures all variables in scope by value but `p` which is explicitly captured by reference (in particular, `checkpoint`, `prev_id`, and `this` are implicitly captured by value here).

Note that the `BOOST_SCOPE_EXIT_ALL` macro follows the C++11 lambda function syntax which is unfortunately different from the `BOOST_SCOPE_EXIT` macro syntax. In particular:

1. The `BOOST_SCOPE_EXIT_ALL` macro cannot capture data members without capturing the object `this` while that is not the case for `BOOST_SCOPE_EXIT`. [5]
2. The `BOOST_SCOPE_EXIT_ALL` macro captures the object in scope using `this` instead of `this_`. [6]
3. The `BOOST_SCOPE_EXIT_ALL` body is terminated by a semicolon `;` instead than by the `BOOST_SCOPE_EXIT_END` macro.

If programmers define the configuration macro `BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS` then the `BOOST_SCOPE_EXIT` macro implementation will use C++11 lamda functions and the `BOOST_SCOPE_EXIT` macro will follow the same syntax of `BOOST_SCOPE_EXIT_ALL` macro, which is the C++11 lambda function syntax. However, `BOOST_SCOPE_EXIT` will no longer be backward compatible and older code using `BOOST_SCOPE_EXIT` might no longer compile (if data members were explicitly captured).

### Template Workaround (GCC)

Various versions of the GCC compiler do not compile `BOOST_SCOPE_EXIT` inside templates (see the Reference section for more information). As a workaround, `BOOST_SCOPE_EXIT_TPL` should be used instead of `BOOST_SCOPE_EXIT` in these cases. [7] The `BOOST_SCOPE_EXIT_TPL` macro has the exact same syntax of `BOOST_SCOPE_EXIT`. For example (see also `world_tpl.cpp`):

    template<typename Person>
    void world<Person>::add_person(Person const& a_person) {
        bool commit = false;
        persons_.push_back(a_person);

        BOOST_SCOPE_EXIT_TPL(&commit, this_) { // Use `_TPL` postfix.
            if(!commit) this_->persons_.pop_back();
        } BOOST_SCOPE_EXIT_END

        // ...

        commit = true;
    }

It is recommended to always use `BOOST_SCOPE_EXIT_TPL` within templates so to maximize portability among different compilers.

### Same Line Expansions

In general, it is not possible to expand the `BOOST_SCOPE_EXIT`, `BOOST_SCOPE_EXIT_TPL`, `BOOST_SCOPE_EXIT_END`, and `BOOST_SCOPE_EXIT_ALL` macros multiple times on the same line. [8]

Therefore, this library provides additional macros `BOOST_SCOPE_EXIT_ID`, `BOOST_SCOPE_EXIT_ID_TPL`, `BOOST_SCOPE_EXIT_END_ID`, and `BOOST_SCOPE_EXIT_ALL_ID` which can be expanded multiple times on the same line as long as programmers specify a unique identifiers as the macros' first parameters. The unique identifier can be any token (not just numeric) that can be concatenated by the C++ preprocessor (e.g., `scope_exit_number_1_at_line_123`). [9]

The `BOOST_SCOPE_EXIT_ID`, `BOOST_SCOPE_EXIT_ID_TPL`, and `BOOST_SCOPE_EXIT_ALL_ID` macros accept a capture list using the exact same syntax as `BOOST_SCOPE_EXIT` and `BOOST_SCOPE_EXIT_ALL` respectively. For example (see also `same_line.cpp`):

    #define SCOPE_EXIT_INC_DEC(variable, offset) \
        BOOST_SCOPE_EXIT_ID(BOOST_PP_CAT(inc, __LINE__), /* unique ID */ \
                &variable, offset) { \
            variable += offset; \
        } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(inc, __LINE__)) \
        \
        BOOST_SCOPE_EXIT_ID(BOOST_PP_CAT(dec, __LINE__), \
                &variable, offset) { \
            variable -= offset; \
        } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(dec, __LINE__))

    #define SCOPE_EXIT_INC_DEC_TPL(variable, offset) \
        BOOST_SCOPE_EXIT_TPL_ID(BOOST_PP_CAT(inc, __LINE__), \
                &variable, offset) { \
            variable += offset; \
        } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(inc, __LINE__)) \
        \
        BOOST_SCOPE_EXIT_TPL_ID(BOOST_PP_CAT(dec, __LINE__), \
                &variable, offset) { \
            variable -= offset; \
        } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(dec, __LINE__))

    #define SCOPE_EXIT_ALL_INC_DEC(variable, offset) \
        BOOST_SCOPE_EXIT_ALL_ID(BOOST_PP_CAT(inc, __LINE__), \
                =, &variable) { \
            variable += offset; \
        }; \
        BOOST_SCOPE_EXIT_ALL_ID(BOOST_PP_CAT(dec, __LINE__), \
                =, &variable) { \
            variable -= offset; \
        };

    template<typename T>
    void f(T& x, T& delta) {
        SCOPE_EXIT_INC_DEC_TPL(x, delta) // Multiple scope exits on same line.
        BOOST_TEST(x == 0);
    }

    int main(void) {
        int x = 0, delta = 10;

        {
            SCOPE_EXIT_INC_DEC(x, delta) // Multiple scope exits on same line.
        }
        BOOST_TEST(x == 0);

        f(x, delta);

    #ifndef BOOST_NO_LAMBDAS
        {
            SCOPE_EXIT_ALL_INC_DEC(x, delta) // Multiple scope exits on same line.
        }
        BOOST_TEST(x == 0);
    #endif // LAMBDAS

        return boost::report_errors();
    }

As shown by the example above, the `BOOST_SCOPE_EXIT_ID`, `BOOST_SCOPE_EXIT_ID_TPL`, `BOOST_SCOPE_EXIT_END_ID`, and `BOOST_SCOPE_EXIT_ALL_ID` macros are especially useful when it is necessary to invoke them multiple times within user-defined macros (because the C++ preprocessor expands all nested macros on the same line).

[3] Rationale. Unfortunately, it is not possible to simply invoke the Boost.ScopeExit macro with no parameters as in `BOOST_SCOPE_EXIT()` because the C++ preprocessor cannot detect emptiness of a macro parameter when the parameter can start with a non-alphanumeric symbol (which is the case when capturing a variable by reference `&variable`).

[4] Rationale. The `BOOST_SCOPE_EXIT_ALL` macro is only defined on C++11 compilers for which the Boost.Config macro `BOOST_NO_LAMBDAS` is not defined. Using `BOOST_SCOPE_EXIT_ALL` on C++03 compilers for which `BOOST_NO_LAMBDAS` is defined will generate (possibly cryptic) compiler errors. Note that a new macro `BOOST_SCOPE_EXIT_ALL` needed to be introduced instead of reusing `BOOST_SCOPE_EXIT` because `BOOST_SCOPE_EXIT(&)` and `BOOST_SCOPE_EXIT(=)` cannot be distinguished from `BOOST_SCOPE_EXIT(void)` or `BOOST_SCOPE_EXIT(this_)` using the C++ preprocessor given that the symbols `&` and `=` are neither prefxied nor postfixed by alphanumeric tokens (this is not an issue for `BOOST_SCOPE_EXIT_ALL` which always has the non-alphanumeric `&` or `=` as the first capture so the first capture tokens are simply never compared with neither `void` nor `this_` for this macro).

[5] At present, there seems to be some discussion to allow C++11 lambda functions to capture data members without capturing the object `this`. If the C++11 standard were changed to allow this, the `BOOST_SCOPE_EXIT_ALL` macro syntax could be extended to be a superset of the `BOOST_SCOPE_EXIT` macro while keeping full backward compatibility.

[6] On compilers that support the use of the `typename` outside templates as allowed by the C++11 standard, `BOOST_SCOPE_EXIT_ALL` can use both `this` and `this_` to capture the object in scope (notably, this is not the case for the MSVC 10.0 compiler).

[7] Rationale. GCC versions compliant with C++11 do not present this issue and given that `BOOST_SCOPE_EXIT_ALL` is only available on C++11 compilers, there is no need for a `BOOST_SCOPE_EXIT_ALL_TPL` macro.

[8] Rationale. The library macros internally use `__LINE__` to generate unique identifiers. Therefore, if the same macro is expanded more than on time on the same line, the generated identifiers will no longer be unique and the code will not compile. (This restriction does not apply to MSVC and other compilers that provide the non-standard `__COUNTER__` macro.)

[9] Because there are restrictions on the set of tokens that the C++ preprocessor can concatenate and because not all compilers correctly implement these restrictions, it is in general recommended to specify unique identifiers as a combination of alphanumeric tokens.