diff --git a/include/boost/scope/scope_check.hpp b/include/boost/scope/scope_check.hpp deleted file mode 100644 index 6162022..0000000 --- a/include/boost/scope/scope_check.hpp +++ /dev/null @@ -1,512 +0,0 @@ -/* - * 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_check.hpp - * - * This header contains definition of \c scope_check template. - */ - -#ifndef BOOST_SCOPE_SCOPE_CHECK_HPP_INCLUDED_ -#define BOOST_SCOPE_SCOPE_CHECK_HPP_INCLUDED_ - -#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_check; - -namespace detail { - -// Workaround for clang < 5.0 which can't pass scope_check as a template template parameter from within scope_check definition -template< typename T > -using is_not_like_scope_check = detail::is_not_like< T, scope_check >; - -//! 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 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 to function taking no arguments. - * - * Additionally, the condition function object `operator()` must not throw, - * as otherwise the action function object may not be called. - * - * 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)` or `release()`. 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 > -class scope_check -{ -//! \cond -private: - struct func_holder : - public detail::compact_storage< Func > - { - typedef detail::compact_storage< Func > func_base; - - 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 > - { - typedef detail::compact_storage< Cond > cond_base; - - 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. - * - * **Effects:** Constructs the scope guard as if by calling - * `scope_check(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< - std::is_nothrow_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_check< F > - >::value >::type - //! \endcond - > - explicit scope_check(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_check(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_check(scope_check&& 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_check& operator= (scope_check&&) = delete; - - scope_check(scope_check const&) = delete; - scope_check& operator= (scope_check 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_check() - 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; - } - - /*! - * \brief Deactivates the scope guard. - * - * **Effects:** As if `set_active(false)`. - * - * **Throws:** Nothing. - * - * \post `this->active() == false` - */ - void release() noexcept - { - m_data.m_active = false; - } -}; - -#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) -template< typename Func, typename Cond > -scope_check(Func, Cond) -> scope_check< Func, Cond >; - -template< typename Func, typename Cond > -scope_check(Func, Cond, bool) -> scope_check< Func, Cond >; - -template< typename Func, typename Cond > -scope_check(scope_check< Func, Cond >&&) -> scope_check< Func, Cond >; -#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) - -/*! - * \brief Creates a conditional scope guard with given callable function objects. - * - * **Effects:** Constructs a scope guard as if by calling - * `scope_check< std::remove_cvref_t< F >, - * std::remove_cvref_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 scope_check< - typename std::remove_cv< typename std::remove_reference< F >::type >::type, - typename std::remove_cv< typename std::remove_reference< C >::type >::type -> make_scope_check(F&& func, C&& cond, bool active = true) - noexcept(std::is_nothrow_constructible< - scope_check< - typename std::remove_cv< typename std::remove_reference< F >::type >::type, - typename std::remove_cv< typename std::remove_reference< C >::type >::type - >, - F, - C, - bool - >::value) -{ - return scope_check< - typename std::remove_cv< typename std::remove_reference< F >::type >::type, - typename std::remove_cv< typename std::remove_reference< C >::type >::type - >(static_cast< F&& >(func), static_cast< C&& >(cond), active); -} - -} // namespace scope -} // namespace boost - -#include - -#endif // BOOST_SCOPE_SCOPE_CHECK_HPP_INCLUDED_ diff --git a/include/boost/scope/scope_exit.hpp b/include/boost/scope/scope_exit.hpp index 8d90283..0a95bb2 100644 --- a/include/boost/scope/scope_exit.hpp +++ b/include/boost/scope/scope_exit.hpp @@ -3,7 +3,7 @@ * (See accompanying file LICENSE_1_0.txt or copy at * https://www.boost.org/LICENSE_1_0.txt) * - * Copyright (c) 2022 Andrey Semashev + * Copyright (c) 2023 Andrey Semashev */ /*! * \file scope/scope_exit.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,7 @@ namespace boost { namespace scope { -template< typename Func > +template< typename Func, typename Cond > class scope_exit; namespace detail { @@ -39,59 +40,228 @@ namespace detail { 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 Scope exit guard that invokes a function upon leaving the scope. + * \brief A predicate that always returns \c true. * - * A scope exit guard wraps a function object callable with no arguments - * that can be one of: + * 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 + typedef bool result_type; + + /*! + * **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 to function taking no arguments. * + * 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, the scope guard - * invokes the wrapped function object on destruction. If inactive, - * the scope guard does not call the wrapped function object. + * 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)` or `release()`. 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. + * or calling `set_active(false)` or `release()`. 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 > +template< typename Func, typename Cond = always_true > class scope_exit { //! \cond private: - struct data : + struct func_holder : public detail::compact_storage< Func > { typedef detail::compact_storage< Func > func_base; + 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 > + { + typedef detail::compact_storage< Cond > cond_base; + + 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 = typename std::enable_if< std::is_constructible< Func, F >::value >::type > - explicit data(F&& func, bool active, std::true_type) noexcept : - func_base(static_cast< F&& >(func)), + 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) { } - template< typename F, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type > - explicit data(F&& func, bool active, std::false_type) try : - func_base(static_cast< F&& >(func)), - 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(); } - catch (...) + + Cond const& get_cond() const noexcept { - if (active) - func(); + return cond_holder::get(); + } + + bool deactivate() noexcept + { + bool active = m_active; + m_active = false; + return active; } }; @@ -100,19 +270,20 @@ class scope_exit //! \endcond public: /*! - * \brief Constructs a scope guard with a given callable function object. + * \brief Constructs a scope guard with a given callable action function object. * - * **Requires:** \c Func is constructible from \a func. + * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible. * - * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from - * `std::forward< F >(func)`, otherwise constructs from `func`. + * \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. * - * If \c Func construction throws and \a active is \c true, invokes \a func before - * returning with the exception. + * **Effects:** Constructs the scope guard as if by calling + * `scope_exit(std::forward< F >(func), Cond(), active)`. * - * **Throws:** Nothing, unless construction of the function object throws. + * **Throws:** Nothing, unless construction of the function objects throw. * - * \param func The callable function object to invoke on destruction. + * \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` @@ -121,7 +292,13 @@ class scope_exit typename F //! \cond , typename = typename std::enable_if< detail::conjunction< - std::is_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, bool, typename std::is_nothrow_constructible< Func, F >::type >, + std::is_nothrow_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 @@ -131,23 +308,91 @@ class scope_exit std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< F, Func >::type, - bool, - typename std::is_nothrow_constructible< Func, F >::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), active, typename std::is_nothrow_constructible< Func, F >::type()) + 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 is nothrow move-constructible or copy-constructible. + * **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. + * 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. * - * **Throws:** Nothing, unless construction of the function object throws. + * If the construction succeeds, marks \a that as inactive. + * + * **Throws:** Nothing, unless move-construction of the function objects throw. * * \param that Move source. * @@ -158,8 +403,8 @@ class scope_exit bool Requires = std::is_constructible< data, typename detail::move_or_copy_construct_ref< Func >::type, - bool, - typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< Func >::type >::type + typename detail::move_or_copy_construct_ref< Cond >::type, + bool >::value, typename = typename std::enable_if< Requires >::type > @@ -169,18 +414,17 @@ class scope_exit std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< Func >::type, - bool, - typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< Func >::type >::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()), - that.m_data.m_active, - typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< Func >::type >::type() + 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() ) { - that.m_data.m_active = false; } scope_exit& operator= (scope_exit&&) = delete; @@ -189,19 +433,28 @@ class scope_exit scope_exit& operator= (scope_exit const&) = delete; /*! - * \brief If `active() == true`, invokes the wrapped callable function object. Destroys the callable. + * \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 the callable throws. + * **Throws:** Nothing, unless invoking a function object throws. */ - ~scope_exit() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< Func& >::value)) + ~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()(); + 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 @@ -241,20 +494,29 @@ class scope_exit #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) template< typename Func > scope_exit(Func) -> scope_exit< Func >; + template< typename Func > scope_exit(Func, bool) -> scope_exit< Func >; -template< typename Func > -scope_exit(scope_exit< Func >&&) -> scope_exit< Func >; +template< typename Func, typename Cond > +scope_exit(Func, Cond) -> scope_exit< Func, Cond >; + +template< typename Func, typename Cond > +scope_exit(Func, Cond, bool) -> scope_exit< Func, Cond >; + +template< typename Func, typename Cond > +scope_exit(scope_exit< Func, Cond >&&) -> scope_exit< Func, Cond >; #endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) /*! - * \brief Creates a scope exit guard with a given callable function object. + * \brief Creates a scope guard with given callable function object. * * **Effects:** Constructs a scope guard as if by calling - * `scope_exit< std::remove_cvref_t< F > >(std::forward< F >(func), active)`. + * `scope_exit< std::remove_cvref_t< F > >( + * std::forward< F >(func), active)`. * - * \param func The callable function object to invoke on destruction. + * \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 > @@ -262,7 +524,9 @@ inline scope_exit< typename std::remove_cv< typename std::remove_reference< F >::type >::type > make_scope_exit(F&& func, bool active = true) noexcept(std::is_nothrow_constructible< - scope_exit< typename std::remove_cv< typename std::remove_reference< F >::type >::type >, + scope_exit< + typename std::remove_cv< typename std::remove_reference< F >::type >::type + >, F, bool >::value) @@ -272,6 +536,50 @@ inline scope_exit< >(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::remove_cvref_t< F >, + * std::remove_cvref_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 typename std::enable_if< + std::is_constructible< + scope_exit< + typename std::remove_cv< typename std::remove_reference< F >::type >::type, + typename std::remove_cv< typename std::remove_reference< C >::type >::type + >, + F, + C, + bool + >::value, + scope_exit< + typename std::remove_cv< typename std::remove_reference< F >::type >::type, + typename std::remove_cv< typename std::remove_reference< C >::type >::type + > +>::type make_scope_exit(F&& func, C&& cond, bool active = true) + noexcept(std::is_nothrow_constructible< + scope_exit< + typename std::remove_cv< typename std::remove_reference< F >::type >::type, + typename std::remove_cv< typename std::remove_reference< C >::type >::type + >, + F, + C, + bool + >::value) +{ + return scope_exit< + typename std::remove_cv< typename std::remove_reference< F >::type >::type, + typename std::remove_cv< typename std::remove_reference< C >::type >::type + >(static_cast< F&& >(func), static_cast< C&& >(cond), active); +} + } // namespace scope } // namespace boost diff --git a/include/boost/scope/scope_fail.hpp b/include/boost/scope/scope_fail.hpp index fd58015..26430ab 100644 --- a/include/boost/scope/scope_fail.hpp +++ b/include/boost/scope/scope_fail.hpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -59,7 +59,7 @@ using is_not_like_scope_fail = detail::is_not_like< T, scope_fail >; * due to an exception - the action function object will not be called if * the scope is left normally. * - * \sa scope_check + * \sa scope_exit * \sa scope_success * * \tparam Func Scope guard action function object type. @@ -67,11 +67,11 @@ using is_not_like_scope_fail = detail::is_not_like< T, scope_fail >; */ template< typename Func, typename Cond = exception_checker > class scope_fail : - public scope_check< Func, Cond > + public scope_exit< Func, Cond > { //! \cond private: - typedef scope_check< Func, Cond > base_type; + typedef scope_exit< Func, Cond > base_type; //! \endcond public: diff --git a/include/boost/scope/scope_success.hpp b/include/boost/scope/scope_success.hpp index d784767..16f22d9 100644 --- a/include/boost/scope/scope_success.hpp +++ b/include/boost/scope/scope_success.hpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -103,7 +103,7 @@ class logical_not * due to an exception - the action function object will only be called if * the scope is left normally. * - * \sa scope_check + * \sa scope_exit * \sa scope_fail * * \tparam Func Scope guard action function object type. @@ -111,11 +111,11 @@ class logical_not */ template< typename Func, typename Cond = exception_checker > class scope_success : - public scope_check< Func, detail::logical_not< Cond > > + public scope_exit< Func, detail::logical_not< Cond > > { //! \cond private: - typedef scope_check< Func, detail::logical_not< Cond > > base_type; + typedef scope_exit< Func, detail::logical_not< Cond > > base_type; //! \endcond public: diff --git a/test/Jamfile b/test/Jamfile index 20bea4b..9a4d0b0 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -36,6 +36,7 @@ project cxx11_lambdas cxx11_auto_declarations cxx11_unified_initialization_syntax + cxx11_hdr_system_error ] ; diff --git a/test/compile_fail/scope_check_noncopyable.cpp b/test/compile_fail/scope_check_noncopyable.cpp deleted file mode 100644 index 006f300..0000000 --- a/test/compile_fail/scope_check_noncopyable.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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_check_noncopyable.cpp - * \author Andrey Semashev - * - * \brief This file tests that \c scope_check is noncopyable. - */ - -#include -#include -#include "function_types.hpp" - -int main() -{ - int n = 0, err = 0; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > > guard1{ normal_func(n), boost::scope::check_error_code(err) }; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > > guard2 = guard1; - - return 0; -} diff --git a/test/run/scope_check.cpp b/test/run/scope_check.cpp deleted file mode 100644 index 5ee9ec7..0000000 --- a/test/run/scope_check.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - * 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_check.cpp - * \author Andrey Semashev - * - * \brief This file contains tests for \c scope_check. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "function_types.hpp" - -int g_n = 0, g_c = 0; - -struct always_true -{ - bool operator()() const noexcept - { - return true; - } -}; - -struct always_false -{ - bool operator()() const noexcept - { - return false; - } -}; - -void check_normal() -{ - int n = 0; - { - boost::scope::scope_check< normal_func, always_true > guard{ normal_func(n), always_true() }; - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - boost::scope::scope_check< normal_func, always_false > guard{ normal_func(n), always_false() }; - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - boost::scope::scope_check< normal_func, always_true > guard{ normal_func(n) }; - BOOST_TEST(guard.active()); - guard.release(); - BOOST_TEST(!guard.active()); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - boost::scope::scope_check< moveable_only_func, always_true > guard{ moveable_only_func(n) }; - BOOST_TEST(guard.active()); - guard.set_active(false); - BOOST_TEST(!guard.active()); - guard.set_active(true); - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - boost::scope::scope_check< normal_func, always_true > guard(normal_func(n), always_true(), false); - BOOST_TEST(!guard.active()); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - boost::scope::scope_check< normal_func, always_true > guard(normal_func(n), false); - BOOST_TEST(!guard.active()); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - boost::scope::scope_check< normal_func, always_true > guard(normal_func(n), false); - BOOST_TEST(!guard.active()); - guard.set_active(true); - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - boost::scope::scope_check< moveable_only_func, always_true > guard1{ moveable_only_func(n) }; - BOOST_TEST(guard1.active()); - boost::scope::scope_check< moveable_only_func, always_true > guard2 = std::move(guard1); - BOOST_TEST(!guard1.active()); - BOOST_TEST(guard2.active()); - boost::scope::scope_check< moveable_only_func, always_true > guard3 = std::move(guard1); - BOOST_TEST(!guard3.active()); - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - normal_func func(n); - always_true cond; - boost::scope::scope_check< normal_func&, always_true& > guard(func, cond); - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - normal_func func(n); - always_true cond; - boost::scope::scope_check< normal_func&, always_true& > guard1(func, cond); - BOOST_TEST(guard1.active()); - boost::scope::scope_check< normal_func&, always_true& > guard2 = std::move(guard1); - BOOST_TEST(!guard1.active()); - BOOST_TEST(guard2.active()); - boost::scope::scope_check< normal_func&, always_true& > guard3 = std::move(guard1); - BOOST_TEST(!guard3.active()); - } - BOOST_TEST_EQ(n, 1); - - struct local - { - static void raw_func() - { - ++g_n; - } - - static bool raw_cond() - { - ++g_c; - return true; - } - }; - - g_n = 0; - g_c = 0; - { - boost::scope::scope_check< void (&)(), bool (&)() > guard(local::raw_func, local::raw_cond); - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(g_c, 1); - BOOST_TEST_EQ(g_n, 1); - - g_n = 0; - g_c = 0; - { - boost::scope::scope_check< void (&)(), bool (&)() > guard1(local::raw_func, local::raw_cond); - BOOST_TEST(guard1.active()); - boost::scope::scope_check< void (&)(), bool (&)() > guard2 = std::move(guard1); - BOOST_TEST(!guard1.active()); - BOOST_TEST(guard2.active()); - boost::scope::scope_check< void (&)(), bool (&)() > guard3 = std::move(guard1); - BOOST_TEST(!guard3.active()); - } - BOOST_TEST_EQ(g_c, 1); - BOOST_TEST_EQ(g_n, 1); -} - -void check_throw() -{ - int n = 0; - try - { - boost::scope::scope_check< throw_on_copy_func, boost::scope::exception_checker > guard{ throw_on_copy_func(n) }; - BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_func"); - } - catch (...) {} - BOOST_TEST_EQ(n, 1); - - n = 0; - try - { - boost::scope::scope_check< throw_on_move_func, boost::scope::exception_checker > guard{ throw_on_move_func(n) }; - } - catch (...) - { - BOOST_ERROR("An exception is not expected to be thrown by throw_on_move_func (copy ctor should be used)"); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - bool scope_ended = false, exception_thrown = false, func_destroyed = false; - try - { - boost::scope::scope_check< throw_on_call_func, boost::scope::exception_checker > guard{ throw_on_call_func(n, func_destroyed) }; - func_destroyed = false; - scope_ended = true; - } - catch (...) - { - exception_thrown = true; - } - BOOST_TEST_EQ(n, 0); - BOOST_TEST(scope_ended); - BOOST_TEST(!exception_thrown); - BOOST_TEST(func_destroyed); -} - -void check_cond() -{ - int n = 0; - { - int err = 0; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) }; - BOOST_TEST(guard.active()); - err = -1; - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - int err = 0; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) }; - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - int err = 0; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err), false }; - BOOST_TEST(!guard.active()); - err = -1; - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - std::error_code err{}; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(err) }; - BOOST_TEST(guard.active()); - err = std::make_error_code(std::errc::invalid_argument); - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - std::error_code err{}; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(err) }; - BOOST_TEST(guard.active()); - } - BOOST_TEST_EQ(n, 0); - - n = 0; - try - { - int err = 0; - boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) }; - BOOST_TEST(guard.active()); - throw std::runtime_error("error"); - } - catch (...) {} - BOOST_TEST_EQ(n, 0); // exception is not the failure condition, err was still 0 when the scope guard was destroyed -} - -void check_deduction() -{ - int n = 0; - { - int err = 0; - auto guard = boost::scope::make_scope_check(normal_func(n), boost::scope::check_error_code(err)); - BOOST_TEST(guard.active()); - BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > >); - err = -1; - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - int err = 0; - auto guard = boost::scope::make_scope_check(normal_func(n), boost::scope::check_error_code(err), false); - BOOST_TEST(!guard.active()); - BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > >); - err = -1; - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - int err = 0; - const normal_func func{ n }; - const auto cond = boost::scope::check_error_code(err); - auto guard = boost::scope::make_scope_check(func, cond, true); - BOOST_TEST(guard.active()); - BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > >); - err = -1; - } - BOOST_TEST_EQ(n, 1); - -#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) - n = 0; - { - int err = 0; - boost::scope::scope_check guard{ normal_func(n), boost::scope::check_error_code(err) }; - BOOST_TEST(guard.active()); - BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > >); - err = -1; - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - int err = 0; - boost::scope::scope_check guard{ normal_func(n), boost::scope::error_code_checker(err), false }; - BOOST_TEST(!guard.active()); - BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_check< normal_func, boost::scope::error_code_checker< int > >); - err = -1; - } - BOOST_TEST_EQ(n, 0); - - n = 0; - { - int err = -1; - boost::scope::scope_check guard([&n] { ++n; }, [&err]() noexcept { return err < 0; }); - BOOST_TEST(guard.active()); - err = -1; - } - BOOST_TEST_EQ(n, 1); - - n = 0; - { - boost::scope::scope_check guard1{ normal_func(n), always_true() }; - boost::scope::scope_check guard2 = std::move(guard1); - BOOST_TEST(guard2.active()); - BOOST_TEST_TRAIT_SAME(decltype(guard2), boost::scope::scope_check< normal_func, always_true >); - } - BOOST_TEST_EQ(n, 1); -#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) -} - -int main() -{ - check_normal(); - check_throw(); - check_cond(); - check_deduction(); - - return boost::report_errors(); -} diff --git a/test/run/scope_exit.cpp b/test/run/scope_exit.cpp index 2e11da4..584758b 100644 --- a/test/run/scope_exit.cpp +++ b/test/run/scope_exit.cpp @@ -13,16 +13,19 @@ */ #include +#include +#include #include #include #include #include #include +#include #include "function_types.hpp" -int g_n = 0; +int g_n = 0, g_c = 0; -void check_normal() +void check_normal_default_cond() { int n = 0; { @@ -128,7 +131,155 @@ void check_normal() BOOST_TEST_EQ(g_n, 1); } -void check_throw() +struct always_true +{ + bool operator()() const noexcept + { + return true; + } +}; + +struct always_false +{ + bool operator()() const noexcept + { + return false; + } +}; + +void check_normal() +{ + int n = 0; + { + boost::scope::scope_exit< normal_func, always_true > guard{ normal_func(n), always_true() }; + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + boost::scope::scope_exit< normal_func, always_false > guard{ normal_func(n), always_false() }; + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + boost::scope::scope_exit< normal_func, always_true > guard{ normal_func(n) }; + BOOST_TEST(guard.active()); + guard.release(); + BOOST_TEST(!guard.active()); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + boost::scope::scope_exit< moveable_only_func, always_true > guard{ moveable_only_func(n) }; + BOOST_TEST(guard.active()); + guard.set_active(false); + BOOST_TEST(!guard.active()); + guard.set_active(true); + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + boost::scope::scope_exit< normal_func, always_true > guard(normal_func(n), always_true(), false); + BOOST_TEST(!guard.active()); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + boost::scope::scope_exit< normal_func, always_true > guard(normal_func(n), false); + BOOST_TEST(!guard.active()); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + boost::scope::scope_exit< normal_func, always_true > guard(normal_func(n), false); + BOOST_TEST(!guard.active()); + guard.set_active(true); + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + boost::scope::scope_exit< moveable_only_func, always_true > guard1{ moveable_only_func(n) }; + BOOST_TEST(guard1.active()); + boost::scope::scope_exit< moveable_only_func, always_true > guard2 = std::move(guard1); + BOOST_TEST(!guard1.active()); + BOOST_TEST(guard2.active()); + boost::scope::scope_exit< moveable_only_func, always_true > guard3 = std::move(guard1); + BOOST_TEST(!guard3.active()); + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + normal_func func(n); + always_true cond; + boost::scope::scope_exit< normal_func&, always_true& > guard(func, cond); + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + normal_func func(n); + always_true cond; + boost::scope::scope_exit< normal_func&, always_true& > guard1(func, cond); + BOOST_TEST(guard1.active()); + boost::scope::scope_exit< normal_func&, always_true& > guard2 = std::move(guard1); + BOOST_TEST(!guard1.active()); + BOOST_TEST(guard2.active()); + boost::scope::scope_exit< normal_func&, always_true& > guard3 = std::move(guard1); + BOOST_TEST(!guard3.active()); + } + BOOST_TEST_EQ(n, 1); + + struct local + { + static void raw_func() + { + ++g_n; + } + + static bool raw_cond() + { + ++g_c; + return true; + } + }; + + g_n = 0; + g_c = 0; + { + boost::scope::scope_exit< void (&)(), bool (&)() > guard(local::raw_func, local::raw_cond); + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(g_c, 1); + BOOST_TEST_EQ(g_n, 1); + + g_n = 0; + g_c = 0; + { + boost::scope::scope_exit< void (&)(), bool (&)() > guard1(local::raw_func, local::raw_cond); + BOOST_TEST(guard1.active()); + boost::scope::scope_exit< void (&)(), bool (&)() > guard2 = std::move(guard1); + BOOST_TEST(!guard1.active()); + BOOST_TEST(guard2.active()); + boost::scope::scope_exit< void (&)(), bool (&)() > guard3 = std::move(guard1); + BOOST_TEST(!guard3.active()); + } + BOOST_TEST_EQ(g_c, 1); + BOOST_TEST_EQ(g_n, 1); +} + +void check_throw_default_cond() { int n = 0; try @@ -178,6 +329,103 @@ void check_throw() BOOST_TEST(func_destroyed); } +void check_throw() +{ + int n = 0; + try + { + boost::scope::scope_exit< throw_on_copy_func, boost::scope::exception_checker > guard{ throw_on_copy_func(n) }; + BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_func"); + } + catch (...) {} + BOOST_TEST_EQ(n, 1); + + n = 0; + try + { + boost::scope::scope_exit< throw_on_move_func, boost::scope::exception_checker > guard{ throw_on_move_func(n) }; + } + catch (...) + { + BOOST_ERROR("An exception is not expected to be thrown by throw_on_move_func (copy ctor should be used)"); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + bool scope_ended = false, exception_thrown = false, func_destroyed = false; + try + { + boost::scope::scope_exit< throw_on_call_func, boost::scope::exception_checker > guard{ throw_on_call_func(n, func_destroyed) }; + func_destroyed = false; + scope_ended = true; + } + catch (...) + { + exception_thrown = true; + } + BOOST_TEST_EQ(n, 0); + BOOST_TEST(scope_ended); + BOOST_TEST(!exception_thrown); + BOOST_TEST(func_destroyed); +} + +void check_cond() +{ + int n = 0; + { + int err = 0; + boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) }; + BOOST_TEST(guard.active()); + err = -1; + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + int err = 0; + boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) }; + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + int err = 0; + boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err), false }; + BOOST_TEST(!guard.active()); + err = -1; + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + std::error_code err{}; + boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(err) }; + BOOST_TEST(guard.active()); + err = std::make_error_code(std::errc::invalid_argument); + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + std::error_code err{}; + boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(err) }; + BOOST_TEST(guard.active()); + } + BOOST_TEST_EQ(n, 0); + + n = 0; + try + { + int err = 0; + boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) }; + BOOST_TEST(guard.active()); + throw std::runtime_error("error"); + } + catch (...) {} + BOOST_TEST_EQ(n, 0); // exception is not the failure condition, err was still 0 when the scope guard was destroyed +} + void check_deduction() { int n = 0; @@ -205,6 +453,38 @@ void check_deduction() } BOOST_TEST_EQ(n, 1); + n = 0; + { + int err = 0; + auto guard = boost::scope::make_scope_exit(normal_func(n), boost::scope::check_error_code(err)); + BOOST_TEST(guard.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >); + err = -1; + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + int err = 0; + auto guard = boost::scope::make_scope_exit(normal_func(n), boost::scope::check_error_code(err), false); + BOOST_TEST(!guard.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >); + err = -1; + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + int err = 0; + const normal_func func{ n }; + const auto cond = boost::scope::check_error_code(err); + auto guard = boost::scope::make_scope_exit(func, cond, true); + BOOST_TEST(guard.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >); + err = -1; + } + BOOST_TEST_EQ(n, 1); + #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) n = 0; { @@ -214,6 +494,14 @@ void check_deduction() } BOOST_TEST_EQ(n, 1); + n = 0; + { + boost::scope::scope_exit guard{ normal_func(n), true }; + BOOST_TEST(guard.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func >); + } + BOOST_TEST_EQ(n, 1); + n = 0; { boost::scope::scope_exit guard([&n] { ++n; }); @@ -229,12 +517,52 @@ void check_deduction() BOOST_TEST_TRAIT_SAME(decltype(guard2), boost::scope::scope_exit< normal_func >); } BOOST_TEST_EQ(n, 1); + + n = 0; + { + int err = 0; + boost::scope::scope_exit guard{ normal_func(n), boost::scope::check_error_code(err) }; + BOOST_TEST(guard.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >); + err = -1; + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + int err = 0; + boost::scope::scope_exit guard{ normal_func(n), boost::scope::error_code_checker(err), false }; + BOOST_TEST(!guard.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >); + err = -1; + } + BOOST_TEST_EQ(n, 0); + + n = 0; + { + int err = -1; + boost::scope::scope_exit guard([&n] { ++n; }, [&err]() noexcept { return err < 0; }); + BOOST_TEST(guard.active()); + err = -1; + } + BOOST_TEST_EQ(n, 1); + + n = 0; + { + boost::scope::scope_exit guard1{ normal_func(n), always_true() }; + boost::scope::scope_exit guard2 = std::move(guard1); + BOOST_TEST(guard2.active()); + BOOST_TEST_TRAIT_SAME(decltype(guard2), boost::scope::scope_exit< normal_func, always_true >); + } + BOOST_TEST_EQ(n, 1); #endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) } int main() { + check_normal_default_cond(); check_normal(); + check_throw_default_cond(); check_throw(); check_deduction();