diff options
-rw-r--r-- | src/mongo/base/SConscript | 9 | ||||
-rw-r--r-- | src/mongo/base/clonable_ptr.h | 648 | ||||
-rw-r--r-- | src/mongo/base/clonable_ptr_test.cpp | 974 | ||||
-rw-r--r-- | src/mongo/base/concept/README | 5 | ||||
-rw-r--r-- | src/mongo/base/concept/assignable.h | 40 | ||||
-rw-r--r-- | src/mongo/base/concept/clonable.h | 47 | ||||
-rw-r--r-- | src/mongo/base/concept/clone_factory.h | 48 | ||||
-rw-r--r-- | src/mongo/base/concept/constructible.h | 65 | ||||
-rw-r--r-- | src/mongo/base/concept/convertible_to.h | 41 | ||||
-rw-r--r-- | src/mongo/base/concept/copy_assignable.h | 45 | ||||
-rw-r--r-- | src/mongo/base/concept/copy_constructible.h | 41 | ||||
-rw-r--r-- | src/mongo/base/concept/unique_ptr.h | 68 |
12 files changed, 2031 insertions, 0 deletions
diff --git a/src/mongo/base/SConscript b/src/mongo/base/SConscript index 848aca3b7f0..af40fdab72a 100644 --- a/src/mongo/base/SConscript +++ b/src/mongo/base/SConscript @@ -55,6 +55,15 @@ env.CppUnitTest( ], ) +env.CppUnitTest( + target=[ + 'clonable_ptr_test', + ], + source=[ + 'clonable_ptr_test.cpp', + ], +) + env.Library( target=[ 'secure_allocator' diff --git a/src/mongo/base/clonable_ptr.h b/src/mongo/base/clonable_ptr.h new file mode 100644 index 00000000000..0b718db4d5d --- /dev/null +++ b/src/mongo/base/clonable_ptr.h @@ -0,0 +1,648 @@ +// 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 <http://www.gnu.org/licenses/>. + * + * 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 <cstddef> +#include <memory> +#include <tuple> +#include <type_traits> + +namespace mongo { +namespace clonable_ptr_detail { +// This is the default `CloneFactory` conforming to `mongo::concept::CloneFactory` for +// `clonable_ptr`. +template <typename Clonable> +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 <typename T> +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 <typename U> + static No& test(typename U::clone_factory_type*); + + template <typename U> + static Yes& test(U*); + + static constexpr bool value = sizeof(test<Derived>(0)) == sizeof(Yes); + + using type = typename std::integral_constant<bool, value>::type; +}; + +template <typename T> +struct detect_clone_factory_type_member : std::conditional<std::is_class<T>::value, + detect_clone_factory_type_member_impl<T>, + std::false_type>::type {}; + +template <typename T, bool has_clone_factory_member = detect_clone_factory_type_member<T>::value> +struct clonable_traits_impl; + +template <typename T> +struct clonable_traits_impl<T, false> { + using clone_factory_type = CloneFactory<T>; +}; + +template <typename T> +struct clonable_traits_impl<T, true> { + 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 <typename T> +struct clonable_traits : clonable_ptr_detail::clonable_traits_impl<T> {}; + +/** + * 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 <typename T, + typename CloneFactory = typename clonable_traits<T>::clone_factory_type, + template <typename, typename...> 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<CloneFactory, UniquePtr<T>> data; + + inline const CloneFactory& cloneFactory() const { + return std::get<0>(data); + } + + inline const UniquePtr<T>& ptr() const { + return std::get<1>(data); + } + + inline UniquePtr<T>& 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<T> clone_with_factory_impl(const T& copy, const CloneFactory& factory) { + return UniquePtr<T>{factory(copy)}; + } + + template <typename Pointerlike> + static inline UniquePtr<T> 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<T>&& p, + const CloneFactory* const f, + const internal_construction&) + : data(*f, std::move(p)) {} + + explicit inline clonable_ptr(UniquePtr<T>&& 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<CloneFactory>()}) && + noexcept(UniquePtr<T>{std::declval<UniquePtr<T>>()})) = default; + + /*! Moves a value, by pointer. Functions like `std::unique_ptr`. */ + inline clonable_ptr& operator=(clonable_ptr&&) & + noexcept(noexcept(std::declval<CloneFactory>() = std::declval<CloneFactory>()) && + noexcept(std::declval<UniquePtr<T>>() = std::declval<UniquePtr<T>>())) = 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 <typename CloneFactory_ = CloneFactory> + inline clonable_ptr( + typename std::enable_if<std::is_empty<CloneFactory_>::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 <typename CloneFactory_ = CloneFactory> + inline clonable_ptr(typename std::enable_if<!std::is_empty<CloneFactory_>::value, + std::nullptr_t>::type) = delete; + + /*! + * Constructs a pointer to nothing, with a default `CloneFactory`. + * This function is unavailable when `CloneFactory` is stateful. + */ + template <typename CloneFactory_ = CloneFactory, + typename = typename std::enable_if<std::is_empty<CloneFactory_>::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 <typename CloneFactory_ = CloneFactory> + explicit inline clonable_ptr( + typename std::enable_if<std::is_empty<CloneFactory_>::value, T* const>::type p) + : clonable_ptr(UniquePtr<T>{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 <typename CloneFactory_ = CloneFactory> + explicit inline clonable_ptr( + typename std::enable_if<!std::is_empty<CloneFactory_>::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<T* ()> cloner= [](const T& p){ return p; }; + * auto getCloner= [=]{ return cloner; }; + * clonable_ptr<T, std::function<T* ()>> 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<T>{p}, std::addressof(factory), internal_construction{}} {} + + /*! + * We forbid construction of a `clonable_ptr` from an unmanaged pointer, when specifying + * a cloning function -- regardless of whether the `CloneFactory` is stateful or not. + * NOTE: We have disabled this constructor, as the following is not exception safe + * code: + * ~~~ + * clonable_ptr<T, std::function<T* ()>> 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. More complicated cases are completely hidden from `clonable_ptr`'s + * inspection, thus making this constructor too dangerous to exist. + */ + explicit inline clonable_ptr(T* const p, CloneFactory&& factory) = delete; + + /*! + * Constructs a `nullptr` valued clonable pointer, with a specified `CloneFactory`, `factory`. + */ + explicit inline clonable_ptr(std::nullptr_t, CloneFactory&& factory) + : clonable_ptr{UniquePtr<T>{nullptr}, std::move(factory), internal_construction{}} {} + + /*! + * 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<T, std::function<T* ()>> good{std::make_unique<T>(), + * [](const T& p){ return p; }}; // GOOD IDEA!!! + * ~~~ + */ + template <typename CloneFactory_ = CloneFactory, + typename Derived, + typename = typename std::enable_if<std::is_empty<CloneFactory_>::value>::type> + inline clonable_ptr(UniquePtr<Derived> 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<T, std::function<T* ()>> good{std::make_unique<T>(), + * [](const T& p){ return p; }}; // GOOD IDEA!!! + * ~~~ + */ + template <typename Derived> + inline clonable_ptr(UniquePtr<Derived> 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<T> copy) & { + return *this = std::move(clonable_ptr{std::move(copy), this->cloneFactory()}); + } + + template <typename Derived> + 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 <typename FactoryType> + inline void setCloneFactory(FactoryType&& factory) & { + this->cloneFactory() = std::forward<FactoryType>(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 { + 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 { + 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<T>() && { + return std::move(this->ptr()); + } + + /*! Provides a constant `UniquePtr< T >` view of the object owned by `*this`. */ + inline operator const UniquePtr<T>&() const& { + return this->ptr(); + } + + /*! Provides a mutable `UniquePtr< T >` view of the object owned by `*this`. */ + inline operator UniquePtr<T>&() & { + return this->ptr(); + } + + /*! Provides a C-style `T *` pointer to the object owned by `*this`. */ + inline T* get() const { + return this->ptr().get(); + } + + inline void reset() & noexcept { + this->ptr().reset(); + } + + inline void reset(T* const p) & { + this->ptr().reset(p); + } + + // Equality + + inline friend bool operator==(const clonable_ptr& lhs, const clonable_ptr& rhs) { + return lhs._makeEqualityLens() == rhs._makeEqualityLens(); + } + + template <template <typename, typename...> class U, typename... UArgs> + inline friend bool operator==(const U<T, UArgs...>& lhs, const clonable_ptr& rhs) { + return lhs == rhs._makeEqualityLens(); + } + + template <template <typename, typename...> class U, typename... UArgs> + inline friend bool operator==(const clonable_ptr& lhs, const U<T, UArgs...>& rhs) { + return lhs._makeEqualityLens() == rhs; + } + + inline friend bool operator==(const std::nullptr_t& lhs, const clonable_ptr& rhs) { + return lhs == rhs._makeEqualityLens(); + } + + inline friend bool operator==(const clonable_ptr& lhs, const std::nullptr_t& rhs) { + return lhs._makeEqualityLens() == rhs; + } + + // Strict weak order + + inline friend bool operator<(const clonable_ptr& lhs, const clonable_ptr& rhs) { + return lhs._makeStrictWeakOrderLens() < rhs._makeStrictWeakOrderLens(); + } + + template <template <typename, typename...> class U, typename... UArgs> + inline friend bool operator<(const U<T, UArgs...>& lhs, const clonable_ptr& rhs) { + return lhs < rhs._makeStrictWeakOrderLens(); + } + + template <template <typename, typename...> class U, typename... UArgs> + inline friend bool operator<(const clonable_ptr& lhs, const U<T, UArgs...>& rhs) { + return lhs._makeStrictWeakOrderLens() < rhs; + } + + inline friend bool operator<(const std::nullptr_t& lhs, const clonable_ptr& rhs) { + return lhs < rhs._makeStrictWeakOrderLens(); + } + + inline friend bool operator<(const clonable_ptr& lhs, const std::nullptr_t& rhs) { + return lhs._makeStrictWeakOrderLens() < rhs; + } +}; + +// Inequality + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator!=(const clonable_ptr<C, F, U>& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs == rhs); +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator!=(const U<C, UArgs...>& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs == rhs); +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator!=(const clonable_ptr<C, F, U>& lhs, const U<C, UArgs...>& rhs) { + return !(lhs == rhs); +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator!=(const std::nullptr_t& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs == rhs); +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator!=(const clonable_ptr<C, F, U>& lhs, const std::nullptr_t& rhs) { + return !(lhs == rhs); +} + +// Greater than + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator>(const clonable_ptr<C, F, U>& lhs, const clonable_ptr<C, F, U>& rhs) { + return rhs < lhs; +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator>(const U<C, UArgs...>& lhs, const clonable_ptr<C, F, U>& rhs) { + return rhs < lhs; +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator>(const clonable_ptr<C, F, U>& lhs, const U<C, UArgs...>& rhs) { + return rhs < lhs; +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator>(const std::nullptr_t& lhs, const clonable_ptr<C, F, U>& rhs) { + return rhs < lhs; +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator>(const clonable_ptr<C, F, U>& lhs, const std::nullptr_t& rhs) { + return rhs < lhs; +} + +// Equal or Less + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator<=(const clonable_ptr<C, F, U>& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs > rhs); +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator<=(const U<C, UArgs...>& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs > rhs); +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator<=(const clonable_ptr<C, F, U>& lhs, const U<C, UArgs...>& rhs) { + return !(lhs > rhs); +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator<=(const std::nullptr_t& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs > rhs); +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator<=(const clonable_ptr<C, F, U>& lhs, const std::nullptr_t& rhs) { + return !(lhs > rhs); +} + +// Equal or greater + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator>=(const clonable_ptr<C, F, U>& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs < rhs); +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator>=(const U<C, UArgs...>& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs < rhs); +} + +template <typename C, typename F, template <typename, typename...> class U, typename... UArgs> +inline bool operator>=(const clonable_ptr<C, F, U>& lhs, const U<C, UArgs...>& rhs) { + return !(lhs < rhs); +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator>=(const std::nullptr_t& lhs, const clonable_ptr<C, F, U>& rhs) { + return !(lhs < rhs); +} + +template <typename C, typename F, template <typename, typename...> class U> +inline bool operator>=(const clonable_ptr<C, F, U>& lhs, const std::nullptr_t& rhs) { + return !(lhs < rhs); +} +} // namespace mongo diff --git a/src/mongo/base/clonable_ptr_test.cpp b/src/mongo/base/clonable_ptr_test.cpp new file mode 100644 index 00000000000..5554f1fbe6a --- /dev/null +++ b/src/mongo/base/clonable_ptr_test.cpp @@ -0,0 +1,974 @@ +// clonable_ptr_test.cpp + +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ + +#include "mongo/base/clonable_ptr.h" + +#include "mongo/stdx/functional.h" +#include "mongo/stdx/memory.h" +#include "mongo/unittest/unittest.h" + +#include <boost/lexical_cast.hpp> + +namespace { +namespace stdx = mongo::stdx; + +// These testing helper classes model various kinds of class which should be compatible with +// `mongo::clonable_ptr`. The basic use cases satisfied by each class are described in each class's +// documentation. + +// This class models the `Clonable` concept, and is used to test the simple case of `clonable_ptr`. +class ClonableTest { +private: + std::string data = + "This is the string data which is stored to make Functor Clonable need a complicated copy " + "ctor."; + +public: + std::unique_ptr<ClonableTest> clone() const { + return mongo::stdx::make_unique<ClonableTest>(); + } +}; + +// This class provides a member structure which models `CloneFactory<AltClonableTest>`. The member +// structure is available under the expected member name of `clone_factory_type`. The +// `CloneFactory` is stateless. +class AltClonableTest { +private: + std::string data = + "This is the string data which is stored to make Functor Clonable need a complicated copy " + "ctor."; + +public: + struct clone_factory_type { + std::unique_ptr<AltClonableTest> operator()(const AltClonableTest&) const { + return mongo::stdx::make_unique<AltClonableTest>(); + } + }; +}; + +// This class requires a companion cloning function models `CloneFactory<Alt2ClonableTest>`. There +// is an attendant specialization of the `mongo::clonable_traits` metafunction to provide the clone +// factory for this type. That `CloneFactory` is stateless. +class Alt2ClonableTest { +private: + std::string data = + "This is the string data which is stored to make Functor Clonable need a complicated copy " + "ctor."; +}; +} // namespace + +namespace mongo { +// This specialization of the `mongo::clonable_traits` metafunction provides a model of a stateless +// `CloneFactory<Alt2ClonableTest>` +template <> +struct clonable_traits<::Alt2ClonableTest> { + struct clone_factory_type { + std::unique_ptr<Alt2ClonableTest> operator()(const Alt2ClonableTest&) const { + return mongo::stdx::make_unique<Alt2ClonableTest>(); + } + }; +}; +} // namespace mongo + +namespace { +// This class uses a stateful cloning function provided by the `getCloningFunction` static member. +// This stateful `CloneFactory<FunctorClonable>` must be passed to constructors of the +// `cloning_ptr`. +class FunctorClonable { +private: + std::string data = + "This is the string data which is stored to make Functor Clonable need a complicated copy " + "ctor."; + +public: + using CloningFunctionType = + mongo::stdx::function<std::unique_ptr<FunctorClonable>(const FunctorClonable&)>; + + static CloningFunctionType getCloningFunction() { + return + [](const FunctorClonable& c) { return mongo::stdx::make_unique<FunctorClonable>(c); }; + } +}; + + +// This class uses a stateful cloning function provided by the `getCloningFunction` static member. +// This stateful `CloneFactory<FunctorWithDynamicStateClonable>` must be passed to constructors of +// the `cloning_ptr`. The `CloneFactory` for this type dynamically updates its internal state. +// This is used to test cloning of objects that have dynamically changing clone factories. +class FunctorWithDynamicStateClonable { +private: + std::string data = + "This is the string data which is stored to make Functor Clonable need a complicated copy " + "ctor."; + +public: + FunctorWithDynamicStateClonable(const FunctorWithDynamicStateClonable&) = delete; + FunctorWithDynamicStateClonable() = default; + + FunctorWithDynamicStateClonable(const std::string& s) : data(s) {} + + using CloningFunctionType = stdx::function<std::unique_ptr<FunctorWithDynamicStateClonable>( + const FunctorWithDynamicStateClonable&)>; + + static CloningFunctionType getCloningFunction() { + return [calls = 0](const FunctorWithDynamicStateClonable& c) mutable { + return mongo::stdx::make_unique<FunctorWithDynamicStateClonable>( + c.data + boost::lexical_cast<std::string>(++calls)); + }; + } +}; + +// This class models `Clonable`, with a return from clone which is +// `Constructible<std::unique_ptr<RawPointerClonable>>` but isn't +// `std::unique_ptr<RawPointerClonable>`. This is used to test that the `clonable_ptr` class does +// not expect `RawPointerClonable::clone() const` to return a model of +// `UniquePtr<RawPointerClonable>` +class RawPointerClonable { +public: + RawPointerClonable* clone() const { + return new RawPointerClonable; + } +}; + +// This class models `Clonable`, with a return from clone which is +// `Constructible<std::unique_ptr<UniquePtrClonable>>` because it is +// `std::unique_ptr<UniquePtrClonable>`. This is used to test that the `clonable_ptr` class can +// use a `UniquePtrClonable::clone() const` that returns a model of +// `UniquePtr<UniquePtrClonable>` +class UniquePtrClonable { +public: + std::unique_ptr<UniquePtrClonable> clone() const { + return mongo::stdx::make_unique<UniquePtrClonable>(); + } +}; + +TEST(ClonablePtrTest, syntax_smoke_test) { +// TODO: Either add a compressed pair type for optimization, or wait for MSVC to get this feature by +// default. MSVC doesn't make its tuple compressed, which causes this test to fail on MSVC. +#ifndef _MSC_VER + { + mongo::clonable_ptr<ClonableTest> p; + + p = mongo::stdx::make_unique<ClonableTest>(); + + mongo::clonable_ptr<ClonableTest> p2 = p; + + ASSERT_TRUE(p != p2); + + static_assert(sizeof(p) == sizeof(ClonableTest*), + "`mongo::clonable_ptr< T >` should be `sizeof` a pointer when there is no " + "CloneFactory"); + } +#endif + + { + mongo::clonable_ptr<AltClonableTest> p; + + p = mongo::stdx::make_unique<AltClonableTest>(); + + mongo::clonable_ptr<AltClonableTest> p2 = p; + + ASSERT_TRUE(p != p2); + } + + { + mongo::clonable_ptr<Alt2ClonableTest> p; + + p = mongo::stdx::make_unique<Alt2ClonableTest>(); + + mongo::clonable_ptr<Alt2ClonableTest> p2 = p; + + ASSERT_TRUE(p != p2); + } + + { + mongo::clonable_ptr<FunctorClonable, FunctorClonable::CloningFunctionType> p{ + FunctorClonable::getCloningFunction()}; + + p = mongo::stdx::make_unique<FunctorClonable>(); + + mongo::clonable_ptr<FunctorClonable, FunctorClonable::CloningFunctionType> p2 = p; + + ASSERT_TRUE(p != p2); + + mongo::clonable_ptr<FunctorClonable, FunctorClonable::CloningFunctionType> p3{ + FunctorClonable::getCloningFunction()}; + + auto tmp = mongo::stdx::make_unique<FunctorClonable>(); + p3 = std::move(tmp); + + ASSERT_TRUE(p != p2); + ASSERT_TRUE(p2 != p3); + ASSERT_TRUE(p != p3); + } +} + +// These tests check that all expected valid syntactic forms of use for the +// `mongo::clonable_ptr<Clonable>` are valid. These tests assert nothing but provide a single +// unified place to check the syntax of this component. Build failures in these parts indicate that +// a change to the component has broken an expected valid syntactic usage. Any expected valid usage +// which is not in this list should be added. +namespace SyntaxTests { +template <typename Clonable> +void construction() { + // Test default construction + { mongo::clonable_ptr<Clonable>{}; } + + // Test construction from a nullptr + { mongo::clonable_ptr<Clonable>{nullptr}; } + + // Test construction from a Clonable pointer. + { + Clonable* const local = nullptr; + mongo::clonable_ptr<Clonable>{local}; + } + + // Test move construction. + { mongo::clonable_ptr<Clonable>{mongo::clonable_ptr<Clonable>{}}; } + + // Test copy construction. + { + mongo::clonable_ptr<Clonable> a; + mongo::clonable_ptr<Clonable> b{a}; + } + + // Test move assignment. + { + mongo::clonable_ptr<Clonable> a; + a = mongo::clonable_ptr<Clonable>{}; + } + + // Test copy assignment. + { + mongo::clonable_ptr<Clonable> a; + mongo::clonable_ptr<Clonable> b; + b = a; + } + + // Test unique pointer construction + { mongo::clonable_ptr<Clonable>{mongo::stdx::make_unique<Clonable>()}; } + + // Test unique pointer construction (conversion) + { + auto acceptor = [](const mongo::clonable_ptr<Clonable>&) {}; + acceptor(mongo::stdx::make_unique<Clonable>()); + } + + // Test non-conversion pointer construction + { static_assert(!std::is_convertible<Clonable*, mongo::clonable_ptr<Clonable>>::value, ""); } + + // Test conversion unique pointer construction + { + static_assert( + std::is_convertible<std::unique_ptr<Clonable>, mongo::clonable_ptr<Clonable>>::value, + ""); + } +} + +// Tests that syntactic forms that require augmented construction are proper +template <typename Clonable, typename CloneFactory> +void augmentedConstruction() { + // Test default construction + { + static_assert( + !std::is_default_constructible<mongo::clonable_ptr<Clonable, CloneFactory>>::value, ""); + } + + // Test Clone Factory construction + { mongo::clonable_ptr<Clonable, CloneFactory>{Clonable::getCloningFunction()}; } + +// TODO: Revist this when MSVC's enable-if and deletion on ctors works. +#ifndef _MSC_VER + // Test non-construction from a nullptr + { + static_assert(!std::is_constructible<mongo::clonable_ptr<Clonable, CloneFactory>, + std::nullptr_t>::value, + ""); + } +#endif + + // Test construction from a nullptr with factory + { mongo::clonable_ptr<Clonable, CloneFactory>{nullptr, Clonable::getCloningFunction()}; } + +// TODO: Revist this when MSVC's enable-if and deletion on ctors works. +#ifndef _MSC_VER + // Test construction from a raw Clonable pointer. + { + static_assert( + !std::is_constructible<mongo::clonable_ptr<Clonable, CloneFactory>, Clonable*>::value, + ""); + } +#endif + + + // Test initialization of a raw Clonable pointer with factory, using reset. + { + Clonable* const local = nullptr; + mongo::clonable_ptr<Clonable, CloneFactory> p{Clonable::getCloningFunction()}; + p.reset(local); + } + + // Test move construction. + { + mongo::clonable_ptr<Clonable, CloneFactory>{ + mongo::clonable_ptr<Clonable, CloneFactory>{Clonable::getCloningFunction()}}; + } + + // Test copy construction. + { + mongo::clonable_ptr<Clonable, CloneFactory> a{Clonable::getCloningFunction()}; + mongo::clonable_ptr<Clonable, CloneFactory> b{a}; + } + + // Test augmented copy construction. + { + mongo::clonable_ptr<Clonable, CloneFactory> a{Clonable::getCloningFunction()}; + mongo::clonable_ptr<Clonable, CloneFactory> b{a, Clonable::getCloningFunction()}; + } + + + // Test move assignment. + { + mongo::clonable_ptr<Clonable, CloneFactory> a{Clonable::getCloningFunction()}; + a = mongo::clonable_ptr<Clonable, CloneFactory>{Clonable::getCloningFunction()}; + } + + // Test copy assignment. + { + mongo::clonable_ptr<Clonable, CloneFactory> a{Clonable::getCloningFunction()}; + mongo::clonable_ptr<Clonable, CloneFactory> b{Clonable::getCloningFunction()}; + b = a; + } + + // Test unique pointer construction + { + mongo::clonable_ptr<Clonable, CloneFactory>{mongo::stdx::make_unique<Clonable>(), + Clonable::getCloningFunction()}; + } + + // Test augmented unique pointer construction + { + mongo::clonable_ptr<Clonable, CloneFactory>{mongo::stdx::make_unique<Clonable>(), + Clonable::getCloningFunction()}; + } + + // Test non-conversion pointer construction + { + static_assert( + !std::is_convertible<mongo::clonable_ptr<Clonable, CloneFactory>, Clonable*>::value, + ""); + } + + // Test non-conversion from factory + { + static_assert( + !std::is_convertible<mongo::clonable_ptr<Clonable, CloneFactory>, CloneFactory>::value, + ""); + } + + // Test conversion unique pointer construction + { + static_assert(!std::is_convertible<std::unique_ptr<Clonable>, + mongo::clonable_ptr<Clonable, CloneFactory>>::value, + ""); + } +} + +template <typename Clonable> +void pointerOperations() { + mongo::clonable_ptr<Clonable> a; + + // Test `.get()` functionality: + { + Clonable* p = a.get(); + (void)p; + } + + // Test `->` functionality + { + // We don't actually want to call the dtor, but we want the compiler to check that we + // have. + if (false) { + a->~Clonable(); + } + } + + // Test `*` functionality + { + Clonable& r = *a; + (void)r; + } + + // Test reset functionality + { + a.reset(); + a.reset(nullptr); + a.reset(new Clonable); + } +} + +template <typename Clonable> +void equalityOperations() { + mongo::clonable_ptr<Clonable> a; + mongo::clonable_ptr<Clonable> b; + + std::unique_ptr<Clonable> ua; + + // Test equality expressions + { + (void)(a == a); + (void)(a == b); + (void)(b == a); + + (void)(a == ua); + (void)(ua == a); + + (void)(nullptr == a); + (void)(a == nullptr); + } + + // Test inequality expressions + { + (void)(a != a); + (void)(a != b); + (void)(b != a); + + (void)(a != ua); + (void)(ua != a); + + (void)(nullptr == a); + (void)(a == nullptr); + } +} +} // namespace SyntaxTests + +TEST(ClonablePtrSyntaxTests, construction) { + SyntaxTests::construction<ClonableTest>(); + SyntaxTests::construction<AltClonableTest>(); + SyntaxTests::construction<Alt2ClonableTest>(); + SyntaxTests::construction<RawPointerClonable>(); + SyntaxTests::construction<UniquePtrClonable>(); +} + +TEST(ClonablePtrSyntaxTests, augmentedConstruction) { + SyntaxTests::augmentedConstruction<FunctorClonable, FunctorClonable::CloningFunctionType>(); + SyntaxTests::augmentedConstruction<FunctorWithDynamicStateClonable, + FunctorWithDynamicStateClonable::CloningFunctionType>(); +} + +TEST(ClonablePtrSyntaxTests, pointerOperations) { + SyntaxTests::pointerOperations<ClonableTest>(); + SyntaxTests::pointerOperations<AltClonableTest>(); + SyntaxTests::pointerOperations<Alt2ClonableTest>(); + SyntaxTests::pointerOperations<RawPointerClonable>(); + SyntaxTests::pointerOperations<UniquePtrClonable>(); +} + +TEST(ClonablePtrSyntaxTests, equalityOperations) { + SyntaxTests::equalityOperations<ClonableTest>(); + SyntaxTests::equalityOperations<AltClonableTest>(); + SyntaxTests::equalityOperations<Alt2ClonableTest>(); + SyntaxTests::equalityOperations<RawPointerClonable>(); + SyntaxTests::equalityOperations<UniquePtrClonable>(); +} + +namespace BehaviorTests { +class DetectDestruction { +public: + static int activeCount; + + static void resetCount() { + activeCount = 0; + } + + ~DetectDestruction() { + --activeCount; + } + + DetectDestruction(const DetectDestruction&) { + ++activeCount; + } + + DetectDestruction& operator=(const DetectDestruction&) = delete; + + DetectDestruction(DetectDestruction&&) = delete; + DetectDestruction& operator=(DetectDestruction&&) = delete; + + DetectDestruction() { + ++activeCount; + } + + std::unique_ptr<DetectDestruction> clone() const { + return mongo::stdx::make_unique<DetectDestruction>(*this); + } +}; + +int DetectDestruction::activeCount = 0; + +struct DestructionGuard { + DestructionGuard(const DestructionGuard&) = delete; + DestructionGuard& operator=(const DestructionGuard&) = delete; + + DestructionGuard() { + DetectDestruction::resetCount(); + } + + ~DestructionGuard() { + ASSERT_EQ(DetectDestruction::activeCount, 0); + } +}; + +TEST(ClonablePtrTest, basic_construction_test) { + // Do not default construct the object + { + DestructionGuard check; + mongo::clonable_ptr<DetectDestruction> p; + ASSERT_EQ(DetectDestruction::activeCount, 0); + } + + // Do not make unnecessary copies of the object from ptr + { + DestructionGuard check; + mongo::clonable_ptr<DetectDestruction> p{new DetectDestruction}; + ASSERT_EQ(DetectDestruction::activeCount, 1); + } + + // Do not make unnecessary copies of the object from unique_ptr + { + DestructionGuard check; + mongo::clonable_ptr<DetectDestruction> p{mongo::stdx::make_unique<DetectDestruction>()}; + ASSERT_EQ(DetectDestruction::activeCount, 1); + } + { + DestructionGuard check; + mongo::clonable_ptr<DetectDestruction> p = mongo::stdx::make_unique<DetectDestruction>(); + ASSERT_EQ(DetectDestruction::activeCount, 1); + } + + // Two separate constructions are unlinked + { + DestructionGuard check; + + mongo::clonable_ptr<DetectDestruction> p1{mongo::stdx::make_unique<DetectDestruction>()}; + ASSERT_EQ(DetectDestruction::activeCount, 1); + + { + mongo::clonable_ptr<DetectDestruction> p2{ + mongo::stdx::make_unique<DetectDestruction>()}; + ASSERT_EQ(DetectDestruction::activeCount, 2); + } + ASSERT_EQ(DetectDestruction::activeCount, 1); + } + + // Two separate constructions can have opposite order and be unlinked + { + DestructionGuard check; + + auto p1 = mongo::stdx::make_unique<mongo::clonable_ptr<DetectDestruction>>( + mongo::stdx::make_unique<DetectDestruction>()); + ASSERT_EQ(DetectDestruction::activeCount, 1); + + auto p2 = mongo::stdx::make_unique<mongo::clonable_ptr<DetectDestruction>>( + mongo::stdx::make_unique<DetectDestruction>()); + ASSERT_EQ(DetectDestruction::activeCount, 2); + + p1.reset(); + ASSERT_EQ(DetectDestruction::activeCount, 1); + + p2.reset(); + ASSERT_EQ(DetectDestruction::activeCount, 0); + } +} + +// TODO: Bring in an "equivalence class for equality predicate testing" framework. +// Equals and Not Equals need to be tested independently -- It is not valid to assume that equals +// and not equals are correctly implemented as complimentary predicates. Equality must be +// reflexive, symmetric and transitive. This requres several instances that all have the same +// value. Simply testing "2 == 2" and "3 != 2" is insufficient. Every combination of position and +// equality must be tested to come out as expected. +// +// Consider that with equality it is important to make sure that `a == b` has the same meaning as `b +// == a`. It is also necessary to check that `a == b` and `b == c` and `a == c` is true, when all +// three are equal, and to do so in reverse: `b == a` and `c == b` and `c == a`. Further, the +// relationships above have to hold for multiple cases. Similar cases need to be tested for +// inequality. +// +// Further, equality is an incredibly important operation to test completely and thoroughly -- +// besides being a critical element in code using any value modeling type, it also is the keystone +// in any testing schedule for a copyable and movable value type. Almost all testing of behavior +// relies upon being able to detect fundamental differences in value. In order to provide this +// correctly, we provide a full battery of tests for equality in all mathematically relevant +// situations. For this equality testing schedule to be correct, we require a mechanism to +// initialize objects (and references to those objects) which have predictable value. These +// predictable values are then used to test known equality expressions for the correct evaluation. +// +// All other tests can then just use equality to verify that an object is in the desired state. +// This greatly simplifies testing and also makes tests more precise. +TEST(ClonablePtrTest, basicEqualityTest) { + DestructionGuard check; + + mongo::clonable_ptr<DetectDestruction> n1; + mongo::clonable_ptr<DetectDestruction> n2; + mongo::clonable_ptr<DetectDestruction> n3; + + mongo::clonable_ptr<DetectDestruction> a = mongo::stdx::make_unique<DetectDestruction>(); + mongo::clonable_ptr<DetectDestruction> b = mongo::stdx::make_unique<DetectDestruction>(); + mongo::clonable_ptr<DetectDestruction> c = mongo::stdx::make_unique<DetectDestruction>(); + + const mongo::clonable_ptr<DetectDestruction>& ap = a; + const mongo::clonable_ptr<DetectDestruction>& bp = b; + const mongo::clonable_ptr<DetectDestruction>& cp = c; + const mongo::clonable_ptr<DetectDestruction>& ap2 = a; + const mongo::clonable_ptr<DetectDestruction>& bp2 = b; + const mongo::clonable_ptr<DetectDestruction>& cp2 = c; + + // Equals operator + + // Identity checks + + ASSERT(n1 == n1); + ASSERT(n2 == n2); + ASSERT(n3 == n3); + + ASSERT(a == a); + ASSERT(b == b); + ASSERT(c == c); + + // Same value checks. (Because unique pointers should never be the same value, we have to use + // references.) + + ASSERT(n1 == n2); + ASSERT(n1 == n3); + + ASSERT(n2 == n1); + ASSERT(n2 == n3); + + ASSERT(n3 == n1); + ASSERT(n3 == n2); + + ASSERT(a == ap); + ASSERT(a == ap2); + ASSERT(ap == a); + ASSERT(ap == ap2); + ASSERT(ap2 == a); + ASSERT(ap2 == ap); + + ASSERT(b == bp); + ASSERT(b == bp2); + ASSERT(bp == b); + ASSERT(bp == bp2); + ASSERT(bp2 == b); + ASSERT(bp2 == bp); + + ASSERT(c == cp); + ASSERT(c == cp2); + ASSERT(cp == c); + ASSERT(cp == cp2); + ASSERT(cp2 == c); + ASSERT(cp2 == cp); + + // Different value checks: + + ASSERT(!(a == n1)); + ASSERT(!(b == n1)); + ASSERT(!(c == n1)); + ASSERT(!(a == n2)); + ASSERT(!(b == n2)); + ASSERT(!(c == n2)); + + ASSERT(!(n1 == a)); + ASSERT(!(n1 == b)); + ASSERT(!(n1 == c)); + ASSERT(!(n2 == a)); + ASSERT(!(n2 == b)); + ASSERT(!(n2 == c)); + + ASSERT(!(a == b)); + ASSERT(!(a == c)); + + ASSERT(!(b == a)); + ASSERT(!(b == c)); + + ASSERT(!(c == a)); + ASSERT(!(c == b)); + + // Not Equals operator + + // Identity checks + + ASSERT(!(n1 != n1)); + ASSERT(!(n2 != n2)); + ASSERT(!(n3 != n3)); + + ASSERT(!(a != a)); + ASSERT(!(b != b)); + ASSERT(!(c != c)); + + // Same value checks. (Because unique pointers should never be the same value, we have to use + // references.) + + ASSERT(!(n1 != n2)); + ASSERT(!(n1 != n3)); + + ASSERT(!(n2 != n1)); + ASSERT(!(n2 != n3)); + + ASSERT(!(n3 != n1)); + ASSERT(!(n3 != n2)); + + ASSERT(!(a != ap)); + ASSERT(!(a != ap2)); + ASSERT(!(ap != a)); + ASSERT(!(ap != ap2)); + ASSERT(!(ap2 != a)); + ASSERT(!(ap2 != ap)); + + ASSERT(!(b != bp)); + ASSERT(!(b != bp2)); + ASSERT(!(bp != b)); + ASSERT(!(bp != bp2)); + ASSERT(!(bp2 != b)); + ASSERT(!(bp2 != bp)); + + ASSERT(!(c != cp)); + ASSERT(!(c != cp2)); + ASSERT(!(cp != c)); + ASSERT(!(cp != cp2)); + ASSERT(!(cp2 != c)); + ASSERT(!(cp2 != cp)); + + // Different value checks: + + ASSERT(a != n1); + ASSERT(b != n1); + ASSERT(c != n1); + ASSERT(a != n2); + ASSERT(b != n2); + ASSERT(c != n2); + + ASSERT(n1 != a); + ASSERT(n1 != b); + ASSERT(n1 != c); + ASSERT(n2 != a); + ASSERT(n2 != b); + ASSERT(n2 != c); + + ASSERT(a != b); + ASSERT(a != c); + + ASSERT(b != a); + ASSERT(b != c); + + ASSERT(c != a); + ASSERT(c != b); +} + +// TODO: all other forms of equality with other types (`std::nullptr_t` and `std::unique_ptr< T >`) +// need testing still. + +TEST(ClonablePtrTest, ownershipStabilityTest) { + { + DestructionGuard check; + + auto ptr_init = mongo::stdx::make_unique<DetectDestruction>(); + const auto* rp = ptr_init.get(); + + mongo::clonable_ptr<DetectDestruction> cp = std::move(ptr_init); + + ASSERT(rp == cp.get()); + + mongo::clonable_ptr<DetectDestruction> cp2 = std::move(cp); + + ASSERT(rp == cp2.get()); + + mongo::clonable_ptr<DetectDestruction> cp3; + + ASSERT(nullptr == cp3); + + cp3 = std::move(cp2); + + ASSERT(rp == cp3.get()); + } + + { + auto ptr_init = mongo::stdx::make_unique<DetectDestruction>(); + const auto* rp = ptr_init.get(); + + mongo::clonable_ptr<DetectDestruction> cp{ptr_init.release()}; + + ASSERT(rp == cp.get()); + + mongo::clonable_ptr<DetectDestruction> cp2 = std::move(cp); + + ASSERT(rp == cp2.get()); + + mongo::clonable_ptr<DetectDestruction> cp3; + + ASSERT(nullptr == cp3.get()); + + cp3 = std::move(cp2); + + ASSERT(rp == cp3.get()); + } +} + +class ClonableObject { +private: + int value = 0; + +public: + // ClonableObject( const ClonableObject & ) { abort(); } + ClonableObject() = default; + explicit ClonableObject(const int v) : value(v) {} + + std::unique_ptr<ClonableObject> clone() const { + return mongo::stdx::make_unique<ClonableObject>(*this); + } + + auto make_equality_lens() const -> decltype(std::tie(this->value)) { + return std::tie(value); + } +}; + +bool operator==(const ClonableObject& lhs, const ClonableObject& rhs) { + return lhs.make_equality_lens() == rhs.make_equality_lens(); +} + +bool operator!=(const ClonableObject& lhs, const ClonableObject& rhs) { + return !(lhs == rhs); +} + +TEST(ClonablePtrTest, noObjectCopySemanticTest) { + mongo::clonable_ptr<ClonableObject> p; + + mongo::clonable_ptr<ClonableObject> p2 = p; + ASSERT(p == p2); + + mongo::clonable_ptr<ClonableObject> p3; + + p3 = p; + ASSERT(p == p3); +} + +TEST(ClonablePtrTest, objectCopySemanticTest) { + mongo::clonable_ptr<ClonableObject> p = mongo::stdx::make_unique<ClonableObject>(1); + mongo::clonable_ptr<ClonableObject> q = mongo::stdx::make_unique<ClonableObject>(2); + ASSERT(p != q); + ASSERT(*p != *q); + + mongo::clonable_ptr<ClonableObject> p2 = p; + ASSERT(p != p2); + ASSERT(*p == *p2); + + mongo::clonable_ptr<ClonableObject> q2 = q; + ASSERT(q2 != q); + ASSERT(q2 != p); + ASSERT(q2 != p2); + ASSERT(*q2 == *q); + + q2 = p2; + ASSERT(q2 != q); + ASSERT(q2 != p); + ASSERT(q2 != p2); + ASSERT(*q2 == *p2); +} + +class Interface { +public: + virtual ~Interface() = default; + virtual void consumeText(const std::string& message) = 0; + virtual std::string produceText() = 0; + + std::unique_ptr<Interface> clone() const { + return std::unique_ptr<Interface>{this->clone_impl()}; + } + +private: + virtual Interface* clone_impl() const = 0; +}; + +class GeneratorImplementation : public Interface { +private: + const std::string root; + int generation = 0; + + GeneratorImplementation* clone_impl() const { + return new GeneratorImplementation{*this}; + } + +public: + explicit GeneratorImplementation(const std::string& m) : root(m) {} + + void consumeText(const std::string&) override {} + + std::string produceText() override { + return root + boost::lexical_cast<std::string>(++generation); + } +}; + +class StorageImplementation : public Interface { +private: + std::string store; + + StorageImplementation* clone_impl() const { + return new StorageImplementation{*this}; + } + +public: + void consumeText(const std::string& m) override { + store = m; + } + std::string produceText() override { + return store; + } +}; + + +TEST(ClonablePtrSimpleTest, simpleUsageExample) { + mongo::clonable_ptr<Interface> source; + mongo::clonable_ptr<Interface> sink; + + mongo::clonable_ptr<Interface> instance = stdx::make_unique<StorageImplementation>(); + + sink = instance; + + ASSERT(instance.get() != sink.get()); + + instance = stdx::make_unique<GeneratorImplementation>("base message"); + + + source = std::move(instance); + + + sink->consumeText(source->produceText()); +} + +} // namespace BehaviorTests +} // namespace diff --git a/src/mongo/base/concept/README b/src/mongo/base/concept/README new file mode 100644 index 00000000000..bc2c74fabe4 --- /dev/null +++ b/src/mongo/base/concept/README @@ -0,0 +1,5 @@ +This directory contains a number of C++ headers which describe concepts for +C++ types used by and with the Mongo library. These headers do not describe +instantiable types, but instead are used to concisely describe models of C++ +type concepts. When C++ grows a proper `Concepts` feature, these files can be +converted to proper concepts. diff --git a/src/mongo/base/concept/assignable.h b/src/mongo/base/concept/assignable.h new file mode 100644 index 00000000000..00a8fbfad1e --- /dev/null +++ b/src/mongo/base/concept/assignable.h @@ -0,0 +1,40 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 "mongo/base/concept/copy_assignable.h" +#include "mongo/base/concept/copy_constructible.h" + +namespace mongo { +namespace concept { +/*! + * The Assignable concept models a type which can be copy assigned and copy constructed. + */ +struct Assignable : CopyConstructible, CopyAssignable {}; +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/clonable.h b/src/mongo/base/concept/clonable.h new file mode 100644 index 00000000000..71bd3590b33 --- /dev/null +++ b/src/mongo/base/concept/clonable.h @@ -0,0 +1,47 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 "mongo/base/concept/constructible.h" +#include "mongo/base/concept/unique_ptr.h" + +namespace mongo { +namespace concept { +/*! + * Objects conforming to the Clonable concept can be dynamically copied, using `this->clone()`. + * The Clonable concept does not specify the return type of the `clone()` function. + */ +struct Clonable { + /*! Clonable objects must be safe to destroy, by pointer. */ + virtual ~Clonable() noexcept = 0; + + /*! Clonable objects can be cloned without knowing the actual dynamic type. */ + Constructible<UniquePtr<Clonable>> clone() const; +}; +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/clone_factory.h b/src/mongo/base/concept/clone_factory.h new file mode 100644 index 00000000000..ae330ade081 --- /dev/null +++ b/src/mongo/base/concept/clone_factory.h @@ -0,0 +1,48 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 "mongo/base/concept/assignable.h" +#include "mongo/base/concept/constructible.h" +#include "mongo/base/concept/unique_ptr.h" + +namespace mongo { +namespace concept { +/*! + * Objects conforming to the `CloneFactory` concept are function-like constructs which return + * objects that are dynamically allocated copies of their inputs. + * These copies can be made without knowing the actual dynamic type. The `CloneFactory` type itself + * must be `Assignable`, in that it can be used with automatically generated copy constructors and + * copy assignment operators. + */ +template <typename T> +struct CloneFactory : Assignable { + Constructible<UniquePtr<T>> operator()(const T*) const; +}; +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/constructible.h b/src/mongo/base/concept/constructible.h new file mode 100644 index 00000000000..d857efdd292 --- /dev/null +++ b/src/mongo/base/concept/constructible.h @@ -0,0 +1,65 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 <type_traits> + +#include "mongo/stdx/type_traits.h" + +namespace mongo { +namespace concept { + +/** + * The Constructable trait indicates whether `T` is constructible from `Constructible`. + * + * RETURNS: true if `T{ std::declval< Constructible >() }` is a valid expression and false + * otherwise. + */ +template <typename T, typename Constructible, typename = void> +struct is_constructible : std::false_type {}; + +template <typename T, typename Constructible> +struct is_constructible<T, + Constructible, + stdx::void_t<decltype(T{std::declval<Constructible<T>>()})>> + : std::true_type {}; + +/** + * The Constructable concept models a type which can be passed to a single-argument constructor of + * `T`. + * This is not possible to describe in the type `Constructible`. + * + * The expression: `T{ std::declval< Constructible< T > >() }` should be valid. + * + * This concept is more broadly applicable than `ConvertibleTo`. `ConvertibleTo` uses implicit + * conversion, whereas `Constructible` uses direct construction. + */ +template <typename T> +struct Constructible {}; +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/convertible_to.h b/src/mongo/base/concept/convertible_to.h new file mode 100644 index 00000000000..ca907d86a57 --- /dev/null +++ b/src/mongo/base/concept/convertible_to.h @@ -0,0 +1,41 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 + +namespace mongo { +namespace concept { +/** + * The ConvertibleTo concept models a type which can be converted implicitly into a `T`. + * The code: `T x; x= ConvertibleTo< T >{};` should be valid. + */ +template <typename T> +struct ConvertibleTo { + operator T(); +} +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/copy_assignable.h b/src/mongo/base/concept/copy_assignable.h new file mode 100644 index 00000000000..e46e2c7af18 --- /dev/null +++ b/src/mongo/base/concept/copy_assignable.h @@ -0,0 +1,45 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 + +namespace mongo { +namespace concept { +/** + * The CopyAssignable concept models a type which can be copy assigned. + * + * The expression: `copyAssignable= copyAssignable` should be valid. + */ +struct CopyAssignable { + /** + * The copy assignment operator is required by `CopyAssignable`. + * NOTE: Copy Assignment is only required on lvalue targets of `CopyAssignable`. + */ + CopyAssignable& operator=(const CopyAssignable&) &; +}; +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/copy_constructible.h b/src/mongo/base/concept/copy_constructible.h new file mode 100644 index 00000000000..e2850c70384 --- /dev/null +++ b/src/mongo/base/concept/copy_constructible.h @@ -0,0 +1,41 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 + +namespace mongo { +namespace concept { +/** + * The CopyConstructable concept models a type which can be copy constructed. + * + * The expression: `CopyConstructible{ copyConstructible }` should be valid. + */ +struct CopyConstructible { + CopyConstructible(const CopyConstructible&); +}; +} // namespace concept +} // namespace mongo diff --git a/src/mongo/base/concept/unique_ptr.h b/src/mongo/base/concept/unique_ptr.h new file mode 100644 index 00000000000..13ca5495e44 --- /dev/null +++ b/src/mongo/base/concept/unique_ptr.h @@ -0,0 +1,68 @@ +/*- + * 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 <http://www.gnu.org/licenses/>. + * + * 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 "mongo/base/concept/convertible_to.h" + +namespace mongo { +namespace concept { +/** + * The `UniquePtr` Concept models a movable owning pointer of an object. + * `std::unique_ptr< T >` is a model of `mongo::concept::UniquePtr< T >`. + */ +template <typename T> +struct UniquePtr { + /** The `UniquePtr< T >` must retire its pointer to `T` on destruction. */ + ~UniquePtr() noexcept; + + UniquePtr(UniquePtr&& p); + UniquePtr& operator=(UniquePtr&& p); + + UniquePtr(); + UniquePtr(T* p); + + ConvertibleTo<T*> operator->() const; + T& operator*() const; + + explicit operator bool() const; + + ConvertibleTo<T*> get() const; + + void reset() noexcept; + void reset(ConvertibleTo<T*>); +}; + +/*! A `UniquePtr` object must be equality comparable. */ +template <typename T> +bool operator==(const UniquePtr<T>& lhs, const UniquePtr<T>& rhs); + +/*! A `UniquePtr` object must be inequality comparable. */ +template <typename T> +bool operator!=(const UniquePtr<T>& lhs, const UniquePtr<T>& rhs); +} // namespace concept +} // namespace mongo |