/* * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * https://www.boost.org/LICENSE_1_0.txt) * * Copyright (c) 2023 Andrey Semashev */ /*! * \file scope/scope_exit.hpp * * This header contains definition of \c scope_exit template. */ #ifndef BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_ #define BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_ #include #include #include #include #include #include #include #include #include #include #ifdef BOOST_HAS_PRAGMA_ONCE #pragma once #endif namespace boost { namespace scope { template< typename Func, typename Cond > class scope_exit; namespace detail { // Workaround for clang < 5.0 which can't pass scope_exit as a template template parameter from within scope_exit definition template< typename T > using is_not_like_scope_exit = detail::is_not_like< T, scope_exit >; //! The scope guard used to invoke the condition and action functions in case of exception during scope guard construction template< typename Func, typename Cond > class init_guard { private: Func& m_func; Cond& m_cond; bool m_active; public: init_guard(Func& func, Cond& cond, bool active) noexcept : m_func(func), m_cond(cond), m_active(active) { } init_guard(init_guard const&) = delete; init_guard& operator= (init_guard const&) = delete; ~init_guard() noexcept(detail::conjunction< detail::is_nothrow_invocable< Func& >, detail::is_nothrow_invocable< Cond& > >::value) { if (m_active && m_cond()) m_func(); } Func&& get_func() noexcept { return static_cast< Func&& >(m_func); } Cond&& get_cond() noexcept { return static_cast< Cond&& >(m_cond); } void deactivate() noexcept { m_active = false; } }; } // namespace detail /*! * \brief A predicate that always returns \c true. * * This predicate can be used as the default condition function object for * \c scope_exit and similar scope guards. */ class always_true { public: //! Predicate result type using result_type = bool; /*! * **Throws:** Nothing. * * \returns \c true. */ result_type operator()() const noexcept { return true; } }; /*! * \brief Scope exit guard that conditionally invokes a function upon leaving the scope. * * The scope guard wraps two function objects: the scope guard action and * a condition for invoking the action. Both function objects must be * callable with no arguments and can be one of: * * \li A user-defined class with a public `operator()`. * \li An lvalue reference to such class. * \li An lvalue reference or pointer to function taking no arguments. * * The condition function object `operator()` must return a value * contextually convertible to \c true, if the action function object * is allowed to be executed, and \c false otherwise. Additionally, * the condition function object `operator()` must not throw, as * otherwise the action function object may not be called. * * The condition function object is optional, and if not specified in * template parameters, the scope guard will operate as if the condition * always returns \c true. * * The scope guard can be in either active or inactive state. By default, * the constructed scope guard is active. When active, and condition * function object returns \c true, the scope guard invokes the wrapped * action function object on destruction. Otherwise, the scope guard * does not call the wrapped action function object. * * The scope guard can be made inactive by moving-from the scope guard * or calling `set_active(false)`. An inactive scope guard can be made * active by calling `set_active(true)`. If a moved-from scope guard * is active on destruction, the behavior is undefined. * * \tparam Func Scope guard action function object type. * \tparam Cond Scope guard condition function object type. */ template< typename Func, typename Cond = always_true > class scope_exit { //! \cond private: struct func_holder : public detail::compact_storage< Func > { using func_base = detail::compact_storage< Func >; template< typename F, typename C, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type > explicit func_holder(F&& func, C&& cond, bool active, std::true_type) noexcept : func_base(static_cast< F&& >(func)) { } template< typename F, typename C, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type > explicit func_holder(F&& func, C&& cond, bool active, std::false_type) : func_holder(detail::init_guard< F, C >(func, cond, active)) { } private: template< typename F, typename C > explicit func_holder(detail::init_guard< F, C >&& init) : func_base(init.get_func()) { init.deactivate(); } }; struct cond_holder : public detail::compact_storage< Cond > { using cond_base = detail::compact_storage< Cond >; template< typename C, typename = typename std::enable_if< std::is_constructible< Cond, C >::value >::type > explicit cond_holder(C&& cond, Func& func, bool active, std::true_type) noexcept : cond_base(static_cast< C&& >(cond)) { } template< typename C, typename = typename std::enable_if< std::is_constructible< Cond, C >::value >::type > explicit cond_holder(C&& cond, Func& func, bool active, std::false_type) : cond_holder(detail::init_guard< Func&, C >(func, cond, active)) { } private: template< typename C > explicit cond_holder(detail::init_guard< Func&, C >&& init) : cond_base(init.get_cond()) { init.deactivate(); } }; struct data : public func_holder, public cond_holder { bool m_active; template< typename F, typename C, typename = typename std::enable_if< detail::conjunction< std::is_constructible< func_holder, F, C, bool, typename std::is_nothrow_constructible< Func, F >::type >, std::is_constructible< cond_holder, C, Func&, bool, typename std::is_nothrow_constructible< Cond, C >::type > >::value >::type > explicit data(F&& func, C&& cond, bool active) noexcept(detail::conjunction< std::is_nothrow_constructible< Func, F >, std::is_nothrow_constructible< Cond, C > >::value) : func_holder(static_cast< F&& >(func), static_cast< C&& >(cond), active, typename std::is_nothrow_constructible< Func, F >::type()), cond_holder(static_cast< C&& >(cond), func_holder::get(), active, typename std::is_nothrow_constructible< Cond, C >::type()), m_active(active) { } Func& get_func() noexcept { return func_holder::get(); } Func const& get_func() const noexcept { return func_holder::get(); } Cond& get_cond() noexcept { return cond_holder::get(); } Cond const& get_cond() const noexcept { return cond_holder::get(); } bool deactivate() noexcept { bool active = m_active; m_active = false; return active; } }; data m_data; //! \endcond public: /*! * \brief Constructs a scope guard with a given callable action function object. * * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible * and is not a pointer to function. * * \note The requirement for \c Cond default constructor to be non-throwing is to allow for * the condition function object to be called in case if constructing either function * object throws. * * **Effects:** Constructs the scope guard as if by calling * `scope_exit(std::forward< F >(func), Cond(), active)`. * * **Throws:** Nothing, unless construction of the function objects throw. * * \param func The callable action function object to invoke on destruction. * \param active Indicates whether the scope guard should be active upon construction. * * \post `this->active() == active` */ template< typename F //! \cond , typename = typename std::enable_if< detail::conjunction< detail::is_nothrow_nonnull_default_constructible< Cond >, std::is_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, typename detail::move_or_copy_construct_ref< Cond >::type, bool >, detail::is_not_like_scope_exit< F > >::value >::type //! \endcond > explicit scope_exit(F&& func, bool active = true) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, typename detail::move_or_copy_construct_ref< Cond >::type, bool >::value )) : m_data ( static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func), static_cast< typename detail::move_or_copy_construct_ref< Cond >::type >(Cond()), active ) { } /*! * \brief Constructs a scope guard with a given callable action and condition function objects. * * **Requires:** \c Func is constructible from \a func. \c Cond is constructible from \a cond. * * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from * `std::forward< F >(func)`, otherwise constructs from `func`. If \c Cond is * nothrow constructible from `C&&` then constructs \c Cond from * `std::forward< C >(cond)`, otherwise constructs from `cond`. * * If \c Func or \c Cond construction throws and \a active is \c true, invokes * \a cond and, if it returns \c true, \a func before returning with the exception. * * **Throws:** Nothing, unless construction of the function objects throw. * * \param func The callable action function object to invoke on destruction. * \param cond The callable condition function object. * \param active Indicates whether the scope guard should be active upon construction. * * \post `this->active() == active` */ template< typename F, typename C //! \cond , typename = typename std::enable_if< detail::conjunction< detail::is_invocable< C const& >, std::is_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, typename detail::move_or_copy_construct_ref< C, Cond >::type, bool > >::value >::type //! \endcond > explicit scope_exit(F&& func, C&& cond, bool active = true) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, typename detail::move_or_copy_construct_ref< C, Cond >::type, bool >::value )) : m_data ( static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func), static_cast< typename detail::move_or_copy_construct_ref< C, Cond >::type >(cond), active ) { } /*! * \brief Move-constructs a scope guard. * * **Requires:** \c Func and \c Cond are nothrow move-constructible or copy-constructible. * * **Effects:** If \c Func is nothrow move-constructible then move-constructs \c Func from * a member of \a that, otherwise copy-constructs \c Func. If \c Cond is nothrow * move-constructible then move-constructs \c Cond from a member of \a that, * otherwise copy-constructs \c Cond. * * If \c Func or \c Cond construction throws and `that.active() == true`, invokes * \c Cond object stored in \a that and, if it returns \c true, \a Func object * (either the newly constructed one, if its construction succeeded, or the original * one stored in \a that) before returning with the exception. * * If the construction succeeds, marks \a that as inactive. * * **Throws:** Nothing, unless move-construction of the function objects throw. * * \param that Move source. * * \post `that.active() == false` */ //! \cond template< bool Requires = std::is_constructible< data, typename detail::move_or_copy_construct_ref< Func >::type, typename detail::move_or_copy_construct_ref< Cond >::type, bool >::value, typename = typename std::enable_if< Requires >::type > //! \endcond scope_exit(scope_exit&& that) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< Func >::type, typename detail::move_or_copy_construct_ref< Cond >::type, bool >::value )) : m_data ( static_cast< typename detail::move_or_copy_construct_ref< Func >::type >(that.m_data.get_func()), static_cast< typename detail::move_or_copy_construct_ref< Cond >::type >(that.m_data.get_cond()), that.m_data.deactivate() ) { } scope_exit& operator= (scope_exit&&) = delete; scope_exit(scope_exit const&) = delete; scope_exit& operator= (scope_exit const&) = delete; /*! * \brief If `active() == true`, and invoking the condition function object returns \c true, invokes * the wrapped callable action function object. Destroys the function objects. * * **Throws:** Nothing, unless invoking a function object throws. */ ~scope_exit() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( detail::conjunction< detail::is_nothrow_invocable< Func& >, detail::is_nothrow_invocable< Cond& > >::value )) { if (BOOST_LIKELY(m_data.m_active && m_data.get_cond()())) m_data.get_func()(); } /*! * \brief Returns \c true if the scope guard is active, otherwise \c false. * * \note This method does not call the condition function object specified on construction. * * **Throws:** Nothing. */ bool active() const noexcept { return m_data.m_active; } /*! * \brief Activates or deactivates the scope guard. * * **Throws:** Nothing. * * \param active The active status to set. * * \post `this->active() == active` */ void set_active(bool active) noexcept { m_data.m_active = active; } }; #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) template< typename Func > explicit scope_exit(Func) -> scope_exit< Func >; template< typename Func > explicit scope_exit(Func, bool) -> scope_exit< Func >; template< typename Func, typename Cond > explicit scope_exit(Func, Cond) -> scope_exit< Func, Cond >; template< typename Func, typename Cond > explicit scope_exit(Func, Cond, bool) -> scope_exit< Func, Cond >; #endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) /*! * \brief Creates a scope guard with a given action function object. * * **Effects:** Constructs a scope guard as if by calling * `scope_exit< std::decay_t< F > >(std::forward< F >(func), active)`. * * \param func The callable action function object to invoke on destruction. * \param active Indicates whether the scope guard should be active upon construction. */ template< typename F > inline scope_exit< typename std::decay< F >::type > make_scope_exit(F&& func, bool active = true) noexcept(std::is_nothrow_constructible< scope_exit< typename std::decay< F >::type >, F, bool >::value) { return scope_exit< typename std::decay< F >::type >(static_cast< F&& >(func), active); } /*! * \brief Creates a conditional scope guard with given callable function objects. * * **Effects:** Constructs a scope guard as if by calling * `scope_exit< std::decay_t< F >, std::decay_t< C > >( * std::forward< F >(func), std::forward< C >(cond), active)`. * * \param func The callable action function object to invoke on destruction. * \param cond The callable condition function object. * \param active Indicates whether the scope guard should be active upon construction. */ template< typename F, typename C > inline #if !defined(BOOST_SCOPE_DOXYGEN) typename std::enable_if< std::is_constructible< scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >, F, C, bool >::value, scope_exit< typename std::decay< F >::type, typename std::decay< C >::type > >::type #else scope_exit< typename std::decay< F >::type, typename std::decay< C >::type > #endif make_scope_exit(F&& func, C&& cond, bool active = true) noexcept(std::is_nothrow_constructible< scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >, F, C, bool >::value) { return scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >(static_cast< F&& >(func), static_cast< C&& >(cond), active); } } // namespace scope } // namespace boost #include #endif // BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_