/* * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * https://www.boost.org/LICENSE_1_0.txt) * * Copyright (c) 2022-2024 Andrey Semashev */ /*! * \file scope/unique_resource.hpp * * This header contains definition of \c unique_resource template. */ #ifndef BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_ #define BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_ #include // for placement new #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BOOST_HAS_PRAGMA_ONCE #pragma once #endif namespace boost { namespace scope { #if !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS) /*! * \brief Simple resource traits for one or more unallocated resource values. * * This class template generates resource traits for `unique_resource` that specify * one or more unallocated resource values. The first value, specified in the \c DefaultValue * non-type template parameter, is considered the default. The other values, listed in * \c UnallocatedValues, are optional. Any resource values other than \c DefaultValue * or listed in \c UnallocatedValues are considered as allocated. * * In order for the generated resource traits to enable optimized implementation of * `unique_resource`, the resource type must support non-throwing construction and assignment * from, and comparison for (in)equality with \c DefaultValue or any of the resource * values listed in \c UnallocatedValues. */ template< auto DefaultValue, auto... UnallocatedValues > struct unallocated_resource { //! Returns the default resource value static decltype(DefaultValue) make_default() noexcept { return DefaultValue; } //! Tests if \a res is an allocated resource value template< typename Resource > static bool is_allocated(Resource const& res) noexcept { static_assert(noexcept(res != DefaultValue && (... && (res != UnallocatedValues))), "Invalid unallocated resource value types: comparing resource values with the unallocated values must be noexcept"); return res != DefaultValue && (... && (res != UnallocatedValues)); } }; #endif // !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS) struct default_resource_t { }; //! Keyword representing default, unallocated resource argument BOOST_INLINE_VARIABLE constexpr default_resource_t default_resource = { }; namespace detail { // The type trait indicates whether \c T is a possibly qualified \c default_resource_t type template< typename T > struct is_default_resource : public std::false_type { }; template< > struct is_default_resource< default_resource_t > : public std::true_type { }; template< > struct is_default_resource< const default_resource_t > : public std::true_type { }; template< > struct is_default_resource< volatile default_resource_t > : public std::true_type { }; template< > struct is_default_resource< const volatile default_resource_t > : public std::true_type { }; template< typename T > struct is_default_resource< T& > : public is_default_resource< T >::type { }; // Lightweight reference wrapper template< typename T > class ref_wrapper { private: T* m_value; public: explicit #if !defined(BOOST_CORE_NO_CONSTEXPR_ADDRESSOF) constexpr #endif ref_wrapper(T& value) noexcept : m_value(boost::addressof(value)) { } ref_wrapper& operator= (T& value) noexcept { m_value = boost::addressof(value); return *this; } ref_wrapper(T&&) = delete; ref_wrapper& operator= (T&&) = delete; operator T& () const noexcept { return *m_value; } template< typename... Args > void operator() (Args&&... args) const noexcept(detail::is_nothrow_invocable< T&, Args&&... >::value) { (*m_value)(static_cast< Args&& >(args)...); } }; template< typename T > struct wrap_reference { using type = T; }; template< typename T > struct wrap_reference< T& > { using type = ref_wrapper< T >; }; template< typename Resource, bool UseCompactStorage > class resource_holder : public detail::compact_storage< typename wrap_reference< Resource >::type > { public: using resource_type = Resource; using internal_resource_type = typename wrap_reference< resource_type >::type; private: using resource_base = detail::compact_storage< internal_resource_type >; public: template< bool Requires = std::is_default_constructible< internal_resource_type >::value, typename = typename std::enable_if< Requires >::type > constexpr resource_holder() noexcept(std::is_nothrow_default_constructible< internal_resource_type >::value) : resource_base() { } template< typename R, typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type > explicit resource_holder(R&& res) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : resource_base(static_cast< R&& >(res)) { } template< typename R, typename D, typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type > explicit resource_holder(R&& res, D&& del, bool allocated) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated, typename std::is_nothrow_constructible< resource_type, R >::type()) { } resource_type& get() noexcept { return resource_base::get(); } resource_type const& get() const noexcept { return resource_base::get(); } internal_resource_type& get_internal() noexcept { return resource_base::get(); } internal_resource_type const& get_internal() const noexcept { return resource_base::get(); } void move_from(internal_resource_type&& that) noexcept(std::is_nothrow_move_assignable< internal_resource_type >::value) { resource_base::get() = static_cast< internal_resource_type&& >(that); } private: template< typename R, typename D > explicit resource_holder(R&& res, D&& del, bool allocated, std::true_type) noexcept : resource_base(static_cast< R&& >(res)) { } template< typename R, typename D > explicit resource_holder(R&& res, D&& del, bool allocated, std::false_type) try : resource_base(res) { } catch (...) { if (allocated) del(res); } }; template< typename Resource > class resource_holder< Resource, false > { public: using resource_type = Resource; using internal_resource_type = typename wrap_reference< resource_type >::type; private: // Note: Not using compact_storage since we will need to reuse storage for this complete object in move_from internal_resource_type m_resource; public: template< bool Requires = std::is_default_constructible< internal_resource_type >::value, typename = typename std::enable_if< Requires >::type > constexpr resource_holder() noexcept(std::is_nothrow_default_constructible< internal_resource_type >::value) : m_resource() { } template< typename R, typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type > explicit resource_holder(R&& res) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : m_resource(static_cast< R&& >(res)) { } template< typename R, typename D, typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type > explicit resource_holder(R&& res, D&& del, bool allocated) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) : resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated, typename std::is_nothrow_constructible< resource_type, R >::type()) { } resource_type& get() noexcept { return m_resource; } resource_type const& get() const noexcept { return m_resource; } internal_resource_type& get_internal() noexcept { return m_resource; } internal_resource_type const& get_internal() const noexcept { return m_resource; } void move_from(internal_resource_type&& that) noexcept(std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::value) { internal_resource_type* p = boost::addressof(m_resource); p->~internal_resource_type(); new (p) internal_resource_type(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that)); } private: template< typename R, typename D > explicit resource_holder(R&& res, D&& del, bool allocated, std::true_type) noexcept : m_resource(static_cast< R&& >(res)) { } template< typename R, typename D > explicit resource_holder(R&& res, D&& del, bool allocated, std::false_type) try : m_resource(res) { } catch (...) { if (allocated) del(res); } }; template< typename Resource, typename Deleter > class deleter_holder : public detail::compact_storage< typename wrap_reference< Deleter >::type > { public: using resource_type = Resource; using deleter_type = Deleter; using internal_deleter_type = typename wrap_reference< deleter_type >::type; private: using deleter_base = detail::compact_storage< internal_deleter_type >; public: template< bool Requires = detail::is_nonnull_default_constructible< internal_deleter_type >::value, typename = typename std::enable_if< Requires >::type > constexpr deleter_holder() noexcept(detail::is_nothrow_nonnull_default_constructible< internal_deleter_type >::value) : deleter_base() { } template< typename D, typename = typename std::enable_if< std::is_constructible< internal_deleter_type, D >::value >::type > explicit deleter_holder(D&& del) noexcept(std::is_nothrow_constructible< internal_deleter_type, D >::value) : deleter_base(static_cast< D&& >(del)) { } template< typename D, typename = typename std::enable_if< std::is_constructible< internal_deleter_type, D >::value >::type > explicit deleter_holder(D&& del, resource_type& res, bool allocated) noexcept(std::is_nothrow_constructible< internal_deleter_type, D >::value) : deleter_holder(static_cast< D&& >(del), res, allocated, typename std::is_nothrow_constructible< internal_deleter_type, D >::type()) { } deleter_type& get() noexcept { return deleter_base::get(); } deleter_type const& get() const noexcept { return deleter_base::get(); } internal_deleter_type& get_internal() noexcept { return deleter_base::get(); } internal_deleter_type const& get_internal() const noexcept { return deleter_base::get(); } private: template< typename D > explicit deleter_holder(D&& del, resource_type& res, bool allocated, std::true_type) noexcept : deleter_base(static_cast< D&& >(del)) { } template< typename D > explicit deleter_holder(D&& del, resource_type& res, bool allocated, std::false_type) try : deleter_base(del) { } catch (...) { if (BOOST_LIKELY(allocated)) del(res); } }; /* * This metafunction indicates whether \c resource_holder should use \c compact_storage * to optimize storage for the resource object. Its definition must be coherent with * `resource_holder::move_from` definition and move constructor implementation in * \c unique_resource_data. * * There is one tricky case with \c unique_resource move constructor, when the resource move * constructor is noexcept and deleter's move and copy constructors are not. It is possible * that \c unique_resource_data move constructor moves the resource into the object being * constructed but fails to construct the deleter. In this case we want to move the resource * back to the original \c unique_resource_data object (which is guaranteed to not throw since * the resource's move constructor is non-throwing). * * However, if we use the move constructor to move the resource back, we need to use placement * new, and this only lets us create a complete object of the resource type, which prohibits * the use of \c compact_storage, as it may create the resource object as a base subobject of * \c compact_storage. Using placement new on a base subobject may corrupt data that is placed * in the trailing padding bits of the resource type. * * To work around this limitation, we also test if move assignment of the resource type is * also non-throwing (which is reasonable to expect, given that the move constructor is * non-throwing). If it is, we can avoid having to destroy and move-construct the resource and * use move-assignment instead. This doesn't require a complete object of the resource type * and allows us to use \c compact_storage. If move assignment is not noexcept then we have * to use the move constructor and disable the \c compact_storage optimization. * * So this trait has to detect (a) whether we are affected by this tricky case of the * \c unique_resource move constructor in the first place and (b) whether we can use move * assignment to move the resource back to the original \c unique_resource object. If we're * not affected or we can use move assignment then we enable \c compact_storage. */ template< typename Resource, typename Deleter > using use_resource_compact_storage = detail::disjunction< std::is_nothrow_move_assignable< typename wrap_reference< Resource >::type >, std::is_nothrow_constructible< typename wrap_reference< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter >::type >, detail::negation< std::is_nothrow_constructible< typename wrap_reference< Resource >::type, typename detail::move_or_copy_construct_ref< Resource >::type > > >; template< typename Resource, typename Deleter, typename Traits > class unique_resource_data : public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >, public detail::deleter_holder< Resource, Deleter > { public: using resource_type = Resource; using deleter_type = Deleter; using traits_type = Traits; private: using resource_holder = detail::resource_holder< resource_type, use_resource_compact_storage< resource_type, deleter_type >::value >; using deleter_holder = detail::deleter_holder< resource_type, deleter_type >; using result_of_make_default = decltype(traits_type::make_default()); public: using internal_resource_type = typename resource_holder::internal_resource_type; using internal_deleter_type = typename deleter_holder::internal_deleter_type; static_assert(noexcept(traits_type::make_default()), "Invalid unique_resource resource traits: make_default must be noexcept"); static_assert(std::is_nothrow_assignable< internal_resource_type&, result_of_make_default >::value, "Invalid unique_resource resource traits: resource must be nothrow-assignable from the result of make_default"); static_assert(noexcept(traits_type::is_allocated(std::declval< resource_type const& >())), "Invalid unique_resource resource traits: is_allocated must be noexcept"); public: template< bool Requires = detail::conjunction< std::is_constructible< resource_holder, result_of_make_default >, std::is_default_constructible< deleter_holder > >::value, typename = typename std::enable_if< Requires >::type > constexpr unique_resource_data() noexcept(detail::conjunction< std::is_nothrow_constructible< resource_holder, result_of_make_default >, std::is_nothrow_default_constructible< deleter_holder > >::value) : resource_holder(traits_type::make_default()), deleter_holder() { } unique_resource_data(unique_resource_data const&) = delete; unique_resource_data& operator= (unique_resource_data const&) = delete; unique_resource_data(unique_resource_data&& that) noexcept(detail::conjunction< std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >, std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type > >::value) : unique_resource_data ( static_cast< unique_resource_data&& >(that), typename std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::type(), typename std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::type() ) { } template< typename D, typename = typename std::enable_if< detail::conjunction< std::is_constructible< resource_holder, result_of_make_default >, std::is_constructible< deleter_holder, D > >::value >::type > explicit unique_resource_data(default_resource_t, D&& del) noexcept(detail::conjunction< std::is_nothrow_constructible< resource_holder, result_of_make_default >, std::is_nothrow_constructible< deleter_holder, D > >::value) : resource_holder(traits_type::make_default()), deleter_holder(static_cast< D&& >(del)) { } template< typename R, typename D, typename = typename std::enable_if< detail::conjunction< detail::negation< detail::is_default_resource< R > >, std::is_constructible< resource_holder, R, D, bool >, std::is_constructible< deleter_holder, D, resource_type&, bool > >::value >::type > explicit unique_resource_data(R&& res, D&& del) noexcept(detail::conjunction< std::is_nothrow_constructible< resource_holder, R, D, bool >, std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool > >::value) : unique_resource_data(static_cast< R&& >(res), static_cast< D&& >(del), traits_type::is_allocated(res)) // don't forward res to is_allocated to make sure res is not moved-from on resource construction { // Since res may not be of the resource type, the is_allocated call made above may require a type conversion or pick a different overload. // We still require it to be noexcept, as we need to know whether we should deallocate it. Otherwise we may leak the resource. static_assert(noexcept(traits_type::is_allocated(res)), "Invalid unique_resource resource traits: is_allocated must be noexcept"); } template< bool Requires = detail::conjunction< std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, std::is_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > >::value > typename std::enable_if< Requires, unique_resource_data& >::type operator= (unique_resource_data&& that) noexcept(detail::conjunction< std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, std::is_nothrow_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > >::value) { assign(static_cast< unique_resource_data&& >(that), typename std::is_nothrow_move_assignable< internal_deleter_type >::type()); return *this; } resource_type& get_resource() noexcept { return resource_holder::get(); } resource_type const& get_resource() const noexcept { return resource_holder::get(); } internal_resource_type& get_internal_resource() noexcept { return resource_holder::get_internal(); } internal_resource_type const& get_internal_resource() const noexcept { return resource_holder::get_internal(); } deleter_type& get_deleter() noexcept { return deleter_holder::get(); } deleter_type const& get_deleter() const noexcept { return deleter_holder::get(); } internal_deleter_type& get_internal_deleter() noexcept { return deleter_holder::get_internal(); } internal_deleter_type const& get_internal_deleter() const noexcept { return deleter_holder::get_internal(); } bool is_allocated() const noexcept { return traits_type::is_allocated(get_resource()); } void set_unallocated() noexcept { get_internal_resource() = traits_type::make_default(); } template< typename R > void assign_resource(R&& res) noexcept(std::is_nothrow_assignable< internal_resource_type&, R >::value) { get_internal_resource() = static_cast< R&& >(res); } template< bool Requires = detail::conjunction< detail::is_swappable< internal_resource_type >, detail::is_swappable< internal_deleter_type >, detail::disjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > > >::value > typename std::enable_if< Requires >::type swap(unique_resource_data& that) noexcept(detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value) { swap_impl ( that, std::integral_constant< bool, detail::is_nothrow_swappable< internal_resource_type >::value >(), std::integral_constant< bool, detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value >() ); } private: unique_resource_data(unique_resource_data&& that, std::true_type, std::true_type) noexcept : resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())) { that.set_unallocated(); } unique_resource_data(unique_resource_data&& that, std::false_type, std::true_type) : resource_holder(static_cast< resource_type const& >(that.get_resource())), deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())) { that.set_unallocated(); } unique_resource_data(unique_resource_data&& that, std::true_type, std::false_type) try : resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), deleter_holder(static_cast< deleter_type const& >(that.get_deleter())) { that.set_unallocated(); } catch (...) { // Since only the deleter's constructor could have thrown an exception here, move the resource back // to the original unique_resource. This is guaranteed to not throw. that.resource_holder::move_from(static_cast< internal_resource_type&& >(resource_holder::get_internal())); } unique_resource_data(unique_resource_data&& that, std::false_type, std::false_type) : resource_holder(static_cast< resource_type const& >(that.get_resource())), deleter_holder(static_cast< deleter_type const& >(that.get_deleter())) { that.set_unallocated(); } template< typename R, typename D, typename = typename std::enable_if< detail::conjunction< std::is_constructible< resource_holder, R, D, bool >, std::is_constructible< deleter_holder, D, resource_type&, bool > >::value >::type > explicit unique_resource_data(R&& res, D&& del, bool allocated) noexcept(detail::conjunction< std::is_nothrow_constructible< resource_holder, R, D, bool >, std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool > >::value) : resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated), deleter_holder(static_cast< D&& >(del), resource_holder::get(), allocated) { } void assign(unique_resource_data&& that, std::true_type) noexcept(std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >::value) { get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); that.set_unallocated(); } void assign(unique_resource_data&& that, std::false_type) { get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); that.set_unallocated(); } void swap_impl(unique_resource_data& that, std::true_type, std::true_type) noexcept { boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); } void swap_impl(unique_resource_data& that, std::true_type, std::false_type) { boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); } void swap_impl(unique_resource_data& that, std::false_type, std::false_type) { boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); } }; template< typename Resource, typename Deleter > class unique_resource_data< Resource, Deleter, void > : public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >, public detail::deleter_holder< Resource, Deleter > { public: using resource_type = Resource; using deleter_type = Deleter; using traits_type = void; private: using resource_holder = detail::resource_holder< resource_type, use_resource_compact_storage< resource_type, deleter_type >::value >; using deleter_holder = detail::deleter_holder< resource_type, deleter_type >; public: using internal_resource_type = typename resource_holder::internal_resource_type; using internal_deleter_type = typename deleter_holder::internal_deleter_type; private: bool m_allocated; public: template< bool Requires = detail::conjunction< std::is_default_constructible< resource_holder >, std::is_default_constructible< deleter_holder > >::value, typename = typename std::enable_if< Requires >::type > constexpr unique_resource_data() noexcept(detail::conjunction< std::is_nothrow_default_constructible< resource_holder >, std::is_nothrow_default_constructible< deleter_holder > >::value) : resource_holder(), deleter_holder(), m_allocated(false) { } unique_resource_data(unique_resource_data const&) = delete; unique_resource_data& operator= (unique_resource_data const&) = delete; template< bool Requires = detail::conjunction< std::is_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >, std::is_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type > >::value, typename = typename std::enable_if< Requires >::type > unique_resource_data(unique_resource_data&& that) noexcept(detail::conjunction< std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >, std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type > >::value) : unique_resource_data ( static_cast< unique_resource_data&& >(that), typename std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::type(), typename std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::type() ) { } template< typename D, typename = typename std::enable_if< detail::conjunction< std::is_default_constructible< resource_holder >, std::is_constructible< deleter_holder, D > >::value >::type > explicit unique_resource_data(default_resource_t, D&& del) noexcept(detail::conjunction< std::is_nothrow_default_constructible< resource_holder >, std::is_nothrow_constructible< deleter_holder, D > >::value) : resource_holder(), deleter_holder(static_cast< D&& >(del)), m_allocated(false) { } template< typename R, typename D, typename = typename std::enable_if< detail::conjunction< detail::negation< detail::is_default_resource< R > >, std::is_constructible< resource_holder, R, D, bool >, std::is_constructible< deleter_holder, D, resource_type&, bool > >::value >::type > explicit unique_resource_data(R&& res, D&& del) noexcept(detail::conjunction< std::is_nothrow_constructible< resource_holder, R, D, bool >, std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool > >::value) : resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), true), deleter_holder(static_cast< D&& >(del), resource_holder::get(), true), m_allocated(true) { } template< bool Requires = detail::conjunction< std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, std::is_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > >::value > typename std::enable_if< Requires, unique_resource_data& >::type operator= (unique_resource_data&& that) noexcept(detail::conjunction< std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >, std::is_nothrow_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type > >::value) { assign(static_cast< unique_resource_data&& >(that), typename std::is_nothrow_move_assignable< internal_deleter_type >::type()); return *this; } resource_type& get_resource() noexcept { return resource_holder::get(); } resource_type const& get_resource() const noexcept { return resource_holder::get(); } internal_resource_type& get_internal_resource() noexcept { return resource_holder::get_internal(); } internal_resource_type const& get_internal_resource() const noexcept { return resource_holder::get_internal(); } deleter_type& get_deleter() noexcept { return deleter_holder::get(); } deleter_type const& get_deleter() const noexcept { return deleter_holder::get(); } internal_deleter_type& get_internal_deleter() noexcept { return deleter_holder::get_internal(); } internal_deleter_type const& get_internal_deleter() const noexcept { return deleter_holder::get_internal(); } bool is_allocated() const noexcept { return m_allocated; } void set_unallocated() noexcept { m_allocated = false; } template< typename R > void assign_resource(R&& res) noexcept(std::is_nothrow_assignable< internal_resource_type&, R >::value) { get_internal_resource() = static_cast< R&& >(res); m_allocated = true; } template< bool Requires = detail::conjunction< detail::is_swappable< internal_resource_type >, detail::is_swappable< internal_deleter_type >, detail::disjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > > >::value > typename std::enable_if< Requires >::type swap(unique_resource_data& that) noexcept(detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value) { swap_impl ( that, std::integral_constant< bool, detail::is_nothrow_swappable< internal_resource_type >::value >(), std::integral_constant< bool, detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value >() ); } private: unique_resource_data(unique_resource_data&& that, std::true_type, std::true_type) noexcept : resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())), m_allocated(that.m_allocated) { that.m_allocated = false; } unique_resource_data(unique_resource_data&& that, std::false_type, std::true_type) : resource_holder(static_cast< resource_type const& >(that.get_resource())), deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())), m_allocated(that.m_allocated) { that.m_allocated = false; } unique_resource_data(unique_resource_data&& that, std::true_type, std::false_type) try : resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())), deleter_holder(static_cast< deleter_type const& >(that.get_deleter())), m_allocated(that.m_allocated) { that.m_allocated = false; } catch (...) { // Since only the deleter's constructor could have thrown an exception here, move the resource back // to the original unique_resource. This is guaranteed to not throw. that.resource_holder::move_from(static_cast< internal_resource_type&& >(resource_holder::get_internal())); } unique_resource_data(unique_resource_data&& that, std::false_type, std::false_type) : resource_holder(static_cast< resource_type const& >(that.get_resource())), deleter_holder(static_cast< deleter_type const& >(that.get_deleter())), m_allocated(that.m_allocated) { that.m_allocated = false; } void assign(unique_resource_data&& that, std::true_type) noexcept(std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >::value) { get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); m_allocated = that.m_allocated; that.m_allocated = false; } void assign(unique_resource_data&& that, std::false_type) { get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter()); get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource()); m_allocated = that.m_allocated; that.m_allocated = false; } void swap_impl(unique_resource_data& that, std::true_type, std::true_type) noexcept { boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); boost::core::invoke_swap(m_allocated, that.m_allocated); } void swap_impl(unique_resource_data& that, std::true_type, std::false_type) { boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); boost::core::invoke_swap(m_allocated, that.m_allocated); } void swap_impl(unique_resource_data& that, std::false_type, std::false_type) { boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource()); boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter()); boost::core::invoke_swap(m_allocated, that.m_allocated); } }; template< typename T > struct is_dereferenceable_impl { template< typename U, typename R = decltype(*std::declval< U const& >()) > static std::true_type _is_dereferenceable_check(int); template< typename U > static std::false_type _is_dereferenceable_check(...); using type = decltype(is_dereferenceable_impl::_is_dereferenceable_check< T >(0)); }; template< typename T > struct is_dereferenceable : public is_dereferenceable_impl< T >::type { }; template< > struct is_dereferenceable< void* > : public std::false_type { }; template< > struct is_dereferenceable< const void* > : public std::false_type { }; template< > struct is_dereferenceable< volatile void* > : public std::false_type { }; template< > struct is_dereferenceable< const volatile void* > : public std::false_type { }; template< > struct is_dereferenceable< void*& > : public std::false_type { }; template< > struct is_dereferenceable< const void*& > : public std::false_type { }; template< > struct is_dereferenceable< volatile void*& > : public std::false_type { }; template< > struct is_dereferenceable< const volatile void*& > : public std::false_type { }; template< > struct is_dereferenceable< void* const& > : public std::false_type { }; template< > struct is_dereferenceable< const void* const& > : public std::false_type { }; template< > struct is_dereferenceable< volatile void* const& > : public std::false_type { }; template< > struct is_dereferenceable< const volatile void* const& > : public std::false_type { }; template< > struct is_dereferenceable< void* volatile& > : public std::false_type { }; template< > struct is_dereferenceable< const void* volatile& > : public std::false_type { }; template< > struct is_dereferenceable< volatile void* volatile& > : public std::false_type { }; template< > struct is_dereferenceable< const volatile void* volatile& > : public std::false_type { }; template< > struct is_dereferenceable< void* const volatile& > : public std::false_type { }; template< > struct is_dereferenceable< const void* const volatile& > : public std::false_type { }; template< > struct is_dereferenceable< volatile void* const volatile& > : public std::false_type { }; template< > struct is_dereferenceable< const volatile void* const volatile& > : public std::false_type { }; template< typename T, bool = detail::is_dereferenceable< T >::value > struct dereference_traits { }; template< typename T > struct dereference_traits< T, true > { using result_type = decltype(*std::declval< T const& >()); static constexpr bool is_noexcept = noexcept(*std::declval< T const& >()); }; } // namespace detail /*! * \brief RAII wrapper for automatically reclaiming arbitrary resources. * * A \c unique_resource object exclusively owns wrapped resource and invokes * the deleter function object on it on destruction. The wrapped resource can have * any type that is: * * \li Move-constructible, where the move constructor is marked as `noexcept`, or * \li Copy-constructible, or * \li An lvalue reference to an object type. * * The deleter must be a function object type that is callable on an lvalue * of the resource type. The deleter must be copy-constructible. * * An optional resource traits template parameter may be specified. Resource * traits can be used to optimize \c unique_resource implementation when * the following conditions are met: * * \li There is at least one value of the resource type that is considered * unallocated (that is, no allocated resource shall be equal to one of * the unallocated resource values). The unallocated resource values need not * be deallocated using the deleter. * \li One of the unallocated resource values can be considered the default. * Constructing the default resource value and assigning it to a resource * object (whether allocated or not) shall not throw exceptions. * \li Resource objects can be tested for being unallocated. Such a test shall * not throw exceptions. * * If specified, the resource traits must be a class type that has the following * public static members: * * \li `R make_default() noexcept` - must return the default resource value such * that `std::is_constructible< Resource, R >::value && * std::is_nothrow_assignable< Resource&, R >::value` is \c true. * \li `bool is_allocated(Resource const& res) noexcept` - must return \c true * if \c res is not one of the unallocated resource values and \c false * otherwise. * * Note that `is_allocated(make_default())` must always return \c false. * * When resource traits satisfying the above requirements are specified, * \c unique_resource will be able to avoid storing additional indication of * whether the owned resource object needs to be deallocated with the deleter * on destruction. It will use the default resource value to initialize the owned * resource object when \c unique_resource is not in the allocated state. * Additionally, it will be possible to construct \c unique_resource with * unallocated resource values, which will create \c unique_resource objects in * unallocated state (the deleter will not be called on unallocated resource * values). * * \tparam Resource Resource type. * \tparam Deleter Resource deleter function object type. * \tparam Traits Optional resource traits type. */ template< typename Resource, typename Deleter, typename Traits BOOST_SCOPE_DETAIL_DOC(= void) > class unique_resource { public: //! Resource type using resource_type = Resource; //! Deleter type using deleter_type = Deleter; //! Resource traits using traits_type = Traits; //! \cond private: using data = detail::unique_resource_data< resource_type, deleter_type, traits_type >; using internal_resource_type = typename data::internal_resource_type; using internal_deleter_type = typename data::internal_deleter_type; data m_data; //! \endcond public: /*! * \brief Constructs an unallocated unique resource guard. * * **Requires:** Default \c Resource value can be constructed. \c Deleter is default-constructible * and is not a pointer to function. * * **Effects:** Initializes the \c Resource object with the default resource value. Default-constructs * the \c Deleter object. * * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. * * \post `this->allocated() == false` */ //! \cond template< bool Requires = std::is_default_constructible< data >::value, typename = typename std::enable_if< Requires >::type > //! \endcond constexpr unique_resource() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_default_constructible< data >::value)) { } /*! * \brief Constructs an unallocated unique resource guard with the given deleter. * * **Requires:** Default \c Resource value can be constructed and \c Deleter is constructible from \a del. * * **Effects:** Initializes the \c Resource value with the default resource value. If \c Deleter is nothrow * constructible from `D&&` then constructs \c Deleter from `std::forward< D >(del)`, * otherwise constructs from `del`. * * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. * * \param res A tag argument indicating default resource value. * \param del Resource deleter function object. * * \post `this->allocated() == false` */ template< typename D //! \cond , typename = typename std::enable_if< std::is_constructible< data, default_resource_t, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >::value >::type //! \endcond > unique_resource(default_resource_t res, D&& del) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( std::is_nothrow_constructible< data, default_resource_t, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >::value )) : m_data ( res, static_cast< typename detail::move_or_copy_construct_ref< D, deleter_type >::type >(del) ) { } /*! * \brief Constructs a unique resource guard with the given resource and a default-constructed deleter. * * **Requires:** \c Resource is constructible from \a res. \c Deleter is default-constructible and * is not a pointer to function. * * **Effects:** Constructs the unique resource object as if by calling * `unique_resource(std::forward< R >(res), Deleter())`. * * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. * * \param res Resource object. */ template< typename R //! \cond , typename = typename std::enable_if< detail::conjunction< detail::is_nothrow_nonnull_default_constructible< deleter_type >, std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< deleter_type >::type >, detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue >::value >::type //! \endcond > explicit unique_resource(R&& res) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::value )) : m_data ( static_cast< typename detail::move_or_copy_construct_ref< R, resource_type >::type >(res), static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(deleter_type()) ) { } /*! * \brief Constructs a unique resource guard with the given resource and deleter. * * **Requires:** \c Resource is constructible from \a res and \c Deleter is constructible from \a del. * * **Effects:** If \c Resource is nothrow constructible from `R&&` then constructs \c Resource * from `std::forward< R >(res)`, otherwise constructs from `res`. If \c Deleter * is nothrow constructible from `D&&` then constructs \c Deleter from * `std::forward< D >(del)`, otherwise constructs from `del`. * * If construction of \c Resource or \c Deleter throws and \a res is not an unallocated resource * value, invokes \a del on \a res (if \c Resource construction failed) or the constructed * \c Resource object (if \c Deleter construction failed). * * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. * * \param res Resource object. * \param del Resource deleter function object. * * \post If \a res is an unallocated resource value then `this->allocated() == false`, otherwise * `this->allocated() == true`. */ template< typename R, typename D //! \cond , typename = typename std::enable_if< detail::conjunction< std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >, detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue >::value >::type //! \endcond > unique_resource(R&& res, D&& del) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( std::is_nothrow_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >::value )) : m_data ( static_cast< typename detail::move_or_copy_construct_ref< R, resource_type >::type >(res), static_cast< typename detail::move_or_copy_construct_ref< D, deleter_type >::type >(del) ) { } unique_resource(unique_resource const&) = delete; unique_resource& operator= (unique_resource const&) = delete; /*! * \brief Move-constructs a unique resource guard. * * **Requires:** \c Resource and \c Deleter are move-constructible. * * **Effects:** If \c Resource is nothrow move-constructible then move-constructs \c Resource, * otherwise copy-constructs. If \c Deleter is nothrow move-constructible then move-constructs * \c Deleter, otherwise copy-constructs. Deactivates the moved-from unique resource object. * * If an exception is thrown during construction, \a that is left in its original state. * * \note This logic ensures that in case of exception the resource is not leaked and remains owned by the * move source. * * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws. * * \param that Move source. * * \post Let \c allocated be equal to `that.allocated()` prior to the operation. Then * `this->allocated() == allocated` and `that.allocated() == false`. */ //! \cond template< bool Requires = std::is_move_constructible< data >::value, typename = typename std::enable_if< Requires >::type > //! \endcond unique_resource(unique_resource&& that) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_constructible< data >::value)) : m_data(static_cast< data&& >(that.m_data)) { } /*! * \brief Move-assigns a unique resource guard. * * **Requires:** \c Resource and \c Deleter are move-assignable. * * **Effects:** Calls `this->reset()`. Then, if \c Deleter is nothrow move-assignable, move-assigns * the \c Deleter object first and the \c Resource object next. Otherwise, move-assigns * the objects in reverse order. Lastly, deactivates the moved-from unique resource object. * * If an exception is thrown, \a that is left in its original state. * * \note The different orders of assignment ensure that in case of exception the resource is not leaked * and remains owned by the move source. * * **Throws:** Nothing, unless assignment of \c Resource or \c Deleter throws. * * \param that Move source. * * \post Let \c allocated be equal to `that.allocated()` prior to the operation. Then * `this->allocated() == allocated` and `that.allocated() == false`. */ #if !defined(BOOST_SCOPE_DOXYGEN) template< bool Requires = std::is_move_assignable< data >::value > typename std::enable_if< Requires, unique_resource& >::type #else unique_resource& #endif operator= (unique_resource&& that) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_assignable< data >::value)) { reset(); m_data = static_cast< data&& >(that.m_data); return *this; } /*! * \brief If the resource is allocated, calls the deleter function on it. Destroys the resource and the deleter. * * **Throws:** Nothing, unless invoking the deleter throws. */ ~unique_resource() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< deleter_type&, resource_type& >::value)) { if (BOOST_LIKELY(m_data.is_allocated())) m_data.get_deleter()(m_data.get_resource()); } /*! * \brief Returns \c true if the resource is allocated and to be reclaimed by the deleter, otherwise \c false. * * \note This method does not test the value of the resource. * * **Throws:** Nothing. */ explicit operator bool () const noexcept { return m_data.is_allocated(); } /*! * \brief Returns \c true if the resource is allocated and to be reclaimed by the deleter, otherwise \c false. * * **Throws:** Nothing. */ bool allocated() const noexcept { return m_data.is_allocated(); } /*! * \brief Returns a reference to the resource object. * * **Throws:** Nothing. */ resource_type const& get() const noexcept { return m_data.get_resource(); } /*! * \brief Returns a reference to the deleter object. * * **Throws:** Nothing. */ deleter_type const& get_deleter() const noexcept { return m_data.get_deleter(); } /*! * \brief Marks the resource as unallocated. Does not call the deleter if the resource was previously allocated. * * **Throws:** Nothing. * * \post `this->allocated() == false` */ void release() noexcept { m_data.set_unallocated(); } /*! * \brief If the resource is allocated, calls the deleter function on it and marks the resource as unallocated. * * **Throws:** Nothing, unless invoking the deleter throws. * * \post `this->allocated() == false` */ void reset() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< deleter_type&, resource_type& >::value)) { if (BOOST_LIKELY(m_data.is_allocated())) { m_data.get_deleter()(m_data.get_resource()); m_data.set_unallocated(); } } /*! * \brief Assigns a new resource object to the unique resource wrapper. * * **Effects:** Calls `this->reset()`. Then, if \c Resource is nothrow assignable from `R&&`, * assigns `std::forward< R >(res)` to the stored resource object, otherwise assigns * `res`. * * If \a res is not an unallocated resource value and an exception is thrown during the operation, * invokes the stored deleter on \a res before returning with the exception. * * **Throws:** Nothing, unless invoking the deleter throws. * * \param res Resource object to assign. * * \post `this->allocated() == false` */ template< typename R > #if !defined(BOOST_SCOPE_DOXYGEN) typename std::enable_if< detail::conjunction< std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type >, detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue >::value >::type #else void #endif reset(R&& res) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( detail::conjunction< detail::is_nothrow_invocable< deleter_type&, resource_type& >, std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type > >::value )) { reset_impl ( static_cast< R&& >(res), typename detail::conjunction< detail::is_nothrow_invocable< deleter_type&, resource_type& >, std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type > >::type() ); } /*! * \brief Invokes indirection on the resource object. * * **Requires:** \c Resource is dereferenceable. * * **Effects:** Returns a reference to the resource object as if by calling `get()`. * * \note If \c Resource is not a pointer type, the compiler will invoke its `operator->`. * Such call sequence will continue until a pointer is obtained. * * **Throws:** Nothing. Note that any implicit subsequent calls to other `operator->` * functions that are caused by this call may have different throw conditions. */ #if !defined(BOOST_SCOPE_DOXYGEN) template< bool Requires = detail::is_dereferenceable< resource_type >::value > typename std::enable_if< Requires, resource_type const& >::type #else resource_type const& #endif operator-> () const noexcept { return get(); } /*! * \brief Dereferences the resource object. * * **Requires:** \c Resource is dereferenceable. * * **Effects:** Returns the result of dereferencing the resource object as if by calling `*get()`. * * **Throws:** Nothing, unless dereferencing the resource object throws. */ #if !defined(BOOST_SCOPE_DOXYGEN) template< bool Requires = detail::is_dereferenceable< resource_type >::value > typename detail::dereference_traits< resource_type, Requires >::result_type #else auto #endif operator* () const noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::dereference_traits< resource_type, Requires >::is_noexcept)) { return *get(); } /*! * \brief Swaps two unique resource wrappers. * * **Requires:** \c Resource and \c Deleter are swappable. At least one of \c Resource and \c Deleter * is nothrow swappable. * * **Effects:** Swaps the resource objects and deleter objects stored in `*this` and \a that * as if by calling unqualified `swap` in a context where `std::swap` is * found by overload resolution. * * If an exception is thrown, and the failed swap operation supports strong exception * guarantee, both `*this` and \a that are left in their original states. * * **Throws:** Nothing, unless swapping the resource objects or deleters throw. * * \param that Unique resource wrapper to swap with. */ #if !defined(BOOST_SCOPE_DOXYGEN) template< bool Requires = detail::is_swappable< data >::value > typename std::enable_if< Requires >::type #else void #endif swap(unique_resource& that) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_swappable< data >::value)) { m_data.swap(that.m_data); } /*! * \brief Swaps two unique resource wrappers. * * **Effects:** As if `left.swap(right)`. */ #if !defined(BOOST_SCOPE_DOXYGEN) template< bool Requires = detail::is_swappable< data >::value > friend typename std::enable_if< Requires >::type #else friend void #endif swap(unique_resource& left, unique_resource& right) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_swappable< data >::value)) { left.swap(right); } //! \cond private: //! Assigns a new resource object to the unique resource wrapper. template< typename R > void reset_impl(R&& res, std::true_type) noexcept { reset(); m_data.assign_resource(static_cast< typename detail::move_or_copy_assign_ref< R, resource_type >::type >(res)); } //! Assigns a new resource object to the unique resource wrapper. template< typename R > void reset_impl(R&& res, std::false_type) { try { reset(); m_data.assign_resource(static_cast< typename detail::move_or_copy_assign_ref< R, resource_type >::type >(res)); } catch (...) { m_data.get_deleter()(static_cast< R&& >(res)); throw; } } //! \endcond }; #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) template< typename Resource, typename Deleter, typename = typename std::enable_if< !detail::is_default_resource< Resource >::value >::type > unique_resource(Resource, Deleter) -> unique_resource< Resource, Deleter >; #endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) /*! * \brief Checks if the resource is valid and creates a \c unique_resource wrapper. * * **Effects:** If the resource \a res is not equal to \a invalid, creates a unique resource wrapper * that is in allocated state and owns \a res. Otherwise creates a unique resource wrapper * in unallocated state. * * \note This function does not call \a del if \a res is equal to \a invalid. * * **Throws:** Nothing, unless \c unique_resource constructor throws. * * \param res Resource to wrap. * \param invalid An invalid value for the resource. * \param del A deleter to invoke on the resource to free it. */ template< typename Resource, typename Deleter, typename Invalid > inline unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type > make_unique_resource_checked(Resource&& res, Invalid const& invalid, Deleter&& del) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN( detail::conjunction< std::is_nothrow_constructible< typename std::decay< Resource >::type, typename detail::move_or_copy_construct_ref< Resource, typename std::decay< Resource >::type >::type >, std::is_nothrow_constructible< typename std::decay< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter, typename std::decay< Deleter >::type >::type > >::value )) { using unique_resource_type = unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type >; if (!(res == invalid)) return unique_resource_type(static_cast< Resource&& >(res), static_cast< Deleter&& >(del)); else return unique_resource_type(default_resource_t(), static_cast< Deleter&& >(del)); } } // namespace scope } // namespace boost #include #endif // BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_