Fully emulates D's scope guard statement feature via C++17's std::uncaught_exceptions() with no dynamic memory allocation and terse syntax via class template deduction. Meant to supersede Boost.ScopeExit.
Fully emulates D’s scope guard statement feature
via C++17’s std::uncaught_exceptions()
with no
dynamic memory allocation and terse syntax via
class template deduction.
Andrei Alexandrescu explains scope guards in detail in his CppCon 2015 talk
Declarative Control Flow.
This library is meant to supersede Boost.ScopeExit.
NOT YET PART OF BOOST
Given a resource that lacks built-in RAII capability, e.g. a C-style
FILE*
, boost::scope_guard
lets you manage its
lifetime in an exception-safe and localized manner:
FILE* f = std::fopen("my/file", "r");
if (!f) { throw std::runtime_error("failed to open file"); }
boost::scope_guard my_guard = [&]{
// Invoked when `my_guard` leaves scope, exactly when `f`'s destructor
// would have been called if it had one.
std::fclose(f);
};
// ...some code that can potentially throw exceptions...
A scope guard can also accept any number of additional arguments that will be
passed to its function upon invocation. This means you can often use
cleanup functions directly, without wrapping them in lambdas:
boost::scope_guard my_guard{std::fclose, f};
Function and arguments are stored by value by default, you can store them by
reference via std::ref
and std::cref
:
std::thread my_thread(compute_something);
boost::scope_guard my_guard{&std::thread::join, std::ref(my_thread)};
Having to name all your scope guard objects so that they don’t conflict with
each other quickly gets tiresome, so there is a
BOOST_SCOPE_GUARD
macro that does this automatically
(however be aware of its limitations):
BOOST_SCOPE_GUARD {std::fclose, f};
Naturally, lambdas are supported as well:
BOOST_SCOPE_GUARD [&]{ my_thread.join(); };
Regular boost::scope_guard
always invokes its
stored cleanup function upon destruction, which may not be desirable. There is
also boost::scope_guard_failure
that invokes its
stored cleanup function only when it is being destroyed due to
stack unwinding (i.e. when an exception is thrown) and
boost::scope_guard_success
that invokes its stored
cleanup function only when it is being destroyed due to flow of control
leaving the scope normally. Naturally, there are corresponding
BOOST_SCOPE_GUARD_FAILURE
and
BOOST_SCOPE_GUARD_SUCCESS
Everything is accessible via
#include <boost/scope_guard.hpp>
boost::scope_guard
, boost::scope_guard_failure
and boost::scope_guard_success
template <typename Fn, typename... Args>
constexpr
boost::scope_guard::scope_guard(Fn&& fn, Args&&... args)
noexcept(/* see description */);
template <typename Fn, typename... Args>
constexpr
boost::scope_guard_failure::scope_guard_failure(Fn&& fn, Args&&... args)
noexcept(/* see description */);
template <typename Fn, typename... Args>
constexpr
boost::scope_guard_success::scope_guard_success(Fn&& fn, Args&&... args)
noexcept(/* see description */);
Stores cleanup function fn
and its arguments args...
after
decaying them and unwrapping
std::reference_wrapper
s (the same way
std::make_tuple
does). Is defined iff decayed and unwrapped
stored cleanup function and its argments are initializable with fn
and args
respectively and stored cleanup function is Callable
with its
stored arguments. Is noexcept
iff initialization of stored cleanup function
and its stored arguments with forwarded fn
and args...
respectively is
noexcept
.
// (1)
boost::scope_guard::~scope_guard()
noexcept(/* see description */);
// (2)
boost::scope_guard_failure::~scope_guard_failure()
noexcept(/* see description */);
// (3)
boost::scope_guard_success::~scope_guard_success()
noexcept(/* see description */);
Forwards and invokes stored cleanup function with its forwarded stored
arguments,
Any exception thrown by the invocation of stored cleanup function will
propagate out of the destructor (and cause std::terminate()
to be called if the destructor was called due to
stack unwinding). Is noexcept
iff invocation of forwarded stored cleanup function with its forwarded stored
arguments is noexcept
.
BOOST_SCOPE_GUARD
, BOOST_SCOPE_GUARD_FAILURE
and BOOST_SCOPE_GUARD_SUCCESS
#define BOOST_SCOPE_GUARD
::boost::scope_guard $some_name$ =
#define BOOST_SCOPE_GUARD_FAILURE
::boost::scope_guard_failure $some_name$ =
#define BOOST_SCOPE_GUARD_SUCCESS
::boost::scope_guard_success $some_name$ =
where $some_name$
is an unspecified unique identifier.
As of C++17, generation of truly unique identifiers in all situations in
standard-conforming and safe manner is not possible. Current implementation
concatenates unspecified token with the value of __LINE__
macro,
so you won’t be able to use these macros inside other macros more than once per
scope, since enclosing macro would expand into a single line, generating
identical names.
When used at namespace scope, it is possible that two scope guards appear in
the same namespace on the same lines of their corresponding source files,
which, too, will generate identical names.