| Home | Libraries | People | FAQ | More

## Tutorial

Imagine that you want to make many modifications to data members of the `World` class in the `World::addPerson` function. You start with adding a new `Person` object to a vector of persons:

    void World::addPerson(Person const& person) {
        bool commit = false;
        m_persons.push_back(person);  // (1) direct action

Some operation 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, last added person must be deleted from `m_persons` when the function throws. All you need is to define a delayed action (release of a resource) right after the direct action (resource acquisition):

    void World::addPerson(Person const& aPerson) {
        bool commit = false;
        m_persons.push_back(aPerson);  // (1) direct action
        BOOST_SCOPE_EXIT( (&commit)(&m_persons) )
        {
            if(!commit)
                m_persons.pop_back(); // (2) rollback action
        } BOOST_SCOPE_EXIT_END

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

        commit = true;                // (4) turn all rollback actions into no-op
    }

The block below point `(1)` is a ScopeExit declaration. Unlike point `(1)`, an execution of the 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.

The ScopeExit declaration starts with `BOOST_SCOPE_EXIT` macro invocation which accepts Boost.Preprocessor sequence of captured variables. If a capture starts with the ampersand sign `&`, a reference to the captured variable will be available inside the ScopeExit body; otherwise, a copy of the variable will be made after the point `(1)` and only the copy will be available inside the body.

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

Consider a more complex case where `World::addPerson` can save intermediate states at some points and roll back to the last saved state. You can use `Person::m_evolution` to store a version of changes and increment it to cancel all rollback actions associated with those changes.

If you pass a current value of `m_evolution` stored in the `checkpoint` variable by value, it will remain unchanged until the end of aa scope and you can compare it with the final value of the `m_evolution`. If the latter wasn't incremented since you saved it, the rollback action inside the block should be executed:

    void World::addPerson(Person const& aPerson) {
        m_persons.push_back(aPerson);

        // This block must be no-throw
        Person& person = m_persons.back();
        Person::evolution_t checkpoint = person.m_evolution;

        BOOST_SCOPE_EXIT( (checkpoint)(&person)(&m_persons) )
        {
            if(checkpoint == person.m_evolution)
                m_persons.pop_back();
        } BOOST_SCOPE_EXIT_END

        // ...

        checkpoint = ++person.m_evolution;

        // Assign new id to the person
        World::id_t const prev_id = person.m_id;
        person.m_id = m_next_id++;
        BOOST_SCOPE_EXIT( (checkpoint)(&person)(&m_next_id)(prev_id) )
        {
            if(checkpoint == person.m_evolution) {
                m_next_id = person.m_id;
                person.m_id = prev_id;
            }
        } BOOST_SCOPE_EXIT_END

        // ...

        checkpoint = ++person.m_evolution;
    }

Full code listing can be found in world.cpp.