Scope based execution of arbitrary code with C++11

Imagine the following peace of code (which is really simple for the sake of the example :)

for (entity in entities)
{
    glPushMatrix();

    // do some stuff

    glPopMatrix();
}

The basic idea here is that you need to maintain the integrity of a states’ stack in a loop. But what happens if you have some tests that allow to break, continue or return :

for (entity in entities)
{
    glPushMatrix();

    // do some stuff

    if (entity->NeedToDoOtherStuff() == false)
    {
        continue;
    }

    // do some other stuff

    glPopMatrix();
}

Wrong. You didn’t pop the matrix, everything gets corrupted. You need to add a glPopMatrix() right before continue. And the more of those special cases, the more glPopMatrix you need to add. And if you have more states to push / pop, you need to duplicate those too.

This scenario happens a lot in real applications. If you allocate some temporary data to work with, you need to cleanup before exiting the function, and if you have many exit points (error checks, etc.) you end up duplicating cleanup code everywhere, which is error prone and fastidious. You can also use goto’s :)

Here is a little templated class which solves this problem using 2 features of C++11 : lambdas and functional.

class Cleanup
{
public:
    template< typename T >
    Cleanup(const T & code)
        : m_Code(code)
    {
    }
    ~Cleanup(void)
    {
        m_Code();
    }
private:
    std::function< void (void) > m_Code;
};

This class can be used like this :

for (entity in entities)
{
    glPushMatrix();
    Cleanup popMatrix([]() { glPopMatrix(); });

    // do your stuff
    // make some tests, continue or break depending on the reults
    // do other stuff
}

As soon as the Cleanup instance goes out of scope (which happens after an iteration is done, or when we break or continue) it will execute the code that you gave it. I find this particularly useful :

  • no longer crashes when you add an exit point, special case, etc. and forget to copy / paste the cleanup code.
  • the cleanup code is defined right at the top of the scope, which in my experience makes it really hard to forget updating it, if you need to add more states, more intermediate data that need cleanup, etc.