|  | Home | Libraries | People | FAQ | More | 
This section presents some alternative and related work to Boost.ScopeExit.
      This is an example of using a badly designed file
      class. An instance of file
      does not close the file in its destructor, a programmer is expected to call
      the close member function explicitly.
      For example (see also try_catch.cpp):
    
file passwd; try { passwd.open("/etc/passwd"); // ... passwd.close(); } catch(...) { std::clog << "could not get user info" << std::endl; if(passwd.is_open()) passwd.close(); throw; }
Note the following issues with this approach:
passwd object is defined
          outside of the try block because
          this object is required inside the catch
          block to close the file.
        passwd object is not
          fully constructed until after the open
          member function returns.
        passwd.close() should not be called, hence the call
          to passwd.is_open().
        
      The Boost.ScopeExit approach does not have
      any of these issues. For example (see also try_catch.cpp):
    
try { file passwd("/etc/passwd"); BOOST_SCOPE_EXIT(&passwd) { passwd.close(); } BOOST_SCOPE_EXIT_END } catch(...) { std::clog << "could not get user info" << std::endl; throw; }
      RAII
      is absolutely perfect for the file
      class introduced above. Use of a properly designed file
      class would look like:
    
try { file passwd("/etc/passwd"); // ... } catch(...) { std::clog << "could not get user info" << std::endl; throw; }
However, using RAII to build up a strong guarantee could introduce a lot of non-reusable RAII types. For example:
persons_.push_back(a_person); pop_back_if_not_commit pop_back_if_not_commit_guard(commit, persons_);
      The pop_back_if_not_commit
      class is either defined out of the scope or as a local class:
    
class pop_back_if_not_commit { bool commit_; std::vector<person>& vec_; // ... ~pop_back_if_not_commit() { if(!commit_) vec_.pop_back(); } };
In some cases strong guarantee can be accomplished with standard utilities:
std::auto_ptr<Person> superman_ptr(new superman()); persons_.push_back(superman_ptr.get()); superman_ptr.release(); // persons_ successfully took ownership
Or with specialized containers such as Boost.PointerContainer or Boost.Multi-Index.
Imagine that a new currency rate is introduced before performing a transaction (see also []):
bool commit = false; std::string currency("EUR"); double rate = 1.3326; std::map<std::string, double> rates; bool currency_rate_inserted = rates.insert(std::make_pair(currency, rate)).second; // Transaction...
      If the transaction does not complete, the currency must be erased from rates. This can be done with ScopeGuard
      and Boost.Lambda (or
      Boost.Phoenix):
    
using namespace boost::lambda; ON_BLOCK_EXIT( if_(currency_rate_inserted && !_1) [ bind( static_cast< std::map<std::string, double>::size_type (std::map<std::string, double>::*)(std::string const&) >(&std::map<std::string, double>::erase) , &rates , currency ) ] , boost::cref(commit) ); // ... commit = true;
Note the following issues with this approach:
if_
          expression refers to commit
          variable indirectly through the _1
          placeholder reducing readability.
        if_[...] requires in-depth knowledge of Boost.Lambda and debugging
          techniques.
        This code will look much better with C++11 lambdas:
ON_BLOCK_EXIT( [currency_rate_inserted, &commit, &rates, ¤cy]() { if(currency_rate_inserted && !commit) rates.erase(currency); } ); // ... commit = true;
      With Boost.ScopeExit we can simply do the
      following (see also scope_guard.cpp):
    
BOOST_SCOPE_EXIT(currency_rate_inserted, &commit, &rates, ¤cy) { if(currency_rate_inserted && !commit) rates.erase(currency); } BOOST_SCOPE_EXIT_END // ... commit = true;
Boost.ScopeExit is similar to scope(exit) feature built into the D programming language.
      A curious reader may notice that the library does not implement scope(success) and scope(failure) of the D
      language. Unfortunately, these are not possible in C++ because failure or success
      conditions cannot be determined by calling std::uncaught_exception
      (see Guru of the Week #47
      for details about std::uncaught_exception and if it has any good
      use at all). However, this is not a big problem because these two D's
      constructs can be expressed in terms of scope(exit)
      and a bool commit
      variable (similarly to some examples presented in the Tutorial
      section).
    
      Using C++11 lambdas, it is relatively easy to implement the Boost.ScopeExit
      construct. For example (see also world_cxx11_lambda.cpp):
    
#include <functional> struct scope_exit { scope_exit(std::function<void (void)> f) : f_(f) {} ~scope_exit(void) { f_(); } private: std::function<void (void)> f_; }; void world::add_person(person const& a_person) { bool commit = false; persons_.push_back(a_person); scope_exit on_exit1([&commit, this](void) { // Use C++11 lambda. if(!commit) persons_.pop_back(); // `persons_` via captured `this`. }); // ... commit = true; }
However, this library allows to program the Boost.ScopeExit construct in a way that is portable between C++03 and C++11 compilers.