// -*- C++ -*- // Copyright The GNU Toolchain Authors. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . /** @file experimental/scope * This is a TS C++ Library header. * @ingroup libfund-ts */ #ifndef _GLIBCXX_EXPERIMENTAL_SCOPE #define _GLIBCXX_EXPERIMENTAL_SCOPE 1 #pragma GCC system_header #include // experimental is currently omitted #if __cplusplus >= 202002L #include #include // uncaught_exceptions #include namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION namespace experimental::inline fundamentals_v3 { #define __cpp_lib_experimental_scope 201902 template concept __not_same_as = !same_as<_Tp, _Up>; template concept __not_lvalue_ref = !is_lvalue_reference_v<_Tp>; template class [[nodiscard]] scope_exit { public: template requires __not_same_as, scope_exit> && constructible_from<_Ef, _Efp> [[nodiscard]] explicit scope_exit(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>) #ifdef __cpp_exceptions try #endif : _M_exit_function(__f) { } #ifdef __cpp_exceptions catch (...) { __f(); } #endif template requires __not_same_as, scope_exit> && constructible_from<_Ef, _Efp> && __not_lvalue_ref<_Efp> && is_nothrow_constructible_v<_Ef, _Efp> explicit scope_exit(_Efp&& __f) noexcept : _M_exit_function(std::forward<_Efp>(__f)) { } scope_exit(scope_exit&& __rhs) noexcept requires is_nothrow_move_constructible_v<_Ef> : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function)) { __rhs.release(); } scope_exit(scope_exit&& __rhs) noexcept(is_nothrow_copy_constructible_v<_Ef>) requires (!is_nothrow_move_constructible_v<_Ef>) && is_copy_constructible_v<_Ef> : _M_exit_function(__rhs._M_exit_function) { __rhs.release(); } scope_exit(const scope_exit&) = delete; scope_exit& operator=(const scope_exit&) = delete; scope_exit& operator=(scope_exit&&) = delete; ~scope_exit() noexcept { if (_M_execute_on_destruction) _M_exit_function(); } void release() noexcept { _M_execute_on_destruction = false; } private: [[no_unique_address]] _Ef _M_exit_function; bool _M_execute_on_destruction = true; }; template scope_exit(_Ef) -> scope_exit<_Ef>; template class [[nodiscard]] scope_fail { public: template requires __not_same_as, scope_fail> && constructible_from<_Ef, _Efp> explicit scope_fail(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>) #ifdef __cpp_exceptions try #endif : _M_exit_function(__f) { } #ifdef __cpp_exceptions catch (...) { __f(); } #endif template requires __not_same_as, scope_fail> && constructible_from<_Ef, _Efp> && __not_lvalue_ref<_Efp> && is_nothrow_constructible_v<_Ef, _Efp> explicit scope_fail(_Efp&& __f) noexcept : _M_exit_function(std::forward<_Efp>(__f)) { } scope_fail(scope_fail&& __rhs) noexcept requires is_nothrow_move_constructible_v<_Ef> : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function)) { __rhs.release(); } scope_fail(scope_fail&& __rhs) noexcept(is_nothrow_copy_constructible_v<_Ef>) requires (!is_nothrow_move_constructible_v<_Ef>) && is_copy_constructible_v<_Ef> : _M_exit_function(__rhs._M_exit_function) { __rhs.release(); } scope_fail(const scope_fail&) = delete; scope_fail& operator=(const scope_fail&) = delete; scope_fail& operator=(scope_fail&&) = delete; ~scope_fail() noexcept { if (std::uncaught_exceptions() > _M_uncaught_init) _M_exit_function(); } void release() noexcept { _M_uncaught_init = __INT_MAX__; } private: [[no_unique_address]] _Ef _M_exit_function; int _M_uncaught_init = std::uncaught_exceptions(); }; template scope_fail(_Ef) -> scope_fail<_Ef>; template class [[nodiscard]] scope_success { public: template requires __not_same_as, scope_success> && constructible_from<_Ef, _Efp> explicit scope_success(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>) : _M_exit_function(__f) { } template requires __not_same_as, scope_success> && constructible_from<_Ef, _Efp> && __not_lvalue_ref<_Efp> && is_nothrow_constructible_v<_Ef, _Efp> explicit scope_success(_Efp&& __f) noexcept : _M_exit_function(std::forward<_Efp>(__f)) { } scope_success(scope_success&& __rhs) noexcept requires is_nothrow_move_constructible_v<_Ef> : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function)) { __rhs.release(); } scope_success(scope_success&& __rhs) noexcept(is_nothrow_copy_constructible_v<_Ef>) requires (!is_nothrow_move_constructible_v<_Ef>) && is_copy_constructible_v<_Ef> : _M_exit_function(__rhs._M_exit_function) { __rhs.release(); } scope_success(const scope_success&) = delete; scope_success& operator=(const scope_success&) = delete; scope_success& operator=(scope_success&&) = delete; ~scope_success() noexcept(noexcept(this->_M_exit_function())) { if (std::uncaught_exceptions() <= _M_uncaught_init) _M_exit_function(); } void release() noexcept { _M_uncaught_init = -__INT_MAX__; } private: [[no_unique_address]] _Ef _M_exit_function; int _M_uncaught_init = std::uncaught_exceptions(); }; template scope_success(_Ef) -> scope_success<_Ef>; template class [[nodiscard]] unique_resource { static_assert(!is_rvalue_reference_v<_Resrc>); static_assert(!is_reference_v<_Del>); struct _Dummy { constexpr void release() { } }; template struct _Wrap { template requires is_constructible_v<_Tp, _Up> _Wrap(_Up&&) noexcept(is_nothrow_constructible_v<_Tp, _Up>); template requires is_constructible_v<_Tp, _Up> _Wrap(_Up&& __r, _Del2&& __d) noexcept(is_nothrow_constructible_v<_Tp, _Up>) : _M_t(std::forward<_Up>(__r)) { __d.release(); } _Wrap() = default; _Wrap(_Wrap&&) = default; _Wrap(_Wrap&& __rhs) noexcept(is_nothrow_constructible_v<_Tp, _Tp&>) requires (!is_nothrow_move_constructible_v<_Tp>) : _M_t(__rhs._M_t) { } _Wrap& operator=(const _Wrap&) = default; _Wrap& operator=(_Wrap&&) = default; constexpr _Tp& get() noexcept { return _M_t; } constexpr const _Tp& get() const noexcept { return _M_t; } [[no_unique_address]] _Tp _M_t{}; }; template struct _Wrap<_Tp&> { template requires is_constructible_v, _Up> _Wrap(_Up&&) noexcept(is_nothrow_constructible_v, _Up>); template _Wrap(_Up&& __r, _Del2&& __d) noexcept(is_nothrow_constructible_v, _Up>) : _M_p(__builtin_addressof(static_cast<_Tp&>(__r))) { __d.release(); } _Wrap() = delete; _Wrap(const _Wrap&) = default; _Wrap& operator=(const _Wrap&) = default; _Tp& get() noexcept { return *_M_p; } const _Tp& get() const noexcept { return *_M_p; } _Tp* _M_p = nullptr; }; using _Res1 = _Wrap<_Resrc>; template requires is_constructible_v<_Tp, _Up> && (is_nothrow_constructible_v<_Tp, _Up> || is_constructible_v<_Tp, _Up&>) using _Fwd_t = __conditional_t, _Up, _Up&>; template static constexpr _Fwd_t<_Tp, _Up> _S_fwd(_Up& __u) { return static_cast<_Fwd_t<_Tp, _Up>&&>(__u); } template static constexpr auto _S_guard(_Del2& __d, _Res2& __r) { if constexpr (is_nothrow_constructible_v<_Tp, _Up>) return _Dummy{}; else return scope_fail{[&] { __d(__r); }}; } public: unique_resource() = default; template requires requires { typename _Fwd_t<_Res1, _Res2>; typename _Fwd_t<_Del, _Del2>; } unique_resource(_Res2&& __r, _Del2&& __d) noexcept((is_nothrow_constructible_v<_Res1, _Res2> || is_nothrow_constructible_v<_Res1, _Res2&>) && (is_nothrow_constructible_v<_Del, _Del2> || is_nothrow_constructible_v<_Del, _Del2&>)) : _M_res(_S_fwd<_Res1, _Res2>(__r), _S_guard<_Res1, _Res2>(__d, __r)), _M_del(_S_fwd<_Del, _Del2>(__d), _S_guard<_Del, _Del2>(__d, _M_res.get())), _M_exec_on_reset(true) { } unique_resource(unique_resource&& __rhs) noexcept requires is_nothrow_move_constructible_v<_Res1> && is_nothrow_move_constructible_v<_Del> : _M_res(std::move(__rhs._M_res)), _M_del(std::move(__rhs._M_del)), _M_exec_on_reset(std::__exchange(__rhs._M_exec_on_reset, false)) { } unique_resource(unique_resource&& __rhs) requires is_nothrow_move_constructible_v<_Res1> && (!is_nothrow_move_constructible_v<_Del>) : _M_res(std::move(__rhs._M_res)), _M_del(_S_fwd<_Del, _Del>(__rhs._M_del.get()), scope_fail([&]{ if (__rhs._M_exec_on_reset) { __rhs._M_del.get()(_M_res.get()); __rhs.release(); } })), _M_exec_on_reset(std::__exchange(__rhs._M_exec_on_reset, false)) { } unique_resource(unique_resource&& __rhs) requires (!is_nothrow_move_constructible_v<_Res1>) : unique_resource(__rhs._M_res.get(), __rhs._M_del.get(), _Dummy{}) { if (__rhs._M_exec_on_reset) { _M_exec_on_reset = true; __rhs._M_exec_on_reset = false; } } // 3.3.3.3, Destructor ~unique_resource() { reset(); } // 3.3.3.4, Assignment unique_resource& operator=(unique_resource&& __rhs) noexcept(is_nothrow_move_assignable_v<_Res1> && is_nothrow_move_assignable_v<_Del>) { reset(); if constexpr (is_nothrow_move_assignable_v<_Res1>) { if constexpr (is_nothrow_move_assignable_v<_Del>) { _M_res = std::move(__rhs._M_res); _M_del = std::move(__rhs._M_del); } else { _M_del = __rhs._M_del; _M_res = std::move(__rhs._M_res); } } else { if constexpr (is_nothrow_move_assignable_v<_Del>) { _M_res = __rhs._M_res; _M_del = std::move(__rhs._M_del); } else { _M_res = __rhs._M_res; _M_del = __rhs._M_del; } } _M_exec_on_reset = std::__exchange(__rhs._M_exec_on_reset, false); return *this; } // 3.3.3.5, Other member functions void reset() noexcept { if (_M_exec_on_reset) { _M_exec_on_reset = false; _M_del.get()(_M_res.get()); } } template void reset(_Res2&& __r) { reset(); if constexpr (is_nothrow_assignable_v<_Res1&, _Res2>) _M_res.get() = std::forward<_Res2>(__r); else _M_res.get() = const_cast&>(__r); _M_exec_on_reset = true; } void release() noexcept { _M_exec_on_reset = false; } const _Resrc& get() const noexcept { return _M_res.get(); } add_lvalue_reference_t> operator*() const noexcept requires is_pointer_v<_Resrc> && (!is_void_v>) { return *get(); } _Resrc operator->() const noexcept requires is_pointer_v<_Resrc> { return _M_res.get(); } const _Del& get_deleter() const noexcept { return _M_del.get(); } private: [[no_unique_address]] _Res1 _M_res{}; [[no_unique_address]] _Wrap<_Del> _M_del{}; bool _M_exec_on_reset = false; template friend unique_resource, decay_t<_Del2>> make_unique_resource_checked(_Res2&&, const _St&, _Del2&&) noexcept(is_nothrow_constructible_v, _Res2> && is_nothrow_constructible_v, _Del2>); template unique_resource(_Res2&& __r, _Del2&& __d, _Dummy __noop) noexcept(is_nothrow_constructible_v<_Resrc, _Res2> && is_nothrow_constructible_v<_Del, _Del2>) : _M_res(std::forward<_Res2>(__r), __noop), _M_del(std::forward<_Del>(__d), __noop) { } }; template unique_resource(_Resrc, _Del) -> unique_resource<_Resrc, _Del>; template> unique_resource, decay_t<_Del>> make_unique_resource_checked(_Resrc&& __r, const _St& __invalid, _Del&& __d) noexcept(is_nothrow_constructible_v, _Resrc> && is_nothrow_constructible_v, _Del>) { if (__r == __invalid) return { std::forward<_Resrc>(__r), std::forward<_Del>(__d), {} }; return { std::forward<_Resrc>(__r), std::forward<_Del>(__d) }; } } // namespace experimental::fundamentals_v3 _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // C++20 #endif // _GLIBCXX_EXPERIMENTAL_SCOPE