SCOPE#
-
SCOPE(...)#
Automatically runs code when a scope is exited (
SCOPE(exit)), exited by means of an exception (SCOPE(fail)), or exited normally (SCOPE(success)).A guard body that throws is treated the way the language treats exceptions: one at a time is an error to report, two at once is fatal.
SCOPE(exit)propagates a failure raised while the scope is being left NORMALLY, so a cleanup that fails on the happy path reaches the caller as an ordinary exception; a failure raised while the scope is ALREADY unwinding is reported (message and location) and aborts, since letting it escape would givestd::terminatewith no diagnostic.SCOPE(fail)runs only while unwinding and therefore always reports and aborts;SCOPE(success)runs only on the normal path and may throw. In all cases the controlled code must returnvoid(enforced at compile time).One consequence worth knowing: inside a
noexceptfunction, aSCOPE(exit)body that throws on the normal path ends the program throughstd::terminatewithout STF’s report. UseSCOPE(fail)plusSCOPE(success)there if the distinction matters.SCOPE(exit)runs its code at the natural termination of the current scope. Example:// SCOPE(exit) runs the lambda upon the termination of the current scope. bool done = false; { SCOPE(exit) { done = true; }; EXPECT(!done, "SCOPE_EXIT should not run early."); } EXPECT(done);
SCOPE(exit, name)additionally names aboolparameter that istruewhen the scope is being left by an exception thrown within it, letting one guard serve both outcomes:The flag comparesSCOPE(exit, failing) { failing ? discard(handle) : commit(handle); };std::uncaught_exceptions()against its value at construction, so a guard declared inside somebody else’s handler correctly reportsfalse.SCOPE(fail)andSCOPE(success)reject the parameter: their outcome is already known by construction. The exception itself is not available to a guard (a destructor is not a handler, andstd::current_exception()is null during unwinding); to react to the exception, wrap the region inON_THROW(policy).SCOPE(fail)runs its code if and only if the current scope is left by means of throwing an exception. Example:bool done = false; { SCOPE(fail) { done = true; }; EXPECT(!done, "SCOPE_FAIL should not run early."); } EXPECT(!done); try { SCOPE(fail) { done = true; }; EXPECT(!done); throw 42; } catch (...) { EXPECT(done); }
Finally,
SCOPE(success)runs its code if and only if the current scope is left by normal flow (as opposed to by an exception). Example:bool done = false; { SCOPE(success) { done = true; }; EXPECT(!done); } EXPECT(done); done = false; try { SCOPE(success) { done = true; }; EXPECT(!done); throw 42; } catch (...) { EXPECT(!done); }
If two or more
SCOPEdeclarations are present in the same scope, they will take effect in the reverse order of their lexical order. Example:int counter = 0; { SCOPE(exit) { EXPECT(counter == 2); counter = 0; }; SCOPE(success) { EXPECT(counter == 1); ++counter; }; SCOPE(exit) { EXPECT(counter == 0); ++counter; }; EXPECT(counter == 0); } EXPECT(counter == 0);
See Also: https://en.cppreference.com/w/cpp/experimental/scope_exit, https://en.cppreference.com/w/cpp/experimental/scope_fail, https://en.cppreference.com/w/cpp/experimental/scope_success