defer.hpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * https://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * Copyright (c) 2022-2024 Andrey Semashev
  7. */
  8. /*!
  9. * \file scope/defer.hpp
  10. *
  11. * This header contains definition of \c defer_guard template.
  12. */
  13. #ifndef BOOST_SCOPE_DEFER_HPP_INCLUDED_
  14. #define BOOST_SCOPE_DEFER_HPP_INCLUDED_
  15. #include <type_traits>
  16. #include <boost/scope/detail/config.hpp>
  17. #include <boost/scope/detail/is_not_like.hpp>
  18. #include <boost/scope/detail/move_or_copy_construct_ref.hpp>
  19. #include <boost/scope/detail/type_traits/conjunction.hpp>
  20. #include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp>
  21. #include <boost/scope/detail/header.hpp>
  22. #ifdef BOOST_HAS_PRAGMA_ONCE
  23. #pragma once
  24. #endif
  25. namespace boost {
  26. namespace scope {
  27. template< typename Func >
  28. class defer_guard;
  29. namespace detail {
  30. // Workaround for clang < 5.0 which can't pass defer_guard as a template template parameter from within defer_guard definition
  31. template< typename T >
  32. using is_not_like_defer_guard = detail::is_not_like< T, defer_guard >;
  33. } // namespace detail
  34. /*!
  35. * \brief Defer guard that invokes a function upon leaving the scope.
  36. *
  37. * The scope guard wraps a function object callable with no arguments
  38. * that can be one of:
  39. *
  40. * \li A user-defined class with a public `operator()`.
  41. * \li An lvalue reference to such class.
  42. * \li An lvalue reference or pointer to function taking no arguments.
  43. *
  44. * The defer guard unconditionally invokes the wrapped function object
  45. * on destruction.
  46. */
  47. template< typename Func >
  48. class defer_guard
  49. {
  50. //! \cond
  51. private:
  52. struct data
  53. {
  54. Func m_func;
  55. template< typename F, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type >
  56. explicit data(F&& func, std::true_type) noexcept :
  57. m_func(static_cast< F&& >(func))
  58. {
  59. }
  60. template< typename F, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type >
  61. explicit data(F&& func, std::false_type) try :
  62. m_func(static_cast< F&& >(func))
  63. {
  64. }
  65. catch (...)
  66. {
  67. func();
  68. }
  69. };
  70. data m_data;
  71. //! \endcond
  72. public:
  73. /*!
  74. * \brief Constructs a defer guard with a given callable function object.
  75. *
  76. * **Requires:** \c Func is constructible from \a func.
  77. *
  78. * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from
  79. * `std::forward< F >(func)`, otherwise constructs from `func`.
  80. *
  81. * If \c Func construction throws, invokes \a func before returning with the exception.
  82. *
  83. * **Throws:** Nothing, unless construction of the function object throws.
  84. *
  85. * \param func The callable function object to invoke on destruction.
  86. */
  87. template<
  88. typename F
  89. //! \cond
  90. , typename = typename std::enable_if< detail::conjunction<
  91. std::is_constructible<
  92. data,
  93. typename detail::move_or_copy_construct_ref< F, Func >::type,
  94. typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< F, Func >::type >::type
  95. >,
  96. detail::is_not_like_defer_guard< F >
  97. >::value >::type
  98. //! \endcond
  99. >
  100. defer_guard(F&& func)
  101. noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
  102. std::is_nothrow_constructible<
  103. data,
  104. typename detail::move_or_copy_construct_ref< F, Func >::type,
  105. typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< F, Func >::type >::type
  106. >::value
  107. )) :
  108. m_data
  109. (
  110. static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func),
  111. typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< F, Func >::type >::type()
  112. )
  113. {
  114. }
  115. defer_guard(defer_guard const&) = delete;
  116. defer_guard& operator= (defer_guard const&) = delete;
  117. /*!
  118. * \brief Invokes the wrapped callable function object and destroys the callable.
  119. *
  120. * **Throws:** Nothing, unless invoking the callable throws.
  121. */
  122. ~defer_guard() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< Func& >::value))
  123. {
  124. m_data.m_func();
  125. }
  126. };
  127. #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
  128. template< typename Func >
  129. defer_guard(Func) -> defer_guard< Func >;
  130. #endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
  131. } // namespace scope
  132. //! \cond
  133. #if defined(BOOST_MSVC)
  134. #define BOOST_SCOPE_DETAIL_UNIQUE_VAR_TAG __COUNTER__
  135. #else
  136. #define BOOST_SCOPE_DETAIL_UNIQUE_VAR_TAG __LINE__
  137. #endif
  138. //! \endcond
  139. /*!
  140. * \brief The macro creates a uniquely named defer guard.
  141. *
  142. * The macro should be followed by a function object that should be called
  143. * on leaving the current scope. Usage example:
  144. *
  145. * ```
  146. * BOOST_SCOPE_DEFER []
  147. * {
  148. * std::cout << "Hello world!" << std::endl;
  149. * };
  150. * ```
  151. *
  152. * \note Using this macro requires C++17.
  153. */
  154. #define BOOST_SCOPE_DEFER \
  155. boost::scope::defer_guard BOOST_JOIN(_boost_defer_guard_, BOOST_SCOPE_DETAIL_UNIQUE_VAR_TAG) =
  156. } // namespace boost
  157. #include <boost/scope/detail/footer.hpp>
  158. #endif // BOOST_SCOPE_DEFER_HPP_INCLUDED_