scope(exit), scope(failure) and scope(success)

by Malte Skarupke

scope(exit) is a great idea from the D programming language. I’ll refer to the D website for motivating examples because they do a good job. There have been many implementations in C++ and Ignacio Castaño showed an elegant implementation using lambdas which is using a bit too many macros for my taste. (it’s using one) There is also an exception-safe implementation by Jon Kalb which doesn’t use the same optimization that Castaño does. I wrote an implementation that combines the benefits of the two.

Also D has scope(exit), scope(failure) and scope(success), where failure runs only if an exception caused stack unwinding, success runs otherwise, and exit runs in any case. And I think it makes sense to offer the complete set. So to keep this post short here is an implementation of all three in C++:

#pragma once

#include <exception>

namespace detail
{

template<typename F>
struct ScopeExitRunner
{
    ScopeExitRunner(const F & to_run)
    try : to_run(to_run)
    {
    }
    catch(...)
    {
        // if the copy constructor for to_run threw,
        // call the argument immediately then rethrow
        to_run();
    }
    ScopeExitRunner(ScopeExitRunner &&) = default;
    ~ScopeExitRunner()
    {
        to_run();
    }
private:
    F to_run;
};

template<typename F>
struct ScopeFailureRunner
{
    ScopeFailureRunner(const F & to_run)
    try : to_run(to_run)
    {
    }
    catch(...)
    {
        to_run();
    }
    ScopeFailureRunner(ScopeFailureRunner &&) = default;
    ~ScopeFailureRunner() noexcept
    {
        static_assert(noexcept(to_run()), "the given function must be noexcept. it will only be run if there was an exception");
        static_assert(noexcept(to_run.~F()), "the given functor must have a noexcept destructor. it is likely that it will be run when there was an exception");
        if (::std::uncaught_exception())
        {
            to_run();
        }
    }
private:
    F to_run;
};

template<typename F>
struct ScopeSuccessRunner
{
    ScopeSuccessRunner(const F & to_run)
        : to_run{to_run}
    {
    }
    ScopeSuccessRunner(ScopeSuccessRunner && other) = default;
    ~ScopeSuccessRunner()
    {
        if (!::std::uncaught_exception())
        {
            to_run();
        }
    }
private:
    F to_run;
};

} // end namespace detail

template<typename T>
detail::ScopeExitRunner<T> AtScopeExit(const T & to_run)
{
    return detail::ScopeExitRunner<T>(to_run);
}
template<typename T>
detail::ScopeFailureRunner<T> AtScopeFailure(const T & to_run)
{
    return detail::ScopeFailureRunner<T>(to_run);
}
template<typename T>
detail::ScopeSuccessRunner<T> AtScopeSuccess(const T & to_run)
{
    return detail::ScopeSuccessRunner<T>(to_run);
}

And you use it for example when dealing with windows functions:

// ...
IUnknown * important = nullptr;
::SomeWindowsFunctionA(/*...*/, &important);
auto release_important = AtScopeExit([important]
{
    important->Release();
});
// ...

This code should be as efficient as if you had placed that call manually at each exit point of your function.

One word about the design decision behind that static_assert(noexcept(to_run())): If the function you give to AtScopeFailure throws an exception then the program would obviously terminate, so I think it’s reasonable to assert on that. It would also make sense to assert in the ScopeExitRunner, but there are many valid uses of AtScopeExit where people know that exceptions don’t happen, and you don’t want to be too annoying to users.

The constructor try/catch block comes from Jon Kalb. As an aside: Watch the videos on Jon Kalb’s website. He will convince you to use exceptions. He promises that exception safe code is easier to read,easier to maintain and understand, easier to write, has no time penalty (sometimes it will be faster than not using exceptions) and is more robust. It’s three hours long but I think it is worth it because he convinced me completely.

After watching his talk I have sometimes looked at code and thought that certain bad coding patterns wouldn’t happen if you were thinking about exceptions. I now think that exception safe coding is one of those things like unit tests: Sure, unit tests are nice and prevent bugs, but a big advantage is that your code has to be more modular if you want to write unit tests. Same thing with exceptions: They are nice and make many cases of error handling safer, but a big advantage is that your code has to be clearer about state when exceptions are around.