| Home | Libraries | People | FAQ | More

## Annex: Alternatives

This section presents some alternative and related work to Boost.ScopeExit.

#### Try-Catch

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:

1. The `passwd` object is defined outside of the `try` block because this object is required inside the `catch` block to close the file.
2. The `passwd` object is not fully constructed until after the `open` member function returns.
3. If opening throws, the `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

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.

#### Scope Guards

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:

1. Boost.Lambda expressions are hard to write correctly (e.g., overloaded functions must be explicitly casted, as demonstrated in the example above).
2. The condition in the `if_` expression refers to `commit` variable indirectly through the `_1` placeholder reducing readability.
3. Setting a breakpoint inside `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, &currency]() {
            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, &currency) {
        if(currency_rate_inserted && !commit) rates.erase(currency);
    } BOOST_SCOPE_EXIT_END

    // ...

    commit = true;

#### The D Programming Language

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).

#### C++11 Lambdas

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.