|  | Home | Libraries | People | FAQ | More | 
This section illustrates how to use this library.
        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]](../../../../../doc/src/images/important.png) | 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  | 
        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.
        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.
      
        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.)
        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:
      
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]
          BOOST_SCOPE_EXIT_ALL
            macro captures the object in scope using this
            instead of this_. [6]
          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).
      
        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.
      
        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_ID_TPL(BOOST_PP_CAT(inc, __LINE__), \ &variable, offset) { \ variable += offset; \ } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(inc, __LINE__)) \ \ BOOST_SCOPE_EXIT_ID_TPL(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_CXX11_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_CXX11_LAMBDAS
          is not defined. Using BOOST_SCOPE_EXIT_ALL
          on C++03 compilers for which BOOST_NO_CXX11_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.