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 »