Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Alternatives

try-catch

This is an example of using a badly designed File class. An instance of File doesn't close a file in a destructor, a programmer is expected to call the close member function explicitly.

File passwd;
try {
    passwd.open("/etc/passwd");
    // ...
    passwd.close();
}
catch(...) {
    log("could not get user info");
    if(passwd.is_open())
        passwd.close();
    throw;
}

Note the following:

ScopeExit doesn't have any of these problems:

try {
    File passwd("/etc/passwd");
    BOOST_SCOPE_EXIT( (&passwd) ) {
        passwd.close();
    } BOOST_SCOPE_EXIT_END
    // ...
}
catch(...) {
    log("could not get user info");
    throw;
}

RAII

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(...) {
    log("could not get user info");
    throw;
}

However, using RAII to build up a strong guarantee could introduce a lot of non-reusable RAII types. For example:

m_persons.push_back(person);
pop_back_if_not_commit pop_back_if_not_commit_guard(commit, m_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 m_commit;
    std::vector<Person>& m_vec;
    // ...
    ~pop_back_if_not_commit() {
        if(!m_commit)
            m_vec.pop_back();
    }
};

In some cases strong guarantee can be accomplished with standard utilities:

std::auto_ptr<Person> spSuperMan(new Superman); 
m_persons.push_back(spSuperMan.get());
spSuperMan.release(); // m_persons successfully took ownership.

or with specialized containers such as Boost Pointer Container Library or Boost Multi-Index Containers Library.

ScopeGuard

Imagine that you add a new currency rate:

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;

and then continue a transaction. If it cannot be completed, you erase the currency from rates. This is how you can do this with ScopeGuard and Boost.Lambda:

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 that

This code will look much better with native lambda expressions proposed for C++0x:

ON_BLOCK_EXIT(
    [currency_rate_inserted, &commit, &rates, &currency]() -> void
    {
        if(currency_rate_inserted && !commit)
            rates.erase(currency);
    }
);

With ScopeExit you can simply do

BOOST_SCOPE_EXIT( (currency_rate_inserted)(&commit)(&rates)(&currency) )
{
    if(currency_rate_inserted && !commit)
        rates.erase(currency);
} BOOST_SCOPE_EXIT_END

// ...

commit = true;

C++0x

In future releases ScopeExit will take advantages of C++0x features.

BOOST_SCOPE_EXIT(currency_rate_inserted, &commit, &rates, &currency)
{
    if(currency_rate_inserted && !commit)
        rates.erase(currency);
} BOOST_SCOPE_EXIT_END
BOOST_SCOPE_EXIT(currency_rate_inserted, &commit, &rates, &currency)
{
    if(currency_rate_inserted && !commit)
        rates.erase(currency);
};
BOOST_SCOPE_EXIT(&, currency_rate_inserted)
{
    if(currency_rate_inserted && !commit)
        rates.erase(currency);
};

The D Programming Language

ScopeExit is similar to scope(exit) feature built into the D programming language.

A curious reader may notice that the library doesn't implement scope(success) and scope(failure) of the D language. Unfortunately, it's not possible in C++ because failure or success condition cannot be determined by calling std::uncaught_exception. It's not a big problem, though. These two constructs can be expressed in terms of scope(exit) and a bool commit variable as explained in Tutorial. Refer to Guru of the Week #47 for more details about std::uncaught_exception and if it has any good use at all.


PrevUpHomeNext