// clonable_ptr.h /*- * Copyright (C) 2016 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #pragma once #include #include #include #include namespace mongo { namespace clonable_ptr_detail { // This is the default `CloneFactory` conforming to `mongo::concept::CloneFactory` for // `clonable_ptr`. template struct CloneFactory { auto operator()(const Clonable& c) const -> decltype(c.clone()) { return c.clone(); } }; // TODO: Move some of these traits detection structs to a template metaprogramming header. template struct detect_clone_factory_type_member_impl { struct Fallback { struct clone_factory_type {}; }; struct Derived : T, Fallback {}; using Yes = char[2]; using No = char[1]; template static No& test(typename U::clone_factory_type*); template static Yes& test(U*); static constexpr bool value = sizeof(test(0)) == sizeof(Yes); using type = typename std::integral_constant::type; }; template struct detect_clone_factory_type_member : std::conditional::value, detect_clone_factory_type_member_impl, std::false_type>::type {}; template ::value> struct clonable_traits_impl; template struct clonable_traits_impl { using clone_factory_type = CloneFactory; }; template struct clonable_traits_impl { using clone_factory_type = typename T::clone_factory_type; }; } // namespace clonable_ptr_detail /** * The 'clonable_traits' class is a specializable traits class for clonable-like types. By * specializing this traits class for a type it is possible to change the global default * `CloneFactory` type for a specific type. Types which conform to `mongo::concept::Clonable` * will get a default `CloneFactory` type whch invokes their specific `Clonable::clone` function. A * specialization can be used to make a type use a different clone factory function. A type `T` may * specify `T::clone_factory_type` instead of specializing this traits type. */ template struct clonable_traits : clonable_ptr_detail::clonable_traits_impl {}; /** * The `clonable_ptr` represents a value-like type held at a distance. The `clonable_ptr` class is * a smart-pointer type which functions like a `std::unique_ptr` with the added ability to create * new copies of the pointee on copy construction. The default CloneFactory assumes that `T` is a * type which models the Concept `mongo::concept::Clonable`. The supplied type may supply an * alternative default `CloneFactory` type by either of two means: * * * `T` may define a member `T::clone_factory_type` which conforms to * `mongo::concept::CloneFactory` * * `T` may have an accompanying specialization of `mongo::clonable_traits< T >` which * defines `clonable_factory_type`. * * NOTE: The `CloneFactory` type is permitted to be stateful, but must be copy constructible and * copy assignable. * NOTE: The `CloneFactory` member does NOT participate in value comparisons for a `clonable_ptr`, * even when it has state. * * `T`: The type of the object being managed. * `CloneFactory`: A type which models the Concept `mongo::concept::CloneFactory`. * `UniquePtr`: A type which models the Concept `mongo::concept::UniquePtr` */ template ::clone_factory_type, template class UniquePtr = std::unique_ptr> class clonable_ptr { private: // `std::tuple` is used to avoid allocating storage for `cloneFactory` if it is a non-storage // type. std::tuple> data; inline const CloneFactory& cloneFactory() const { return std::get<0>(data); } inline const UniquePtr& ptr() const { return std::get<1>(data); } inline UniquePtr& ptr() { return std::get<1>(data); } inline const auto& _makeEqualityLens() const noexcept { return this->ptr(); } inline const auto& _makeStrictWeakOrderLens() const noexcept { return this->ptr(); } static inline UniquePtr clone_with_factory_impl(const T& copy, const CloneFactory& factory) { return UniquePtr{factory(copy)}; } template static inline UniquePtr clone_with_factory(Pointerlike&& copy, const CloneFactory& factory) { if (!copy) return nullptr; return clone_with_factory_impl(*copy, factory); } struct internal_construction {}; explicit inline clonable_ptr(UniquePtr&& p, const CloneFactory* const f, const internal_construction&) : data(*f, std::move(p)) {} explicit inline clonable_ptr(UniquePtr&& p, CloneFactory&& f, const internal_construction&) : data(std::move(f), std::move(p)) {} public: /*! Destroys this pointer. Functions like `std::unique_ptr`. */ inline ~clonable_ptr() noexcept = default; /*! Moves a value, by pointer. Functions like `std::unique_ptr`. */ inline clonable_ptr(clonable_ptr&&) noexcept( noexcept(CloneFactory{std::declval()}) && noexcept(UniquePtr{std::declval>()})) = default; /*! Moves a value, by pointer. Functions like `std::unique_ptr`. */ inline clonable_ptr& operator=(clonable_ptr&&) & noexcept(noexcept(std::declval() = std::declval()) && noexcept(std::declval>() = std::declval>())) = default; /*! * Constructs a pointer referring to a new copy of an original value. The old object owned by * `*this` will be deleted, and `*this` will manage a new copy of `copy`, as created by * `copy->clone()`. If `copy` is not managing anything (its internal pointer is `nullptr`), * then this new copy will also be nullptr. * * POST: `copy != nullptr ? copy != *this : copy == *this` -- If `copy` stores a pointer to a * value, then `*this` will have an independent pointer. If `copy` stores `nullptr`, then * `*this` will also store `nullptr`. * * `copy`: The original value to copy. * THROWS: Any exceptions thrown by `cloneFactory( *copy )`. * TODO: Consider adding a noexcept deduction specifier to this copy operation. */ inline clonable_ptr(const clonable_ptr& copy) : data{copy.cloneFactory(), clone_with_factory(copy, copy.cloneFactory())} {} /*! * Constructs a pointer referring to a new copy of an original value. The old object owned by * `*this` will be deleted, and `*this` will manage a new copy of `copy`, as created by * `copy->clone()`. If `copy` is not managing anything (its internal pointer is `nullptr`), * then this new copy will also be nullptr. * * POST: `copy != nullptr ? copy != *this : copy == *this` -- If `copy` stores a pointer to a * value, then `*this` will have an independent pointer. If `copy` stores `nullptr`, then * `*this` will also store `nullptr`. * * NOTE: The `CloneFactory` will be copied from the `copy` poiner, by default. * * `copy`: The original value to copy. * `factory`: The factory to use for cloning. Defaults to the source's factory. * THROWS: Any exceptions thrown by `factory( *copy )`. * TODO: Consider adding a noexcept deduction specifier to this copy operation. */ inline clonable_ptr(const clonable_ptr& copy, const CloneFactory& factory) : data{factory, clone_with_factory(copy, factory)} {} /*! * Changes the value of this pointer, by creating a new object having the same value as `copy`. * The old object owned by `*this` will be deleted, and `*this` will manage a new copy of * `copy`, as created by `copy->clone()`. If `copy` is not managing anything (its internal * pointer is `nullptr`), then this new copy will also be nullptr. * * NOTE: This operation cannot be conducted on an xvalue or prvalue instance. (This prevents * silliness such as: `func_returning_ptr()= some_other_func_returning_ptr();`) * * NOTE: `copy`'s `CloneFactory` will be used to copy. * * POST: `copy != nullptr ? copy != *this : copy == *this` -- If `copy` stores a pointer to a * value, then `*this` will have an independent pointer. If `copy` stores `nullptr`, then * `*this` will also store `nullptr`. * * `copy`: The value to make a copy of. * RETURNS: A reference to this pointer, after modification. * TODO: Consider adding a noexcept deduction specifier to this copy operation. */ inline clonable_ptr& operator=(const clonable_ptr& copy) & { return *this = clonable_ptr{copy}; } // Maintenance note: The two enable_if overloads of `clonable_ptr( std::nullptr_t )` are // necessary, due to the fact that `std::nullptr_t` is capable of implicit conversion to a // built-in pointer type. If the stateful form being deleted causes the `nullptr` to convert, // this could cause binding to another ctor which may be undesired. /*! * `nullptr` construct a clonable pointer (to `nullptr`), if the `CloneFactory` type is * stateless. * The value will be a pointer to nothing, with a default `CloneFactory`. * NOTE: This constructor is only available for types with a stateless `CloneFactory` type. */ template inline clonable_ptr( typename std::enable_if::value, std::nullptr_t>::type) {} /*! * Disable `nullptr` construction of clonable pointer (to `nullptr`), if the `CloneFactory` type * is stateful. * NOTE: This constructor is disabled for types with a stateless `CloneFactory` type. */ template inline clonable_ptr(typename std::enable_if::value, std::nullptr_t>::type) = delete; /*! * Constructs a pointer to nothing, with a default `CloneFactory`. * This function is unavailable when `CloneFactory` is stateful. */ template ::value>::type> explicit inline clonable_ptr() noexcept {} /*! Constructs a pointer to nothing, with the specified `CloneFactory`. */ explicit inline clonable_ptr(CloneFactory factory) : data{factory, nullptr} {} /*! * Constructs a `clonable_ptr` which owns `p`, initializing the stored pointer with `p`. * This function is unavailable when `CloneFactory` is stateful. * `p`: The pointer to take ownership of. */ template explicit inline clonable_ptr( typename std::enable_if::value, T* const>::type p) : clonable_ptr(UniquePtr{p}) {} /*! * Disable single-argument construction of clonable pointer (with a raw pointer), if the * `CloneFactory` type is stateful. * NOTE: This constructor is disabled for types with a stateless `CloneFactory` type. */ template explicit inline clonable_ptr( typename std::enable_if::value, T* const>::type) = delete; // The reason that we have two overloads for clone factory is to ensure that we avoid as many // exception-unsafe uses as possible. The const-lvalue-reference variant in conjunction with // the rvalue-reference variant lets us separate the cases of "constructed in place" from // "passed from a local". In the latter case, we can't make our type any safer, since the // timing of the construction of the local and the timing of the `new` on the raw pointer are // out of our control. At least we prevent an accidental use which SEEMS exception safe but // isn't -- hopefully highlighting exception unsafe code, by making it more explicit. In the // former, "constructed in place", case, we are able to successfully move construct without // exception problems, if it's nothrow move constructible. If it isn't we flag a compiler // error. In this case, too, we prevent accidental use which SEEMS exception safe and hopefully // will similarly highlight exception unsafe code. /*! * Constructs a `clonable_ptr` which owns `p`, initializing the stored pointer with `p`. The * `factory` parameter will be used as the `CloneFactory` * `p`: The pointer to take ownership of. * `factory`: The clone factory to use in future copies. * NOTE: It is not recommended to use this constructor, as the following is not exception safe * code: * ~~~ * std::function cloner= [](const T& p){ return p; }; * auto getCloner= [=]{ return cloner; }; * clonable_ptr> bad{new T, getCloner()}; // BAD IDEA!!! * ~~~ * Even if the above could be made exception safe, there are other more complicated use cases * which would not be exception safe. (The above is not exception safe, because the `new T` * expression can be evaluated before the `getCloner()` expression is evaluated. `getCloner()` * is allowed to throw, thus leaving `new T` to be abandoned. */ explicit inline clonable_ptr(T* const p, const CloneFactory& factory) : clonable_ptr{UniquePtr{p}, std::addressof(factory), internal_construction{}} {} /*! * Constructs a `clonable_ptr` which owns `p`, initializing the stored pointer with `p`. The * `factory` parameter will be used as the `CloneFactory` for future copies. * `p`: The pointer to take ownership of. * `factory`: The clone factory to use in future copies. * NOTE: It is not recommended to use this constructor, as the following is not exception safe * code: * ~~~ * clonable_ptr> bad{new T, [](const T& p){ return p; }}; // BAD IDEA!!! * ~~~ * Even if the above could be made exception safe, there are other more complicated use cases * which would not be exception safe. (The above is not exception safe, because the `new T` * expression can be evaluated before the lambda expression is evaluated and converted to a * `std::function`. The `std::function` constructor is allowed to throw, thus leaving `new T` * to be abandoned. */ explicit inline clonable_ptr(T* const p, CloneFactory&& factory) : clonable_ptr{UniquePtr{p}, std::move(factory), internal_construction{}} { static_assert(std::is_nothrow_move_constructible::value, "The `CloneFactory` type must be nothrow constructible to use as an r-value " "in an initialization expression with a raw pointer."); } /*! * Constructs a `clonable_ptr` by transferring ownership from `p` to `*this`. A default * `CloneFactory` will be provided for future copies. * `p`: The pointer to take ownership of. * NOTE: This constructor allows for implicit conversion from a `UniquePtr` (xvalue) object. * NOTE: This constructor is unavailable when `CloneFactory` is stateful. * NOTE: This usage should be preferred over the raw-pointer construction forms, when using * factories as constructor arguments, as in the following exception safe code: * ~~~ * clonable_ptr> good{std::make_unique(), * [](const T& p){ return p; }}; // GOOD IDEA!!! * ~~~ */ template ::value>::type> inline clonable_ptr(UniquePtr p) : data{CloneFactory{}, std::move(p)} {} /*! * Constructs a `clonable_ptr` by transferring ownership from `p` to `*this`. The `factory` * parameter will be used as the `CloneFactory` for future copies. * NOTE: This constructor allows for implicit conversion from a `UniquePtr` (xvalue) object. * `p`: The pointer to take ownership of. * `factory`: The clone factory to use in future copies. * NOTE: This usage should be preferred over the raw-pointer construction forms, when using * factories as constructor arguments, as in the following exception safe code: * ~~~ * clonable_ptr> good{std::make_unique(), * [](const T& p){ return p; }}; // GOOD IDEA!!! * ~~~ */ template inline clonable_ptr(UniquePtr p, CloneFactory factory) : data{std::move(factory), std::move(p)} {} /*! * Changes the value of this pointer, by creating a new object having the same value as `copy`. * The old object owned by `*this` will be deleted, and `*this` will manage a new copy of * `copy`, as created by `copy->clone()`. If `copy` is not managing anything (its internal * pointer is `nullptr`), then this new copy will also be nullptr. * * NOTE: This operation cannot be performed on an xvalue or prvalue instance. (This prevents * silliness such as: `func_returning_ptr()= some_other_func_returning_ptr();`) * * NOTE: `copy`'s `CloneFactory` will be used to copy. * * POST: `copy != nullptr ? copy != *this : copy == *this` -- If `copy` stores a pointer to a * value, then `*this` will have an independent pointer. If `copy` stores `nullptr`, then * `*this` will also store `nullptr`. * * `copy`: The value to make a copy of. * RETURNS: A reference to this pointer, after modification. */ inline clonable_ptr& operator=(UniquePtr copy) & { return *this = std::move(clonable_ptr{std::move(copy), this->cloneFactory()}); } template inline clonable_ptr& operator=(UniquePtr< Derived > copy) & { return *this = std::move(clonable_ptr{std::move(copy), this->cloneFactory()}); } /*! * Change the `CloneFactory` for `*this` to `factory`. * NOTE: This operation cannot be performed on an xvalue or prvalue instance. (This prevents * silliness such as: `func_returning_ptr().setCloneFactory( factory );`.) */ template inline void setCloneFactory(FactoryType&& factory) & { this->cloneFactory() = std::forward(factory); } /*! * Dereferences the pointer owned by `*this`. * NOTE: The behavior is undefined if `this->get() == nullptr`. * RETURNS: The object owned by `*this`, equivalent to `*get()`. */ inline auto& operator*() const noexcept(noexcept(*this->ptr())) { return *this->ptr(); } /*! * Dereferences the pointer owned by `*this`. * NOTE: The behavior is undefined if `this->get() == nullptr`. * RETURNS: A pointer to the object owned by `*this`, equivalent to `get()`. */ inline auto* operator-> () const noexcept(noexcept(this->ptr().operator->())) { return this->ptr().operator->(); } /*! * Returns `true` if `*this` owns a pointer to a value, and `false` otherwise. * RETURNS: A value equivalent to `static_cast< bool >( this->get() )`. */ explicit inline operator bool() const noexcept { return this->ptr().get(); } /*! * Converts `*this` to a `UniquePtr< T >` by transferring ownership. This function will retire * ownership of the pointer owned by `*this`. This is a safe operation, as this function cannot * be called from an lvalue context -- rvalue operations are used to represent transfer of * ownership semantics. * * NOTE: This function is only applicable in `rvalue` contexts. * NOTE: This function has transfer of ownership semantics. * * RETURNS: A `UniquePtr< T >` which owns the pointer formerly managed by `*this`. */ inline operator UniquePtr() && { return std::move(this->ptr()); } /*! Provides a constant `UniquePtr< T >` view of the object owned by `*this`. */ inline operator const UniquePtr&() const& { return this->ptr(); } /*! Provides a mutable `UniquePtr< T >` view of the object owned by `*this`. */ inline operator UniquePtr&() & { return this->ptr(); } /*! Provides a C-style `T *` pointer to the object owned by `*this`. */ inline T* get() const noexcept(noexcept(this->ptr().get())) { return this->ptr().get(); } inline void reset() & noexcept { this->ptr().reset(); } inline void reset(T* const p) & noexcept(noexcept(this->ptr().reset(p))) { this->ptr().reset(p); } // Equality template class U> inline friend bool operator==(const clonable_ptr& lhs, const clonable_ptr& rhs) { return lhs._makeEqualityLens() == rhs._makeEqualityLens(); } template class U> inline friend bool operator==(const U& lhs, const clonable_ptr& rhs) { return lhs == rhs._makeEqualityLens(); } template class U> inline friend bool operator==(const clonable_ptr& lhs, const U& rhs) { return lhs._makeEqualityLens() == rhs; } template class U> inline friend bool operator==(const std::nullptr_t& lhs, const clonable_ptr& rhs) { return lhs == rhs._makeEqualityLens(); } template class U> inline friend bool operator==(const clonable_ptr& lhs, const std::nullptr_t& rhs) { return lhs._makeEqualityLens() == rhs; } // Strict weak order template class U> inline friend bool operator<(const clonable_ptr& lhs, const clonable_ptr& rhs) { return lhs._makeStrictWeakOrderLens() < rhs._makeStrictWeakOrderLens(); } template class U> inline friend bool operator<(const U& lhs, const clonable_ptr& rhs) { return lhs < rhs._makeStrictWeakOrderLens(); } template class U> inline friend bool operator<(const clonable_ptr& lhs, const U& rhs) { return lhs._makeStrictWeakOrderLens() < rhs; } template class U> inline friend bool operator<(const std::nullptr_t& lhs, const clonable_ptr& rhs) { return lhs < rhs._makeStrictWeakOrderLens(); } template class U> inline friend bool operator<(const clonable_ptr& lhs, const std::nullptr_t& rhs) { return lhs._makeStrictWeakOrderLens() < rhs; } }; // Inequality template class U> inline bool operator!=(const clonable_ptr& lhs, const clonable_ptr& rhs) { return !(lhs == rhs); } template class U> inline bool operator!=(const U& lhs, const clonable_ptr& rhs) { return !(lhs == rhs); } template class U> inline bool operator!=(const clonable_ptr& lhs, const U& rhs) { return !(lhs == rhs); } template class U> inline bool operator!=(const std::nullptr_t& lhs, const clonable_ptr& rhs) { return !(lhs == rhs); } template class U> inline bool operator!=(const clonable_ptr& lhs, const std::nullptr_t& rhs) { return !(lhs == rhs); } // Greater than template class U> inline bool operator>(const clonable_ptr& lhs, const clonable_ptr& rhs) { return rhs < lhs; } template class U> inline bool operator>(const U& lhs, const clonable_ptr& rhs) { return rhs < lhs; } template class U> inline bool operator>(const clonable_ptr& lhs, const U& rhs) { return rhs < lhs; } template class U> inline bool operator>(const std::nullptr_t& lhs, const clonable_ptr& rhs) { return rhs < lhs; } template class U> inline bool operator>(const clonable_ptr& lhs, const std::nullptr_t& rhs) { return rhs < lhs; } // Equal or Less template class U> inline bool operator<=(const clonable_ptr& lhs, const clonable_ptr& rhs) { return !(lhs > rhs); } template class U> inline bool operator<=(const U& lhs, const clonable_ptr& rhs) { return !(lhs > rhs); } template class U> inline bool operator<=(const clonable_ptr& lhs, const U& rhs) { return !(lhs > rhs); } template class U> inline bool operator<=(const std::nullptr_t& lhs, const clonable_ptr& rhs) { return !(lhs > rhs); } template class U> inline bool operator<=(const clonable_ptr& lhs, const std::nullptr_t& rhs) { return !(lhs > rhs); } // Equal or greater template class U> inline bool operator>=(const clonable_ptr& lhs, const clonable_ptr& rhs) { return !(lhs < rhs); } template class U> inline bool operator>=(const U& lhs, const clonable_ptr& rhs) { return !(lhs < rhs); } template class U> inline bool operator>=(const clonable_ptr& lhs, const U& rhs) { return !(lhs < rhs); } template class U> inline bool operator>=(const std::nullptr_t& lhs, const clonable_ptr& rhs) { return !(lhs < rhs); } template class U> inline bool operator>=(const clonable_ptr& lhs, const std::nullptr_t& rhs) { return !(lhs < rhs); } } // namespace mongo