You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

unique_resource.h 8.2KB

5 years ago
7 years ago
7 years ago
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // MIT License
  2. //
  3. // Copyright (c) 2017-2019 offa
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in all
  13. // copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. // SOFTWARE.
  22. #pragma once
  23. #include "scope_exit.h"
  24. #include "detail/wrapper.h"
  25. #include <utility>
  26. #include <type_traits>
  27. namespace sr
  28. {
  29. namespace detail
  30. {
  31. template<class T, class U,
  32. class R = std::conditional_t<std::is_nothrow_constructible_v<T, U>, U&&, U>
  33. >
  34. constexpr R forward_if_nothrow_constructible(U&& arg)
  35. {
  36. return std::forward<U>(arg);
  37. }
  38. }
  39. template<class R, class D>
  40. class unique_resource
  41. {
  42. public:
  43. template<class RR, class DD,
  44. std::enable_if_t<(std::is_constructible_v<R ,RR> && std::is_constructible_v<D, DD>
  45. && (std::is_nothrow_constructible_v<R, RR> || std::is_constructible_v<R, RR&>)
  46. && (std::is_nothrow_constructible_v<D, DD> || std::is_constructible_v<D, DD&>)), int> = 0
  47. >
  48. unique_resource(RR&& r, DD&& d) noexcept((std::is_nothrow_constructible_v<R, RR> || std::is_nothrow_constructible_v<R, RR&>)
  49. && (std::is_nothrow_constructible_v<D, DD> || std::is_nothrow_constructible_v<D, DD&>))
  50. : resource(detail::forward_if_nothrow_constructible<R, RR>(std::forward<RR>(r)), scope_exit{[&r, &d] { d(r); }}),
  51. deleter(detail::forward_if_nothrow_constructible<D, DD>(std::forward<DD>(d)), scope_exit{[this, &d] { d(get()); }}),
  52. execute_on_reset(true)
  53. {
  54. }
  55. unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible_v<R>
  56. && std::is_nothrow_move_constructible_v<D>)
  57. : resource(std::move_if_noexcept(other.resource.get()), scope_exit{[] { }}),
  58. deleter(std::move_if_noexcept(other.deleter.get()), scope_exit{[&other] {
  59. if( other.execute_on_reset == true )
  60. {
  61. other.get_deleter()(other.resource.get());
  62. }
  63. other.release(); }}),
  64. execute_on_reset(std::exchange(other.execute_on_reset, false))
  65. {
  66. }
  67. unique_resource(const unique_resource&) = delete;
  68. ~unique_resource()
  69. {
  70. reset();
  71. }
  72. void reset() noexcept
  73. {
  74. if( execute_on_reset == true )
  75. {
  76. execute_on_reset = false;
  77. get_deleter()(resource.get());
  78. }
  79. }
  80. template<class RR>
  81. void reset(RR&& r)
  82. {
  83. reset();
  84. using R1 = typename detail::Wrapper<R>::type;
  85. auto se = scope_exit{[this, &r] { get_deleter()(r); }};
  86. if constexpr( std::is_nothrow_assignable_v<R1&, RR> == true )
  87. {
  88. resource.reset(std::forward<RR>(r));
  89. }
  90. else
  91. {
  92. resource.reset(std::as_const(r));
  93. }
  94. execute_on_reset = true;
  95. se.release();
  96. }
  97. void release() noexcept
  98. {
  99. execute_on_reset = false;
  100. }
  101. const R& get() const noexcept
  102. {
  103. return resource.get();
  104. }
  105. template<class RR = R, std::enable_if_t<std::is_pointer_v<RR>, int> = 0>
  106. RR operator->() const noexcept
  107. {
  108. return resource.get();
  109. }
  110. template<class RR = R,
  111. std::enable_if_t<( std::is_pointer_v<RR>
  112. && !std::is_void_v<std::remove_pointer_t<RR>>), int> = 0>
  113. std::add_lvalue_reference_t<std::remove_pointer_t<RR>> operator*() const noexcept
  114. {
  115. return *get();
  116. }
  117. const D& get_deleter() const noexcept
  118. {
  119. return deleter.get();
  120. }
  121. template<class RR = R, class DD = D,
  122. std::enable_if_t<(std::is_nothrow_move_assignable_v<RR>
  123. || std::is_nothrow_copy_assignable_v<RR>)
  124. && (std::is_nothrow_move_assignable_v<DD>
  125. || std::is_nothrow_copy_assignable_v<DD>), int> = 0
  126. >
  127. unique_resource& operator=(unique_resource&& other) noexcept(std::is_nothrow_assignable_v<R&, R>
  128. && std::is_nothrow_assignable_v<D&, D>)
  129. {
  130. if( this != &other )
  131. {
  132. reset();
  133. if constexpr( std::is_nothrow_move_assignable_v<RR> == true )
  134. {
  135. if constexpr( std::is_nothrow_move_assignable_v<DD> == true )
  136. {
  137. resource.reset(std::move(other.resource));
  138. deleter.reset(std::move(other.deleter));
  139. }
  140. else
  141. {
  142. deleter.reset(other.deleter);
  143. resource.reset(std::move(other.resource));
  144. }
  145. }
  146. else
  147. {
  148. if constexpr( std::is_nothrow_move_assignable_v<DD> == true )
  149. {
  150. resource.reset(other.resource);
  151. deleter.reset(std::move(other.deleter));
  152. }
  153. else
  154. {
  155. resource.reset(other.resource);
  156. deleter.reset(other.deleter);
  157. }
  158. }
  159. execute_on_reset = std::exchange(other.execute_on_reset, false);
  160. }
  161. return *this;
  162. }
  163. unique_resource& operator=(const unique_resource&) = delete;
  164. private:
  165. detail::Wrapper<R> resource;
  166. detail::Wrapper<D> deleter;
  167. bool execute_on_reset;
  168. };
  169. template<class R, class D>
  170. unique_resource(R, D) -> unique_resource<R, D>;
  171. template<class R, class D, class S = std::decay_t<R>>
  172. unique_resource<std::decay_t<R>, std::decay_t<D>> make_unique_resource_checked(R&& r, const S& invalid, D&& d)
  173. noexcept(std::is_nothrow_constructible_v<std::decay_t<R>, R>
  174. && std::is_nothrow_constructible_v<std::decay_t<D>, D>)
  175. {
  176. const bool must_release{r == invalid};
  177. unique_resource<std::decay_t<R>, std::decay_t<D>> ur{std::forward<R>(r), std::forward<D>(d)};
  178. if( must_release == true )
  179. {
  180. ur.release();
  181. }
  182. return ur;
  183. }
  184. }