// bdlb_scopeexit.h -*-C++-*- #ifndef INCLUDED_BDLB_SCOPEEXIT #define INCLUDED_BDLB_SCOPEEXIT #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a general-purpose proctor object for scope-exit logic. // //@CLASSES: // bdlb::ScopeExit: executes a function upon destruction // bdlb::ScopeExitAny: an alias to 'ScopeExit<bsl::function<void()>>' // bdlb::ScopeExitUtil: C++11 or later factory method for creating guards // //@MACROS: // BDLB_SCOPEEXIT_GUARD: creates a scope guard using an exit function // BDLB_SCOPEEXIT_PROCTOR: creates a scope proctor from name and exit function // //@SEE_ALSO: // P0052R6 - Generic Scope Guard and RAII Wrapper for the Standard Library // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0052r6.pdf // //@DESCRIPTION: // This component provides a class template mechanism 'bdlb::ScopeExit', that // will invoke a client supplied function upon its destruction. // 'bdlb::ScopeExit' is intended to facilitate creating scoped-proctors // (similar to those found in {'bslma'}) that run a user-specified exit // function upon their destruction, unless 'release()'-d. The primary purpose // of such a proctor is to execute a guaranteed undo of some operation in case // the complete chain of operations was not successful (exception thrown or // early return). The proctor may also be used as a guard to unconditionally // run its exit function upon exiting a scope, however for such guards // dedicated classes are highly recommended (see also {Guard vs Proctor}. In // case of a sufficiently functional C++11 or later platform this component // also defines a utility 'bdlb::ScopeExitUtil' containing a factory method for // creating 'bdlb::ScopeExit' objects ('makeScopeExit'). This component also // defines a type alias 'bdlb::ScopeExitAny' that is used in C++03 code to // create a proctor where the type of the exit function is not known (for // example it is the result of a bind expression). Finally, this component // defines two macros that allow creating the 'bdlb::ScopeExit' objects, // without mentioning the exit function type, in a uniform way for platforms // limited to C++03 and those supporting sufficiently functional C++11 or // later, but with a more efficient implementation for the latter. The first // macro, 'BDLB_SCOPEEXIT_PROCTOR' creates a scope proctor with a given // variable name and exit function. The second macro, 'BDLB_SCOPEEXIT_GUARD' // creates a scope guard variable of unspecified name given only an exit // function argument. See also {Guard vs Proctor}, and {C++03 Restrictions // When Exit Function Type is Unknown}. // ///Guard vs Proctor ///---------------- // Guard and Proctor are terminology used by BDE methodology (see below). // Because 'bdlb::ScopeExit' is so general (its exit function is provided by // the user) it can be used both as a Guard and as a Proctor. Below are the // BDE definitions followed by an explanation of how they apply to this // component. // ///Definitions ///- - - - - - // A (scoped) Guard is an object that maintains control over an associated // resource (often acquired on construction) and releases that control when the // Guard is destroyed (typically at scope exit -- either normally or because an // exception was thrown). The guard may provide an explicit release method, // but -- unlike a Proctor -- such a release method is not commonly used. // // A Proctor is a special kind of guard intended to restore a valid state under // abnormal circumstances (e.g., a thrown exception), until a valid state is // restored normally, after which the Proctor's responsibility is explicitly // released by its client. The Proctor must provide a mechanism to release the // resource from management, and -- unlike a standard Guard -- its management // responsibility is typically released prior to its destruction. // ///Further Explanation ///- - - - - - - - - - // A Guard controls the lifetime a resource, it owns it during the resource's // complete lifetime, and its purpose is to free up that resource once the work // that needed it is done. Guards may provide a 'release' method for "off // label use" in case the responsibility for freeing up the resource is // transferred to some other code, but it is not its common use. // // A Proctor is commonly responsible for undoing changes when a locally // non-recoverable error occurs. Proctors do not necessarily clean up // resources (although they might, as part of restoring a valid state). When // all goes well, Proctors do nothing, because when all the work is // successfully completed the user code "deactivates" the Proctor (or Proctors) // to "commit" the work by calling the 'release' method. // ///C++03 Restrictions When Exit Function Type Is Unknown ///----------------------------------------------------- // There are two restrictions when using this component with C++03 and both // are related to missing core language features. // // The first restriction is related to 'bdlb::ScopeExit' being a move-only // type, and although this class tries its best to emulate move semantics, // returning an instance of 'bdlb::ScopeExit' from a factory function is just // not feasible under C++03. It is technically possible, but would require a // lot of boilerplate code on the user side, making the use of the component // way too cumbersome to use under C++03. // // The second restriction is caused by the absence of the type-deducing 'auto' // keyword in C++03. Although we can deduce the type of the exit function in // the factory method, without the type deduction capabilities provided by // 'auto' we cannot turn that return type into the type of a variable. // // For the above reasons we do not generate or use a factory method in C++03 // but rely on 'bsl::function<void()>' (as described below) to define a type // that is able to store and call any exit function. // // In case the type of the exit function is known to the programmer (see // {Example 2: Using a Scope Exit Guard In C++03}) it is possible to simply // provide that type as the template argument for 'bdlb::ScopeExit'. But if // our exit function has an unknown type (such as is the result of a 'bind' // expression) we are facing the same above two restrictions (see also // {Example 3: Unknown Exit Function Type In C++03}). // // Given the C+03 restrictions, the following design decisions have been made: //: 1. Do not provide the utility and factory function when compiling with //: C++03. //: //: 2. Provide a type 'bdlb::ScopeExitAny' that is an alias to //: 'bdlb::ScopeExit<bsl::function<void()> >'. An instance of //: 'bdlb::ScopeExitAny' can be created without having to name the type of //: the exit function it is constructed from because 'bsl::function' //: provides type erasure. The downside is the runtime performance cost of //: type erasure (virtual function call that might inhibit inlining) and //: potential memory allocation for stored arguments - all of which are the //: cost of using 'bsl::function'. //: //: 3. Provide 'BDLB_SCOPEEXIT_PROCTOR' and 'BDLB_SCOPEEXIT_GUARD' macros that //: hide the difference between using a legacy C++03 'bdlb::ScopeExitAny', //: or C++ 'auto' type deduction from the factory function call. The macros //: thereby will select the most efficient 'bdlb::ScopeExit' type that is //: feasible on the given compiler platform without having to specify the //: exact type of the exit function (the template parameter to //: 'bdlb::ScopeExit'). // ///Memory Allocation and Relationship with BDE Allocators ///------------------------------------------------------ // The exit function is created as a member of the guard object, so no memory // is allocated for storing it. However, if the exit function copy or move // constructors require memory allocation, that memory is supplied by the // currently installed default allocator. Currently this component does not // support custom allocators. The main use-case of a guard object is to be // created on the stack and automatic variables are supposed to use the // default allocator, therefore no allocator support was added. // ///Notes ///----- // This component has been inspired by P0052R6 a since not-adopted ISO C++ // Library proposal, but has been extended to support C++03, BDE methodology, // and attributes. // ///Usage Examples ///-------------- // This section illustrates intended use of this component. // ///Example 1: Using a Scope Exit Proctor in C++11 or later ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - // In this example we assume a C++ compiler supporting sufficiently functional // C++11 or later. Suppose we are creating a simple database that stores names // and their associated addresses and we store the names and addresses in two // separate tables. While adding data, these tables may fail the insertion, // in which case we need to roll back the already inserted data, such as if we // inserted the address first, we need to remove it if insertion of the // associated name fails. // // First, we emulate our database access with the following simple functions: //.. // int removedAddressId = 0; // int insertAddress(const char *address) // { // (void)address; // return (0 == removedAddressId ? 2 : 3); // } // // int insertCustomer(const char *name, int addressId) // { // (void)name; // (void)addressId; // if (0 == removedAddressId) throw 5; // Simulate failure once // return 7; // } // // void removeAddress(int id) // { // removedAddressId = id; // } //.. // Next, we draw up our complex, customer-creating function signature: //.. // int addCustomer11(const char *name, const char *address) // { //.. // Then we implement it, starting by inserting the address: //.. // const int addressId = insertAddress(address); //.. // Our dummy function returns 42, indicating successful address insertion. // // Next, we create a proctor to remove the already inserted address if the name // insertion fails: //.. // auto addressProctor = bdlb::ScopeExitUtil::makeScopeExit( // [=](){ removeAddress(addressId); }); //.. // Then, we attempt to insert the name: //.. // const int custId = insertCustomer(name, addressId); //.. // As our dummy 'insertCustomer' function will fail first time by throwing an // exception (when'removedAddressId' is zero) we exit this function to the // caller's error handling 'catch' clause. While exiting the function due to // the exception, the local stack is unwound. The non-trivial destructors of // local variables are invoked (in the opposite order of their creation). In // this case, the destructor of 'addressProctor' invokes its exit function, // saving our non-zero 'addressId' value into the global 'removedAddressId' // variable. // // On the second call to this function, because 'removedAddressId' is now // non-zero, 'insertCustomer' will not fail, and we continue execution here. // // Next, if the insertion succeeded we are done, so we need to release the // proctor to make the address permanent, after which we can return the ID: //.. // addressProctor.release(); // // return custId; // RETURN // } //.. // Now we can verify that a first attempt to add a customer fails with the // "right" exception and that 'removedAddressId' is the expected value: //.. // bool seenException = false; // try { // addCustomer11("Quibi", "6555 Barton Ave, Los Angeles, CA, 90038"); // } // catch (int exceptionValue) { // assert(5 == exceptionValue); // seenException = true; // } // assert(seenException); // assert(2 == removedAddressId); //.. // Finally we verify that calling 'addCustomer11' again succeeds with the right // identifier returned, and that 'removedAddressId' does not change: //.. // assert(7 == addCustomer11("Plum Inc.", "1i Imagine Sq, Coppertin, CA")); // assert(2 == removedAddressId); //.. // ///Example 2: Using a Scope Exit Guard in C++03 /// - - - - - - - - - - - - - - - - - - - - - - // Suppose we are in the same situation as in the C++11 or later example, but // we have to create a solution that supports C++03 as well. // // First, we have to hand-craft a functor that calls 'removeAddress' with a // given ID because C++03 does not support lambdas: //.. // class RemoveAddress { // int d_id; // the identifier of the address (row) to remove // // public: // explicit RemoveAddress(int id) // : d_id(id) // { // } // // void operator()() const // { // removeAddress(d_id); // } // }; //.. // Then, we implement the add customer function for C++03: //.. // int addCustomer03(const char *name, const char *address) // { // const int addressId = insertAddress(address); //.. // The code is almost the same code as was in 'addCustomer11' (the // implementation that requires sufficiently functional C++11 or later // platform), except for the proctor variable definition. // // Next, we define the proctor variable with an explicitly spelled out type // (that uses the functor type template argument), and a functor object // initialized with the identifier of the address to remove: //.. // bdlb::ScopeExit<RemoveAddress> addrProctor((RemoveAddress(addressId))); //.. // Notice the extra parentheses we had to use to avoid "the most vexing parse" // (https://en.wikipedia.org/wiki/Most_vexing_parse) issue. Since we are in // C++03, we cannot use (curly) brace initialization to avoid that issue. // // Now, we can complete the rest of the 'addCustomer03', which is exactly the // same as the corresponding part of the 'addCustomer11' variant: //.. // const int custId = insertCustomer(name, addressId); // addrProctor.release(); // // return custId; // RETURN // } //.. // Finally, we can verify that both during the failing first attempt to add a // customer to our imaginary database and the successful second attempt the // 'RemoveAddress' functor based proctor works just as well as the lambda based // 'addCustomer11' variant did: //.. // removedAddressId = 0; // seenException = false; // try { // addCustomer03("Quibi", "6555 Barton Ave, Los Angeles, CA, 90038"); // } // catch (int exceptionValue) { // assert(5 == exceptionValue); // seenException = true; // } // assert(seenException); // assert(2 == removedAddressId); // // assert(7 == addCustomer03("Plum Inc.", "1i Imagine Sq, Coppertin, CA")); // assert(2 == removedAddressId); //.. // /// Example 3: Unknown Exit Function Type In C++03 ///- - - - - - - - - - - - - - - - - - - - - - - - // Suppose that we decide not to write a functor class for removing an address // but use the function itself directly with 'bdlf::BindUtil::bind' and that // way keep the roll-back-code near the point of use like lambdas allow us in // C++11 and later, albeit with a less elegant syntax. // // First, we design our bind expression as // 'bdlf::BindUtil::bind(&removeAddress, addressId)'. // // Then, we can even try it to see if it works as intended by calling the // result of a bind expression using a constant for the address ID: //.. // removedAddressId = 0; // bdlf::BindUtil::bind(&removeAddress, 11)(); // assert(11 == removedAddressId); //.. // Notice the subtle '()' after the bind expression. We immediately call it // after creating it (then destroy it). We have to do it this way. We have no // idea what its type is so we cannot make a variable for it. // // Next, we create yet another customer adding function that differs only in // its proctor definition from the 'addCustomer11' variant: //.. // int addCustomerAny(const char *name, const char *address) // { // const int addressId = insertAddress(address); //.. // Because we do not know the type of our exit function (it is "some functor // object of some type", created by 'bind') we have to use the 'bsl::function' // based 'bdlb::ScopeExitAny': //.. // bdlb::ScopeExitAny addressProctor(bdlf::BindUtil::bind(&removeAddress, // addressId)); //.. // Consult {C++03 Restrictions When Exit Function Type Is Unknown} to be aware // what additional runtime costs this more compact code has compared to a // "hand made" functor with a known type. // // Note that since we have to take the address of a function to create the // 'bind'-expression-functor we cannot use this format with standard library // functions (unless taking their address is explicitly allowed by the C++ // standard), and if 'removeAddress' were an overloaded function the code would // not compile as the compiler would not know which address we want. // // The rest of the function is the same and omitted for brevity. // // Finally, we can verify that 'bind' and 'bdlb::ScopeExitAny' based proctor // works just as well: //.. // removedAddressId = 0; // seenException = false; // try { // addCustomerAny("Quibi", "6555 Barton Ave, Los Angeles, CA, 90038"); // } // catch (int exceptionValue) { // assert(5 == exceptionValue); // seenException = true; // } // assert(seenException); // assert(2 == removedAddressId); // // assert(7 == addCustomerAny("Plum Inc.", "1i Imagine Sq, Coppertin, CA")); // assert(2 == removedAddressId); //.. // ///Example 4: Using the Scope Exit Proctor Macro ///- - - - - - - - - - - - - - - - - - - - - - - // Suppose we have to create portable code that will compile with C++03 as well // as C++11 and later compilers. We also want our code to use the more // efficient type-deducing 'auto' with factory-method variant when compiled // with a sufficiently functional C++11 or later compiler, and only fall back // to the slower 'bdlb::ScopeExitAny' solution on C++03 compilers. // // We still need to use either functor ('RemoveAddress' in our examples) or a // bind expression for the exit function because C++03 has no lambdas, // therefore our portable code cannot use lambdas. But we *can* choose the // easy-to-use 'BDLB_SCOPEEXIT_PROCTOR' macro and not sprinkle the add customer // function with '#ifdef' to see which proctor definition to use. // // To keep things simple this component provides a single proctor macro // instead of two macro names to remember (one for the case case when the type // of the exit function is known and one when it isn't). In case the exit // function name is known we can just directly use // 'bdlb::ScopeExit< --ExitFunctionType-- >' on any compiler. // // First, we start the add customer function as usual: //.. // int addCustomerMacro(const char *name, const char *address) // { // const int addressId = insertAddress(address); //.. // Then, we define the proctor using a bind expression and the macro: //.. // BDLB_SCOPEEXIT_PROCTOR(proctor, bdlf::BindUtil::bind(&removeAddress, // addressId)); //.. // Alternatively, we could have also written a functor and write the shorter // 'BDLB_SCOPEEXIT_PROCTOR(proctor, RemoveAddress(addressId))' for the proctor. // // The rest of the function is the same and omitted for brevity. // // Finally, we can verify the easy proctor with the now customary code: //.. // removedAddressId = 0; // seenException = false; // try { // addCustomerMacro("Quibi", "6555 Barton Ave, Los Angeles, CA, 90038"); // } // catch (int exceptionValue) { // assert(5 == exceptionValue); // seenException = true; // } // assert(seenException); // assert(2 == removedAddressId); // // assert(7 == addCustomerMacro("Plum Inc.", "1i Imagine Sq, Coppertin, CA")); // assert(2 == removedAddressId); //.. // ///Example 5: Using the Scope Exit Guard Macro ///- - - - - - - - - - - - - - - - - - - - - - // Suppose that we are writing a printing system that is capable of printing // out dynamic data structures that can contain numbers, strings, arrays, maps, // etc. When printing out data we often have to print delimiters and find // that it is really easy to forget to print the closing the delimiter. So we // look for a simple way to automate them. We decide we don't want to change // the printing of the opening delimiters, just have a way to automate the // printing of the close delimiters without worrying about early returns or // 'break', 'continue', or other control flow changes. // // First, we create a functor type that prints closing delimiters: //.. // class CloseDelimPrinter { // const char *d_closingChars_p; // held, not owned // // public: // explicit CloseDelimPrinter(const char *s) // : d_closingChars_p(s) // { // } // // void operator()() const // { // outStream << d_closingChars_p; // To a fixed stream for brevity // } // }; //.. // Then, we can use the above functor and a scope exit guard to automate // closing of delimiters in the printing functions: //.. // void printTemplateWithArgs( // const bsl::string_view>& templateName, // const bsl::vector<bsl::string_view>& args) // { //.. // Next, we can move the printing of the opening delimiter and the closing one // near each other in code, so it is clearly visible if an opened delimiter is // closed: //.. // outStream << templateName << '<'; // BDLB_SCOPEEXIT_GUARD(CloseDelimPrinter(">")); //.. // The macro works in C++03 and C++11 and later, gives the guard variable a // unique (but unspecified) name, adds an extra set of parentheses to take care // of "the most vexing parse" and suppresses unused variable compiler warnings. // The name for the guard variable created is unspecified. Because this is a // guard meaning we do not need to call 'release()' (unlike a proctor), // therefore the name is unimportant. // // Now, we can just print what goes inside the delimiters, and we are done: //.. // if (args.empty()) { // // Safe to just return, the guard takes care of closing the '<' // return; // RETURN // } // // typedef bsl::vector<bsl::string_view>::const_iterator Cit; // // Cit cit = args.begin(); // outStream << *cit; // Print first argument // ++cit; // // for (;cit != args.end(); ++cit) { // outStream << ", " << *cit; // Print subsequent argument // } // // const bsl::string_view last = *(args.end() - 1); // if (last.back() == '>') { // outStream << ' '; // } // } //.. // Finally, we can print some templates and verify that the argument // delimiters are closed: //.. // bsl::vector<bsl::string_view> targs; // printTemplateWithArgs("TypeList", targs); // assert(outStreamContent() == "TypeList<>"); // // targs.push_back("bsl::string_view"); // printTemplateWithArgs("bsl::vector", targs); // assert(outStreamContent() == "bsl::vector<bsl::string_view>"); // // targs.push_back("bsl::vector<bsl::string_view>"); // printTemplateWithArgs("bsl::unordered_map", targs); // assert(outStreamContent() == // "bsl::unordered_map<bsl::string_view, bsl::vector<bsl::string_view> >"); //.. #include <bdlscm_version.h> #include <bsla_maybeunused.h> #include <bsla_nodiscard.h> #include <bslmf_assert.h> #include <bslmf_conditional.h> #include <bslmf_decay.h> #include <bslmf_enableif.h> #include <bslmf_isconvertible.h> #include <bslmf_isfunction.h> #include <bslmf_isnothrowmoveconstructible.h> #include <bslmf_ispointer.h> #include <bslmf_issame.h> #include <bslmf_movableref.h> #include <bslmf_removepointer.h> #include <bslmf_util.h> #include <bsl_functional.h> #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER #include <bsl_type_traits.h> #endif #ifdef BSLS_COMPILERFEATURES_SUPPORT_CTAD #include <bsl_utility.h> // 'bsl::move' #endif #include <bsls_buildtarget.h> #include <bsls_compilerfeatures.h> #include <bsls_keyword.h> #include <bsls_platform.h> #include <bsls_util.h> // ============================================================================ // MACROS // ============================================================================ #ifdef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES BSLMF_ASSERT(BSLS_COMPILERFEATURES_CPLUSPLUS >= 201103L); // 'BSLS_COMPILERFEATURES_CPLUSPLUS >= 201103L' is implied here. #define BDLB_SCOPEEXIT_USES_MODERN_CPP // This macro determines if we have a sufficiently functional C++11 or // later compiler so the type-deducing 'auto' with factory-method can // be compiled and works as expected. When the macro is not defined // (C++03 form) no utility 'struct' (therefore no factory method) will // be defined by this component and the variable definition macros (for // Proctor and Guard) will use 'bdlb::ScopeExitAny'. When this macro // is defined (C++11 or later that has sufficiently operation rvalue // references and type-deducing 'auto' keyword implementation) the // factory method and its utility 'struct' will be defined and the // variable definition macros will use 'auto' to determine the type of // the 'bdlb::ScopeExit' variable. This macro is only internally in // this component header and therefore it is undefined at the end of // the file. #endif #ifdef BDE_BUILD_TARGET_EXC #define BDLB_SCOPEEXIT_NOEXCEPT_SPEC BSLS_KEYWORD_NOEXCEPT_SPECIFICATION #else #define BDLB_SCOPEEXIT_NOEXCEPT_SPEC(...) #endif // This macro is for internal use only and is undefined at the end of the // file. #ifdef BDLB_SCOPEEXIT_USES_MODERN_CPP #define BDLB_SCOPEEXIT_PROCTOR(NAME, EXIT_FUNC) \ auto NAME{ BloombergLP::bdlb::ScopeExitUtil::makeScopeExit(EXIT_FUNC) } #else #define BDLB_SCOPEEXIT_PROCTOR(NAME, EXIT_FUNC) \ BloombergLP::bdlb::ScopeExitAny NAME((EXIT_FUNC)) #endif // Create a local variable with the specified 'NAME' with a type that is an // instantiation of 'bdlb::ScopeExit' initialized with the specified // 'EXIT_FUNC'. Note that the specific type of 'bdlb::ScopeExit' used will // depend on available language features. #define BDLB_SCOPEEXIT_PRIVATE_CAT(X, Y) BDLB_SCOPEEXIT_PRIVATE_CAT_IMP(X, Y) // This macro is for use by 'BDLB_SCOPEEXIT_GUARD' only to provide unique // variable names. It is *not* undefined by the end of the file but its // direct use is not supported under any circumstances. #define BDLB_SCOPEEXIT_PRIVATE_CAT_IMP(X, Y) X##Y // This macro is for use by 'BDLB_SCOPEEXIT_GUARD' only to provide unique // variable names. It is *not* undefined by the end of the file but its // direct use is not supported under any circumstances. #if defined(BSLS_PLATFORM_CMP_MSVC) || defined(BSLS_PLATFCORM_CMP_GNU) || \ defined(BSLS_PLATFCORM_CMP_CLANG) // MSVC: The '__LINE__' macro breaks when '/ZI' is used (see Q199057 or // KB199057). Fortunately the '__COUNTER__' extension provided by MSVC // is even better. Since '__COUNTER__' is also available on all our // supported newer C++ platforms (GNU g++ and clang) we use it there as // well. #define BDLB_SCOPEEXIT_PRIVATE_UNIQNUM __COUNTER__ #else #define BDLB_SCOPEEXIT_PRIVATE_UNIQNUM __LINE__ #endif // This macro is for use by 'BDLB_SCOPEEXIT_GUARD' only to provide unique // variable names. It is *not* undefined by the end of the file but its // direct use is not supported under any circumstances. #define BDLB_SCOPEEXIT_GUARD(EXIT_FUNC) \ BSLA_MAYBE_UNUSED \ const BDLB_SCOPEEXIT_PROCTOR( \ BDLB_SCOPEEXIT_PRIVATE_CAT(bdlbScopeExitGuard_, \ BDLB_SCOPEEXIT_PRIVATE_UNIQNUM), \ EXIT_FUNC) // Create a local variable with a generated unique name and with a type // that is an instantiation of 'bdlb::ScopeExit' initialized with the // specified 'EXIT_FUNC'. Note that the specific type of 'bdlb::ScopeExit' // used will depend on available language features. The behavior is // undefined if this macro is used (expanded) more than once on the same // source line. Note that using this macro in another macro more than once // is equivalent to using it more than once on the same source line. // ============================================================================ // CLASS TYPES // ============================================================================ namespace BloombergLP { namespace bdlb { // =============== // class ScopeExit // =============== template <class EXIT_FUNC> class BSLA_NODISCARD_CPP17 ScopeExit { // 'ScopeExit' is a general-purpose scope proctor class template that is // intended to be used as an automatic (stack) variable that calls an exit // function upon its destruction (when its scope is exited). // // The template argument 'EXIT_FUNC' shall be a function object type, or a // pointer to a function. If 'EXIT_FUNC' is an object type, it shall // satisfy the requirements of *Destructible*, *Callable*, and // *MoveConstructible* as specified by the ISO C++ standard. Note that to // fulfill the *MoveConstructible* constraint a type does not have to // implement a move constructor. If it has a copy constructor, that will // work fine as long at the move constructor is not deleted (or in case of // C++03 emulated moves, 'private'). The behavior is undefined if calling // (the member instance of) 'EXIT_FUNC' throws an exception (as it will be // called from the destructor). private: // TEMPLATE PARAMETER CONTRACT BSLMF_ASSERT( !bsl::is_pointer<EXIT_FUNC>::value || bsl::is_function< typename bsl::remove_pointer<EXIT_FUNC>::type>::value); // Only function pointers are allowed, no other pointers. BSLMF_ASSERT(!bsl::is_reference<EXIT_FUNC>::value); // Reference types are not allowed, only objects. // We could check for more, but 'is_destructible' needs full C++11 // '<type_traits>' support, and 'is_invocable' is C++17 or later. So we // skip checking these to avoid major conditional compilation clutter. The // chance of passing non-destructible or non-callable 'EXIT_FUNC' argument // is low, and they will result in a reasonable error message. private: // PRIVATE TYPES typedef bslmf::MovableRefUtil MoveUtil; // Shorter lines for more readable code. private: // PRIVATE DATA EXIT_FUNC d_exitFunction; // A function pointer or functor to call bool d_executeOnDestruction; // 'false' if 'release' was called private: // NOT IMPLEMENTED ScopeExit(const ScopeExit&) BSLS_KEYWORD_DELETED; // No copying ScopeExit& operator=(const ScopeExit&) BSLS_KEYWORD_DELETED; // No #ifdef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES // assignment ScopeExit& operator=(ScopeExit&&) BSLS_KEYWORD_DELETED; // at all. #endif public: // CREATORS template <class EXIT_FUNC_PARAM> explicit ScopeExit( BSLS_COMPILERFEATURES_FORWARD_REF(EXIT_FUNC_PARAM) function, typename bsl::enable_if< #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER // Enable explicit conversions on platforms where we can use // 'bsl::is_constructible'. Since this converting constructor itself is // marked 'explicit' we are not enabling anything extra by allowing // 'explicit' conversions when 'EXIT_FUNC' is already explicitly // convertible. bsl::is_constructible< // Copy construct EXIT_FUNC, const typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type& >::value || bsl::is_constructible< // Move construct EXIT_FUNC, bslmf::MovableRef<typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type> >::value #else // 'bsl::is_convertible' will not allow 'explicit' conversions as we cannot // check their existence in C++03 without running into a compilation error. bsl::is_convertible< // "Copy convert" const typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type&, EXIT_FUNC >::value || bsl::is_convertible< // "Move convert" bslmf::MovableRef<typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type>, EXIT_FUNC >::value #endif >::type * = 0); // Create a 'ScopeExit' object, which, upon its destruction will invoke // the specified 'function' (or functor) unless its 'release' method // was called. If 'function' is copied into the 'EXIT_FUNC' member, // and that copy throws an exception, invoke 'function' and rethrow the // exception. If 'EXIT_FUNC_PARAM' cannot be move converted to // 'EXIT_FUNC' via no-throw means (either because such conversion does // not exist or it is not marked as non-throwing), 'function' will // always be copied into the member. This constructor participates in // overload resolution only if 'EXIT_FUNC_PARAM' is neither 'EXIT_FUNC' // nor 'bdlb::ScopeExit<EXIT_FUNC>' and 'EXIT_FUNC_PARAM' is // convertible to 'EXIT_FUNC'. The behavior is undefined if 'function' // or the member instance of 'EXIT_FUNC' throws an exception upon // invocation. #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER explicit ScopeExit(void (*function)()); // Create a 'ScopeExit' object, which, upon its destruction will invoke // the specified 'function' unless the 'release' method was called. // If 'function' is copied into the 'EXIT_FUNC' member, and that copy // throws an exception, invoke 'function' and rethrow the exception. // The behavior is undefined if 'function' or the member instance of // 'EXIT_FUNC' throws an exception upon invocation. Note that this // separate constructor for function pointers exists because without it // the forwarding logic in the templated constructor implementation // generates warnings about meaningless types generated. #endif ScopeExit(bslmf::MovableRef<ScopeExit> original) #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER BDLB_SCOPEEXIT_NOEXCEPT_SPEC( bsl::is_nothrow_move_constructible<EXIT_FUNC>::value || bsl::is_nothrow_copy_constructible<EXIT_FUNC>::value); #else BDLB_SCOPEEXIT_NOEXCEPT_SPEC( bsl::is_nothrow_move_constructible<EXIT_FUNC>::value); #endif // If 'bsl::is_nothrow_move_constructible<EXIT_FUNC>::value' is 'true' // or 'EXIT_FUNC' is a move-only type, move construct, otherwise, copy // construct the exit function from the specified 'original'. If // construction succeeds, call 'release()' on 'original'. ~ScopeExit(); // Destroy this object. Execute the exit function unless 'release()' // has been called on this object. public: // MANIPULATORS void release(); // Turn off the execution of the exit function of this object on // destruction. }; #ifdef BSLS_COMPILERFEATURES_SUPPORT_CTAD // CLASS TEMPLATE DEDUCTION GUIDES template <class EXIT_FUNC_PARAM> explicit ScopeExit(BSLS_COMPILERFEATURES_FORWARD_REF(EXIT_FUNC_PARAM) function) -> ScopeExit<typename bsl::decay<EXIT_FUNC_PARAM>::type>; #endif // ==================== // typedef ScopeExitAny // ==================== typedef ScopeExit<bsl::function<void()> > ScopeExitAny; // 'ScopeExitAny' is an alias to 'ScopeExit<bsl::function<void()> >', // effectively making it a polymorphic scope exit type. #ifdef BDLB_SCOPEEXIT_USES_MODERN_CPP // =================== // class ScopeExitUtil // =================== struct ScopeExitUtil { // A utility that provides a factory function for sufficiently function // C++11 or later platforms to create scope guards using the type-deducing // 'auto' keyword. Notice that the utility type does not exist on C++03 // platforms. // CLASS METHODS template <class EXIT_FUNC_PARAM> BSLA_NODISCARD static ScopeExit<typename bsl::decay<EXIT_FUNC_PARAM>::type> makeScopeExit(EXIT_FUNC_PARAM&& function); // Return a 'ScopeExit' guard that uses the specified 'function' as its // exit function, and has the decayed type of 'function' (class type or // or function pointer type) as its exit function member type. }; #endif // BDLB_SCOPEEXIT_USES_MODERN_CPP // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // --------------- // class ScopeExit // --------------- // CREATORS template <class EXIT_FUNC> template <class EXIT_FUNC_PARAM> inline ScopeExit<EXIT_FUNC>::ScopeExit( BSLS_COMPILERFEATURES_FORWARD_REF(EXIT_FUNC_PARAM) function, typename bsl::enable_if< #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER bsl::is_constructible< // Copy construct EXIT_FUNC, const typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type& >::value || bsl::is_constructible< // Move construct EXIT_FUNC, bslmf::MovableRef<typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type> >::value #else bsl::is_convertible< // "Copy convert" const typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type&, EXIT_FUNC >::value || bsl::is_convertible< // "Move convert" bslmf::MovableRef<typename MoveUtil::Decay<EXIT_FUNC_PARAM>::type>, EXIT_FUNC >::value #endif >::type *) #ifdef BDE_BUILD_TARGET_EXC try #endif #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER : d_exitFunction( bslmf::Util::forward< typename bsl::conditional< bsl::is_nothrow_constructible<EXIT_FUNC, EXIT_FUNC_PARAM>::value || !bsl::is_constructible< EXIT_FUNC, const typename MoveUtil::RemoveReference< EXIT_FUNC_PARAM>::type& >::value, EXIT_FUNC_PARAM, const typename MoveUtil::RemoveReference< EXIT_FUNC_PARAM>::type& >::type >(function)) #else // BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER : d_exitFunction(BSLS_COMPILERFEATURES_FORWARD(EXIT_FUNC_PARAM, function)) // When we are unable to determine if it's safe to move the 'function' // parameter or not (based on its type, using type traits), we forward the // parameter in the usual manner, with the BDE-provided macro that picks // the best forwarding possible on the given compiler. This prevents // compilation errors in case the functor is a move-only object. #endif // BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER , d_executeOnDestruction(true) { } #ifdef BDE_BUILD_TARGET_EXC catch (...) { MoveUtil::access(function)(); } #endif #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER template <class EXIT_FUNC> inline ScopeExit<EXIT_FUNC>::ScopeExit(void (*function)()) #ifdef BDE_BUILD_TARGET_EXC try #endif : d_exitFunction(function) , d_executeOnDestruction(true) { } #ifdef BDE_BUILD_TARGET_EXC catch (...) { function(); } #endif #endif // BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER template <class EXIT_FUNC> inline ScopeExit<EXIT_FUNC>::ScopeExit(bslmf::MovableRef<ScopeExit> original) #ifdef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER BDLB_SCOPEEXIT_NOEXCEPT_SPEC( bsl::is_nothrow_move_constructible<EXIT_FUNC>::value || bsl::is_nothrow_copy_constructible<EXIT_FUNC>::value) #else BDLB_SCOPEEXIT_NOEXCEPT_SPEC( bsl::is_nothrow_move_constructible<EXIT_FUNC>::value) #endif : d_exitFunction(MoveUtil::move_if_noexcept( MoveUtil::access(original).d_exitFunction)) , d_executeOnDestruction(true) { MoveUtil::access(original).release(); } template <class EXIT_FUNC> inline ScopeExit<EXIT_FUNC>::~ScopeExit() { if (d_executeOnDestruction) { d_exitFunction(); } } // MANIPULATORS template <class EXIT_FUNC> inline void ScopeExit<EXIT_FUNC>::release() { d_executeOnDestruction = false; } #ifdef BDLB_SCOPEEXIT_USES_MODERN_CPP // ------------------- // class ScopeExitUtil // ------------------- // CLASS METHODS template <class EXIT_FUNC_PARAM> inline ScopeExit<typename bsl::decay<EXIT_FUNC_PARAM>::type> ScopeExitUtil::makeScopeExit(EXIT_FUNC_PARAM&& function) { return ScopeExit<typename bsl::decay<EXIT_FUNC_PARAM>::type>( bslmf::Util::forward<EXIT_FUNC_PARAM>(function)); } #endif // BDLB_SCOPEEXIT_USES_MODERN_CPP } // close package namespace } // close enterprise namespace // ============================================================================ // TYPE TRAITS // ============================================================================ #ifndef BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER // In case there is no reliable '<type_traits>' header we use 'bsl' a trait to // mark our class no-throw-move-constructible, if the single member is. namespace bsl { template <class EXIT_FUNC> struct is_nothrow_move_constructible<BloombergLP::bdlb::ScopeExit<EXIT_FUNC> > : is_nothrow_move_constructible<EXIT_FUNC> {}; } // close 'bsl' namespace #endif #ifdef BDLB_SCOPEEXIT_USES_MODERN_CPP #undef BDLB_SCOPEEXIT_USES_MODERN_CPP #endif #ifdef BDLB_SCOPEEXIT_NOEXCEPT_SPEC #undef BDLB_SCOPEEXIT_NOEXCEPT_SPEC #endif #endif // ---------------------------------------------------------------------------- // Copyright 2018 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------