// MIT License // // Copyright (c) 2017-2021 offa // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #pragma once #include "scope_exit.h" #include "detail/wrapper.h" #include #include namespace sr { namespace detail { template, U&&, U> > constexpr R forward_if_nothrow_constructible(U&& arg) { return std::forward(arg); } } template class unique_resource { public: unique_resource() : resource(R{}), deleter(D{}), execute_on_reset(false) { } template && std::is_constructible_v && (std::is_nothrow_constructible_v || std::is_constructible_v) && (std::is_nothrow_constructible_v || std::is_constructible_v)), int> = 0 > unique_resource(RR&& r, DD&& d) noexcept((std::is_nothrow_constructible_v || std::is_nothrow_constructible_v) && (std::is_nothrow_constructible_v || std::is_nothrow_constructible_v)) : resource(detail::forward_if_nothrow_constructible(std::forward(r)), scope_exit{[&r, &d] { d(r); }}), deleter(detail::forward_if_nothrow_constructible(std::forward
(d)), scope_exit{[this, &d] { d(get()); }}), execute_on_reset(true) { } unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) : resource(std::move_if_noexcept(other.resource.get())), deleter(std::move_if_noexcept(other.deleter.get()), scope_exit{[&other] { if( other.execute_on_reset == true ) { other.get_deleter()(other.resource.get()); } other.release(); }}), execute_on_reset(std::exchange(other.execute_on_reset, false)) { } unique_resource(const unique_resource&) = delete; ~unique_resource() { reset(); } void reset() noexcept { if( execute_on_reset == true ) { execute_on_reset = false; get_deleter()(resource.get()); } } template void reset(RR&& r) { reset(); using R1 = typename detail::Wrapper::type; auto se = scope_exit{[this, &r] { get_deleter()(r); }}; if constexpr( std::is_nothrow_assignable_v == true ) { resource.reset(std::forward(r)); } else { resource.reset(std::as_const(r)); } execute_on_reset = true; se.release(); } void release() noexcept { execute_on_reset = false; } const R& get() const noexcept { return resource.get(); } template, int> = 0> RR operator->() const noexcept { return resource.get(); } template && !std::is_void_v>), int> = 0> std::add_lvalue_reference_t> operator*() const noexcept { return *get(); } const D& get_deleter() const noexcept { return deleter.get(); } template || std::is_copy_assignable_v) && (std::is_nothrow_move_assignable_v
|| std::is_copy_assignable_v
), int> = 0> unique_resource& operator=(unique_resource&& other) noexcept(std::is_nothrow_assignable_v && std::is_nothrow_assignable_v) { if( this != &other ) { reset(); if constexpr( std::is_nothrow_move_assignable_v == true ) { if constexpr( std::is_nothrow_move_assignable_v
== true ) { resource.reset(std::move(other.resource)); deleter.reset(std::move(other.deleter)); } else { deleter.reset(other.deleter); resource.reset(std::move(other.resource)); } } else { if constexpr( std::is_nothrow_move_assignable_v
== true ) { resource.reset(other.resource); deleter.reset(std::move(other.deleter)); } else { resource.reset(other.resource); deleter.reset(other.deleter); } } execute_on_reset = std::exchange(other.execute_on_reset, false); } return *this; } unique_resource& operator=(const unique_resource&) = delete; private: detail::Wrapper resource; detail::Wrapper deleter; bool execute_on_reset; }; template unique_resource(R, D) -> unique_resource; template> unique_resource, std::decay_t> make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(std::is_nothrow_constructible_v, R> && std::is_nothrow_constructible_v, D>) { unique_resource, std::decay_t> ur{std::forward(r), std::forward(d)}; if( bool(r == invalid) ) { ur.release(); } return ur; } }