summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorADAM David Alan Martin <adam.martin@10gen.com>2017-03-30 15:11:03 -0400
committerADAM David Alan Martin <adam.martin@10gen.com>2017-03-30 15:11:03 -0400
commitc20914d6739c8474e80646e461b8f5121f9758cf (patch)
tree23715ed58d712356c12e5c576b4180a72a9182df
parent1fecd837ad6e7d0913a9e65789fbfb6c2c74304a (diff)
downloadmongo-c20914d6739c8474e80646e461b8f5121f9758cf.tar.gz
SERVER-26025 Smart pointer with clone on copy
This clonable pointer works like a unique_ptr, but upon copy it invokes a customizable cloning function. A `clone` member function is used by default. There exist several customization points.
-rw-r--r--src/mongo/base/SConscript9
-rw-r--r--src/mongo/base/clonable_ptr.h648
-rw-r--r--src/mongo/base/clonable_ptr_test.cpp974
-rw-r--r--src/mongo/base/concept/README5
-rw-r--r--src/mongo/base/concept/assignable.h40
-rw-r--r--src/mongo/base/concept/clonable.h47
-rw-r--r--src/mongo/base/concept/clone_factory.h48
-rw-r--r--src/mongo/base/concept/constructible.h65
-rw-r--r--src/mongo/base/concept/convertible_to.h41
-rw-r--r--src/mongo/base/concept/copy_assignable.h45
-rw-r--r--src/mongo/base/concept/copy_constructible.h41
-rw-r--r--src/mongo/base/concept/unique_ptr.h68
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