boost.scope_guard

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.

Boost.ScopeGuard

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

Introduction

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

API Reference

Headers

Everything is accessible via

#include <boost/scope_guard.hpp>

boost::scope_guard, boost::scope_guard_failure and boost::scope_guard_success

Constructors
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_wrappers (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.

Destructors
// (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,

  1. Always.
  2. Iff the destructor was called due to stack unwinding
    (i.e. when an exception is thrown).
  3. Iff the destructor was called due to flow of control leaving the scope
    normally.

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.

Limitations

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.