A scope guard runs a deferred action when its scope is left:
-
scope_exit- executes the action on scope exit. -
scope_fail- executes the action if the scope is left during exception unwinding. -
scope_success- executes the action if the scope is not left during exception unwinding.
Normal C++ control flow, including return, break, continue, and exceptions, still destroys the guard. This makes scope guards useful for cleanup and rollback across different exit paths.
- C++11
- Header-only
- Dependency-free
- Thin callback wrapping, no added std::function or virtual table penalties
- No implicitly ignored return, callbacks must return void
- Defer or Scope Guard syntax and "With" syntax
-
#include <scope_guard.hpp> std::fstream file("test.txt"); SCOPE_EXIT{ file.close(); }; // File closes when the enclosing scope is left.
-
persons.push_back(person); // Add the person to the database. SCOPE_FAIL{ persons.pop_back(); }; // Roll back if a later operation throws.
-
Person person{/*...*/}; // ... SCOPE_SUCCESS{ persons.push_back(person); }; // Add the person if the scope exits normally. -
Custom Scope Guard
persons.push_back(person); // Add the person to the database. MAKE_SCOPE_EXIT(rollback) { persons.pop_back(); }; // ... rollback.dismiss(); // Commit the change and cancel the rollback.
persons.push_back(person); // Add the person to the database. auto rollback = scope_guard::make_scope_exit([&]() { persons.pop_back(); }); // ... rollback.dismiss(); // Commit the change and cancel the rollback.
-
With Scope Guard
std::fstream file("test.txt"); WITH_SCOPE_EXIT({ file.close(); }) { // File closes when this block is left. // ... }
Inside
WITH_SCOPE_*,breakandcontinueaffect only this block, not an enclosing loop. Use a regularSCOPE_*guard to control an outer loop.
scope_guard::make_scope_exit(F&& action);- returns a scope_exit guard with the action.SCOPE_EXIT{action};- macro for creating scope_exit with the action.MAKE_SCOPE_EXIT(name) {action};- macro for creating named scope_exit with the action.WITH_SCOPE_EXIT({action}) {/*...*/}- macro for creating a scope with scope_exit with the action.
scope_guard::make_scope_fail(F&& action);- returns a scope_fail guard with the action.SCOPE_FAIL{action};- macro for creating scope_fail with the action.MAKE_SCOPE_FAIL(name) {action};- macro for creating named scope_fail with the action.WITH_SCOPE_FAIL({action}) {/*...*/}- macro for creating a scope with scope_fail with the action.
scope_guard::make_scope_success(F&& action);- returns a scope_success guard with the action.SCOPE_SUCCESS{action};- macro for creating scope_success with the action.MAKE_SCOPE_SUCCESS(name) {action};- macro for creating named scope_success with the action.WITH_SCOPE_SUCCESS({action}) {/*...*/}- macro for creating a scope with scope_success with the action.
DEFER{action};- macro for creating defer with the action.MAKE_DEFER(name) {action};- macro for creating named defer with the action.WITH_DEFER({action}) {/*...*/}- macro for creating a scope with defer with the action.
Guards created by factories and macros provide dismiss(), which disables the action.
Guards are move-only. Moving transfers responsibility for executing the action.
-
SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE- requires a nothrow move-constructible action. It can be combined with any action policy. -
SCOPE_GUARD_MAY_THROW_ACTION- allows action exceptions to propagate. -
SCOPE_GUARD_NO_THROW_ACTION- requires anoexceptaction. -
SCOPE_GUARD_SUPPRESS_THROW_ACTION- suppresses exceptions thrown by the action. -
By default,
SCOPE_GUARD_MAY_THROW_ACTIONis used. Action exceptions propagate normally. If an action throws during exception unwinding, the program terminates. UseSCOPE_GUARD_NO_THROW_ACTIONorSCOPE_GUARD_SUPPRESS_THROW_ACTIONfor cleanup paths that must not throw. -
SCOPE_GUARD_CATCH_HANDLER- a non-throwing statement run when an action exception is caught. It is ignored unlessSCOPE_GUARD_SUPPRESS_THROW_ACTIONis defined.#define SCOPE_GUARD_SUPPRESS_THROW_ACTION #define SCOPE_GUARD_CATCH_HANDLER /* log cleanup failure */ ; #include <scope_guard.hpp>
Define exception settings consistently in every translation unit before including scope_guard.hpp. SCOPE_GUARD_CATCH_HANDLER must not throw.
-
Factories accept only rvalue callables and store them by value. Pass a temporary or use
std::move:auto action = [&]() { /* cleanup */ }; auto guard = scope_guard::make_scope_exit(std::move(action)); // OK // auto guard = scope_guard::make_scope_exit(action); // compile error
-
Actions take no arguments and must return
void. -
Macro-generated actions use
[&]lambda capture. Use a factory function with an explicit lambda capture when different ownership is required. -
Guards execute in reverse construction order.
SCOPE_GUARD_OPT_BUILD_EXAMPLES, SCOPE_GUARD_OPT_BUILD_TESTS, and SCOPE_GUARD_OPT_INSTALL default to ON when scope_guard is the top-level project and OFF when it is a subproject.
For manual integration, copy scope_guard.hpp into your project.
For CMake integration, add this project as a subdirectory and link the interface target:
add_subdirectory(scope_guard)
target_link_libraries(your_target PRIVATE scope_guard::scope_guard)If scope_guard is installed as a CMake package:
find_package(scope_guard CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE scope_guard::scope_guard)- Andrei Alexandrescu "Systematic Error Handling in C++"
- Andrei Alexandrescu "Declarative Control Flow"