Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

217 lines
7.6KB

  1. /*
  2. * Scope Guard
  3. * Copyright (C) 2017-2018 offa
  4. *
  5. * This file is part of Scope Guard.
  6. *
  7. * Scope Guard is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * Scope Guard is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with Scope Guard. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #pragma once
  21. #include "scope_exit.h"
  22. #include "detail/wrapper.h"
  23. #include <utility>
  24. #include <type_traits>
  25. namespace sr
  26. {
  27. namespace detail
  28. {
  29. template<class T, class U,
  30. class R = std::conditional_t<std::is_nothrow_constructible_v<T, U>, U&&, U>
  31. >
  32. constexpr R forward_if_nothrow_constructible(U&& arg)
  33. {
  34. return std::forward<U>(arg);
  35. }
  36. }
  37. template<class R, class D>
  38. class unique_resource
  39. {
  40. public:
  41. template<class RR, class DD,
  42. std::enable_if_t<(std::is_constructible_v<R ,RR> && std::is_constructible_v<D, DD>
  43. && (std::is_nothrow_constructible_v<R, RR> || std::is_constructible_v<R, RR&>)
  44. && (std::is_nothrow_constructible_v<D, DD> || std::is_constructible_v<D, DD&>)), int> = 0
  45. >
  46. explicit unique_resource(RR&& r, DD&& d) noexcept((std::is_nothrow_constructible_v<R, RR> || std::is_nothrow_constructible_v<R, RR&>)
  47. && (std::is_nothrow_constructible_v<D, DD> || std::is_nothrow_constructible_v<D, DD&>))
  48. : m_resource(detail::forward_if_nothrow_constructible<R, RR>(std::forward<RR>(r)), scope_exit{[&r, &d] { d(r); }}),
  49. m_deleter(detail::forward_if_nothrow_constructible<D, DD>(std::forward<DD>(d)), scope_exit{[this, &d] { d(get()); }}),
  50. m_execute_on_destruction(true)
  51. {
  52. }
  53. unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible_v<R>
  54. && std::is_nothrow_move_constructible_v<D>)
  55. : m_resource(std::move_if_noexcept(other.m_resource.get()), scope_exit{[] { }}),
  56. m_deleter(std::move_if_noexcept(other.m_deleter.get()), scope_exit{[&other] {
  57. other.get_deleter()(other.m_resource.get());
  58. other.release(); }}),
  59. m_execute_on_destruction(std::exchange(other.m_execute_on_destruction, false))
  60. {
  61. }
  62. unique_resource(const unique_resource&) = delete;
  63. ~unique_resource()
  64. {
  65. reset();
  66. }
  67. void reset() noexcept
  68. {
  69. if( m_execute_on_destruction == true )
  70. {
  71. m_execute_on_destruction = false;
  72. get_deleter()(m_resource.get());
  73. }
  74. }
  75. template<class RR>
  76. void reset(RR&& r)
  77. {
  78. reset();
  79. using R1 = typename detail::Wrapper<R>::type;
  80. auto se = scope_exit{[this, &r] { get_deleter()(r); }};
  81. if constexpr( std::is_nothrow_assignable_v<R1&, RR> == true )
  82. {
  83. m_resource.reset(std::forward<RR>(r));
  84. }
  85. else
  86. {
  87. m_resource.reset(std::as_const(r));
  88. }
  89. m_execute_on_destruction = true;
  90. se.release();
  91. }
  92. void release() noexcept
  93. {
  94. m_execute_on_destruction = false;
  95. }
  96. const R& get() const noexcept
  97. {
  98. return m_resource.get();
  99. }
  100. template<class RR = R, std::enable_if_t<std::is_pointer_v<RR>, int> = 0>
  101. RR operator->() const noexcept
  102. {
  103. return m_resource.get();
  104. }
  105. template<class RR = R,
  106. std::enable_if_t<( std::is_pointer_v<RR>
  107. && !std::is_void_v<std::remove_pointer_t<RR>>), int> = 0>
  108. std::add_lvalue_reference_t<std::remove_pointer_t<RR>> operator*() const noexcept
  109. {
  110. return *get();
  111. }
  112. const D& get_deleter() const noexcept
  113. {
  114. return m_deleter.get();
  115. }
  116. template<class RR = R, class DD = D,
  117. std::enable_if_t<(std::is_nothrow_move_assignable_v<RR>
  118. || std::is_nothrow_copy_assignable_v<RR>)
  119. && (std::is_nothrow_move_assignable_v<DD>
  120. || std::is_nothrow_copy_assignable_v<DD>), int> = 0
  121. >
  122. unique_resource& operator=(unique_resource&& other) noexcept(std::is_nothrow_assignable_v<R&, R>
  123. && std::is_nothrow_assignable_v<D&, D>)
  124. {
  125. if( this != &other )
  126. {
  127. reset();
  128. if constexpr( std::is_nothrow_move_assignable_v<RR> == true )
  129. {
  130. if constexpr( std::is_nothrow_move_assignable_v<DD> == true )
  131. {
  132. m_resource.reset(std::move(other.m_resource));
  133. m_deleter.reset(std::move(other.m_deleter));
  134. }
  135. else
  136. {
  137. m_deleter.reset(other.m_deleter);
  138. m_resource.reset(std::move(other.m_resource));
  139. }
  140. }
  141. else
  142. {
  143. if constexpr( std::is_nothrow_move_assignable_v<DD> == true )
  144. {
  145. m_resource.reset(other.m_resource);
  146. m_deleter.reset(std::move(other.m_deleter));
  147. }
  148. else
  149. {
  150. m_resource.reset(other.m_resource);
  151. m_deleter.reset(other.m_deleter);
  152. }
  153. }
  154. m_execute_on_destruction = std::exchange(other.m_execute_on_destruction, false);
  155. }
  156. return *this;
  157. }
  158. unique_resource& operator=(const unique_resource&) = delete;
  159. private:
  160. detail::Wrapper<R> m_resource;
  161. detail::Wrapper<D> m_deleter;
  162. bool m_execute_on_destruction;
  163. };
  164. template<class R, class D>
  165. unique_resource(R, D) -> unique_resource<R, D>;
  166. template<class R, class D, class S = std::decay_t<R>>
  167. unique_resource<std::decay_t<R>, std::decay_t<D>> make_unique_resource_checked(R&& r, const S& invalid, D&& d)
  168. noexcept(std::is_nothrow_constructible_v<std::decay_t<R>, R>
  169. && std::is_nothrow_constructible_v<std::decay_t<D>, D>)
  170. {
  171. const bool must_release{r == invalid};
  172. unique_resource<std::decay_t<R>, std::decay_t<D>> ur{std::forward<R>(r), std::forward<D>(d)};
  173. if( must_release == true )
  174. {
  175. ur.release();
  176. }
  177. return ur;
  178. }
  179. }