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.

369 lines
14 KiB

  1. // _____ _____ _ _____
  2. // / ____| / ____| | | / ____|_ _
  3. // | (___ ___ ___ _ __ ___ | | __ _ _ __ _ _ __ __| | | | _| |_ _| |_
  4. // \___ \ / __/ _ \| '_ \ / _ \ | | |_ | | | |/ _` | '__/ _` | | | |_ _|_ _|
  5. // ____) | (_| (_) | |_) | __/ | |__| | |_| | (_| | | | (_| | | |____|_| |_|
  6. // |_____/ \___\___/| .__/ \___| \_____|\__,_|\__,_|_| \__,_| \_____|
  7. // | | https://github.com/Neargye/scope_guard
  8. // |_| version 0.9.1
  9. //
  10. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
  11. // SPDX-License-Identifier: MIT
  12. // Copyright (c) 2018 - 2021 Daniil Goncharov <neargye@gmail.com>.
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining a copy
  15. // of this software and associated documentation files (the "Software"), to deal
  16. // in the Software without restriction, including without limitation the rights
  17. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18. // copies of the Software, and to permit persons to whom the Software is
  19. // furnished to do so, subject to the following conditions:
  20. //
  21. // The above copyright notice and this permission notice shall be included in all
  22. // copies or substantial portions of the Software.
  23. //
  24. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30. // SOFTWARE.
  31. #ifndef NEARGYE_SCOPE_GUARD_HPP
  32. #define NEARGYE_SCOPE_GUARD_HPP
  33. #define SCOPE_GUARD_VERSION_MAJOR 0
  34. #define SCOPE_GUARD_VERSION_MINOR 9
  35. #define SCOPE_GUARD_VERSION_PATCH 1
  36. #include <type_traits>
  37. #if (defined(_MSC_VER) && _MSC_VER >= 1900) || ((defined(__clang__) || defined(__GNUC__)) && __cplusplus >= 201700L)
  38. #include <exception>
  39. #endif
  40. // scope_guard throwable settings:
  41. // SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE requires nothrow constructible action.
  42. // SCOPE_GUARD_MAY_THROW_ACTION action may throw exceptions.
  43. // SCOPE_GUARD_NO_THROW_ACTION requires noexcept action.
  44. // SCOPE_GUARD_SUPPRESS_THROW_ACTION exceptions during action will be suppressed.
  45. // SCOPE_GUARD_CATCH_HANDLER exceptions handler. If SCOPE_GUARD_SUPPRESS_THROW_ACTIONS is not defined, it will do nothing.
  46. #if !defined(SCOPE_GUARD_MAY_THROW_ACTION) && !defined(SCOPE_GUARD_NO_THROW_ACTION) && !defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)
  47. # define SCOPE_GUARD_MAY_THROW_ACTION
  48. #elif (defined(SCOPE_GUARD_MAY_THROW_ACTION) + defined(SCOPE_GUARD_NO_THROW_ACTION) + defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)) > 1
  49. # error Only one of SCOPE_GUARD_MAY_THROW_ACTION and SCOPE_GUARD_NO_THROW_ACTION and SCOPE_GUARD_SUPPRESS_THROW_ACTION may be defined.
  50. #endif
  51. #if !defined(SCOPE_GUARD_CATCH_HANDLER)
  52. # define SCOPE_GUARD_CATCH_HANDLER /* Suppress exception.*/
  53. #endif
  54. namespace scope_guard {
  55. namespace detail {
  56. #if defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS))
  57. # define NEARGYE_NOEXCEPT(...) noexcept
  58. # define NEARGYE_TRY try {
  59. # define NEARGYE_CATCH } catch (...) { SCOPE_GUARD_CATCH_HANDLER }
  60. #else
  61. # define NEARGYE_NOEXCEPT(...) noexcept(__VA_ARGS__)
  62. # define NEARGYE_TRY
  63. # define NEARGYE_CATCH
  64. #endif
  65. #define NEARGYE_MOV(...) static_cast<typename std::remove_reference<decltype(__VA_ARGS__)>::type&&>(__VA_ARGS__)
  66. #define NEARGYE_FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
  67. // NEARGYE_NODISCARD encourages the compiler to issue a warning if the return value is discarded.
  68. #if !defined(NEARGYE_NODISCARD)
  69. # if defined(__clang__)
  70. # if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L
  71. # define NEARGYE_NODISCARD [[nodiscard]]
  72. # else
  73. # define NEARGYE_NODISCARD __attribute__((__warn_unused_result__))
  74. # endif
  75. # elif defined(__GNUC__)
  76. # if __GNUC__ >= 7 && __cplusplus >= 201703L
  77. # define NEARGYE_NODISCARD [[nodiscard]]
  78. # else
  79. # define NEARGYE_NODISCARD __attribute__((__warn_unused_result__))
  80. # endif
  81. # elif defined(_MSC_VER)
  82. # if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
  83. # define NEARGYE_NODISCARD [[nodiscard]]
  84. # elif defined(_Check_return_)
  85. # define NEARGYE_NODISCARD _Check_return_
  86. # else
  87. # define NEARGYE_NODISCARD
  88. # endif
  89. # else
  90. # define NEARGYE_NODISCARD
  91. # endif
  92. #endif
  93. #if defined(_MSC_VER) && _MSC_VER < 1900
  94. inline int uncaught_exceptions() noexcept {
  95. return *(reinterpret_cast<int*>(static_cast<char*>(static_cast<void*>(_getptd())) + (sizeof(void*) == 8 ? 0x100 : 0x90)));
  96. }
  97. #elif (defined(__clang__) || defined(__GNUC__)) && __cplusplus < 201700L
  98. struct __cxa_eh_globals;
  99. extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
  100. inline int uncaught_exceptions() noexcept {
  101. return static_cast<int>(*(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxa_get_globals())) + sizeof(void*))));
  102. }
  103. #else
  104. inline int uncaught_exceptions() noexcept {
  105. return std::uncaught_exceptions();
  106. }
  107. #endif
  108. class on_exit_policy {
  109. bool execute_;
  110. public:
  111. explicit on_exit_policy(bool execute) noexcept : execute_{execute} {}
  112. void dismiss() noexcept {
  113. execute_ = false;
  114. }
  115. bool should_execute() const noexcept {
  116. return execute_;
  117. }
  118. };
  119. class on_fail_policy {
  120. int ec_;
  121. public:
  122. explicit on_fail_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {}
  123. void dismiss() noexcept {
  124. ec_ = -1;
  125. }
  126. bool should_execute() const noexcept {
  127. return ec_ != -1 && ec_ < uncaught_exceptions();
  128. }
  129. };
  130. class on_success_policy {
  131. int ec_;
  132. public:
  133. explicit on_success_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {}
  134. void dismiss() noexcept {
  135. ec_ = -1;
  136. }
  137. bool should_execute() const noexcept {
  138. return ec_ != -1 && ec_ >= uncaught_exceptions();
  139. }
  140. };
  141. template <typename T, typename = void>
  142. struct is_noarg_returns_void_action
  143. : std::false_type {};
  144. template <typename T>
  145. struct is_noarg_returns_void_action<T, decltype((std::declval<T>())())>
  146. : std::true_type {};
  147. template <typename T, bool = is_noarg_returns_void_action<T>::value>
  148. struct is_nothrow_invocable_action
  149. : std::false_type {};
  150. template <typename T>
  151. struct is_nothrow_invocable_action<T, true>
  152. : std::integral_constant<bool, noexcept((std::declval<T>())())> {};
  153. template <typename F, typename P>
  154. class scope_guard {
  155. using A = typename std::decay<F>::type;
  156. static_assert(is_noarg_returns_void_action<A>::value,
  157. "scope_guard requires no-argument action, that returns void.");
  158. static_assert(std::is_same<P, on_exit_policy>::value || std::is_same<P, on_fail_policy>::value || std::is_same<P, on_success_policy>::value,
  159. "scope_guard requires on_exit_policy, on_fail_policy or on_success_policy.");
  160. #if defined(SCOPE_GUARD_NO_THROW_ACTION)
  161. static_assert(is_nothrow_invocable_action<A>::value,
  162. "scope_guard requires noexcept invocable action.");
  163. #endif
  164. #if defined(SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE)
  165. static_assert(std::is_nothrow_move_constructible<A>::value,
  166. "scope_guard requires nothrow constructible action.");
  167. #endif
  168. P policy_;
  169. A action_;
  170. void* operator new(std::size_t) = delete;
  171. void operator delete(void*) = delete;
  172. public:
  173. scope_guard() = delete;
  174. scope_guard(const scope_guard&) = delete;
  175. scope_guard& operator=(const scope_guard&) = delete;
  176. scope_guard& operator=(scope_guard&&) = delete;
  177. scope_guard(scope_guard&& other) noexcept(std::is_nothrow_move_constructible<A>::value)
  178. : policy_{false},
  179. action_{NEARGYE_MOV(other.action_)} {
  180. policy_ = NEARGYE_MOV(other.policy_);
  181. other.policy_.dismiss();
  182. }
  183. scope_guard(const A& action) = delete;
  184. scope_guard(A& action) = delete;
  185. explicit scope_guard(A&& action) noexcept(std::is_nothrow_move_constructible<A>::value)
  186. : policy_{true},
  187. action_{NEARGYE_MOV(action)} {}
  188. void dismiss() noexcept {
  189. policy_.dismiss();
  190. }
  191. ~scope_guard() NEARGYE_NOEXCEPT(is_nothrow_invocable_action<A>::value) {
  192. if (policy_.should_execute()) {
  193. NEARGYE_TRY
  194. action_();
  195. NEARGYE_CATCH
  196. }
  197. }
  198. };
  199. template <typename F>
  200. using scope_exit = scope_guard<F, on_exit_policy>;
  201. template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
  202. NEARGYE_NODISCARD scope_exit<F> make_scope_exit(F&& action) noexcept(noexcept(scope_exit<F>{NEARGYE_FWD(action)})) {
  203. return scope_exit<F>{NEARGYE_FWD(action)};
  204. }
  205. template <typename F>
  206. using scope_fail = scope_guard<F, on_fail_policy>;
  207. template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
  208. NEARGYE_NODISCARD scope_fail<F> make_scope_fail(F&& action) noexcept(noexcept(scope_fail<F>{NEARGYE_FWD(action)})) {
  209. return scope_fail<F>{NEARGYE_FWD(action)};
  210. }
  211. template <typename F>
  212. using scope_success = scope_guard<F, on_success_policy>;
  213. template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
  214. NEARGYE_NODISCARD scope_success<F> make_scope_success(F&& action) noexcept(noexcept(scope_success<F>{NEARGYE_FWD(action)})) {
  215. return scope_success<F>{NEARGYE_FWD(action)};
  216. }
  217. struct scope_exit_tag {};
  218. template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
  219. scope_exit<F> operator<<(scope_exit_tag, F&& action) noexcept(noexcept(scope_exit<F>{NEARGYE_FWD(action)})) {
  220. return scope_exit<F>{NEARGYE_FWD(action)};
  221. }
  222. struct scope_fail_tag {};
  223. template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
  224. scope_fail<F> operator<<(scope_fail_tag, F&& action) noexcept(noexcept(scope_fail<F>{NEARGYE_FWD(action)})) {
  225. return scope_fail<F>{NEARGYE_FWD(action)};
  226. }
  227. struct scope_success_tag {};
  228. template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
  229. scope_success<F> operator<<(scope_success_tag, F&& action) noexcept(noexcept(scope_success<F>{NEARGYE_FWD(action)})) {
  230. return scope_success<F>{NEARGYE_FWD(action)};
  231. }
  232. #undef NEARGYE_MOV
  233. #undef NEARGYE_FWD
  234. #undef NEARGYE_NOEXCEPT
  235. #undef NEARGYE_TRY
  236. #undef NEARGYE_CATCH
  237. #undef NEARGYE_NODISCARD
  238. } // namespace scope_guard::detail
  239. using detail::make_scope_exit;
  240. using detail::make_scope_fail;
  241. using detail::make_scope_success;
  242. } // namespace scope_guard
  243. // NEARGYE_MAYBE_UNUSED suppresses compiler warnings on unused entities, if any.
  244. #if !defined(NEARGYE_MAYBE_UNUSED)
  245. # if defined(__clang__)
  246. # if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L
  247. # define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
  248. # else
  249. # define NEARGYE_MAYBE_UNUSED __attribute__((__unused__))
  250. # endif
  251. # elif defined(__GNUC__)
  252. # if __GNUC__ >= 7 && __cplusplus >= 201703L
  253. # define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
  254. # else
  255. # define NEARGYE_MAYBE_UNUSED __attribute__((__unused__))
  256. # endif
  257. # elif defined(_MSC_VER)
  258. # if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
  259. # define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
  260. # else
  261. # define NEARGYE_MAYBE_UNUSED __pragma(warning(suppress : 4100 4101 4189))
  262. # endif
  263. # else
  264. # define NEARGYE_MAYBE_UNUSED
  265. # endif
  266. #endif
  267. #if !defined(NEARGYE_STR_CONCAT)
  268. # define NEARGYE_STR_CONCAT_(s1, s2) s1##s2
  269. # define NEARGYE_STR_CONCAT(s1, s2) NEARGYE_STR_CONCAT_(s1, s2)
  270. #endif
  271. #if !defined(NEARGYE_COUNTER)
  272. # if defined(__COUNTER__)
  273. # define NEARGYE_COUNTER __COUNTER__
  274. # elif defined(__LINE__)
  275. # define NEARGYE_COUNTER __LINE__
  276. # endif
  277. #endif
  278. #if defined(SCOPE_GUARD_NO_THROW_ACTION)
  279. # define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() noexcept -> void
  280. #else
  281. # define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() -> void
  282. #endif
  283. #define NEARGYE_MAKE_SCOPE_EXIT ::scope_guard::detail::scope_exit_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
  284. #define NEARGYE_MAKE_SCOPE_FAIL ::scope_guard::detail::scope_fail_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
  285. #define NEARGYE_MAKE_SCOPE_SUCCESS ::scope_guard::detail::scope_success_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
  286. #define NEARGYE_SCOPE_GUARD_WITH_(g, i) for (int i = 1; i--; g)
  287. #define NEARGYE_SCOPE_GUARD_WITH(g) NEARGYE_SCOPE_GUARD_WITH_(g, NEARGYE_STR_CONCAT(NEARGYE_INTERNAL_OBJECT_, NEARGYE_COUNTER))
  288. // SCOPE_EXIT executing action on scope exit.
  289. #define MAKE_SCOPE_EXIT(name) auto name = NEARGYE_MAKE_SCOPE_EXIT
  290. #define SCOPE_EXIT NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_EXIT(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_EXIT_, NEARGYE_COUNTER))
  291. #define WITH_SCOPE_EXIT(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_EXIT{ guard })
  292. // SCOPE_FAIL executing action on scope exit when an exception has been thrown before scope exit.
  293. #define MAKE_SCOPE_FAIL(name) auto name = NEARGYE_MAKE_SCOPE_FAIL
  294. #define SCOPE_FAIL NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_FAIL(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_FAIL_, NEARGYE_COUNTER))
  295. #define WITH_SCOPE_FAIL(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_FAIL{ guard })
  296. // SCOPE_SUCCESS executing action on scope exit when no exceptions have been thrown before scope exit.
  297. #define MAKE_SCOPE_SUCCESS(name) auto name = NEARGYE_MAKE_SCOPE_SUCCESS
  298. #define SCOPE_SUCCESS NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_SUCCESS(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_SUCCESS_, NEARGYE_COUNTER))
  299. #define WITH_SCOPE_SUCCESS(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_SUCCESS{ guard })
  300. // DEFER executing action on scope exit.
  301. #define MAKE_DEFER(name) MAKE_SCOPE_EXIT(name)
  302. #define DEFER SCOPE_EXIT
  303. #define WITH_DEFER(guard) WITH_SCOPE_EXIT(guard)
  304. #endif // NEARGYE_SCOPE_GUARD_HPP