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.