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

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.

Read the rest of this entry »