diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp index 7bc90d50c4..5f07376cb1 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp @@ -14,52 +14,124 @@ #pragma once -#include - -#if defined(BSONCXX_POLY_USE_MNMLSTC) - -#include - -namespace bsoncxx { -BSONCXX_INLINE_NAMESPACE_BEGIN -namespace stdx { - -using ::core::make_unique; - -} // namespace stdx -BSONCXX_INLINE_NAMESPACE_END -} // namespace bsoncxx - -#elif defined(BSONCXX_POLY_USE_BOOST) - -#include - -namespace bsoncxx { -BSONCXX_INLINE_NAMESPACE_BEGIN -namespace stdx { - -using ::boost::make_unique; - -} // namespace stdx -BSONCXX_INLINE_NAMESPACE_END -} // namespace bsoncxx - -#elif __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) - +#include #include +#include +#include + +#include namespace bsoncxx { BSONCXX_INLINE_NAMESPACE_BEGIN namespace stdx { -using ::std::make_unique; +namespace detail { + +// Switch backend of make_unique by the type we are creating. +// It would be easier to 'if constexpr' on whether we are an array and whether to direct-init or +// value-init, but we don't have if-constexpr and we need it to guard against an uterance of a +// possibly-illegal 'new' expression. +template +struct make_unique_impl { + // For make_unique: + template ()...))> + static std::unique_ptr make(std::true_type /* direct-init */, Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + // For make_unique_for_overwrite: + template + static std::unique_ptr make(std::false_type /* value-init */) { + return std::unique_ptr(new T); + } +}; + +// For unbounded arrays: +template +struct make_unique_impl { + template ()])> + static std::unique_ptr make(ShouldDirectInit, std::size_t count) { + // These can share a function via a plain if, because both new expressions + // must be semantically valid + if (ShouldDirectInit()) { + return std::unique_ptr(new Elem[count]()); + } else { + return std::unique_ptr(new Elem[count]); + } + } +}; + +// Bounded arrays are disallowed: +template +struct make_unique_impl {}; + +// References are nonsense: +template +struct make_unique_impl {}; + +// References are nonsense: +template +struct make_unique_impl {}; + +} // namespace detail + +/** + * @brief Create a new std::unique_ptr that points-to the given type, direct-initialized based on + * the given arguments. + * + * @tparam T Any non-array object type or any array of unknown bound. + * @param args If T is a non-array object type, these are the constructor arguments used to + * direct-initialize the object. If T is an array of unknown bound, then the sole argument must be a + * single std::size_t that specifies the number of objects to allocate in the array. + * + * Requires: + * - If T is an array of unknown bounds, then args... must be a single size_t and the element type + * of T must be value-initializable. + * - Otherwise, if T is a non-array object type, then T must be direct-initializable with arguments + * `args...` + * - Otherwise, this function is excluded from overload resolution. + */ +template , + typename = decltype(Impl::make(std::true_type{}, std::declval()...))> +std::unique_ptr make_unique(Args&&... args) { + return Impl::make(std::true_type{}, std::forward(args)...); +} + +/** + * @brief Create a new std::unique_ptr that points-to a default-initialized instance of the given + * type. + * + * @tparam T A non-array object type or an array of unknown bound + * @param args If T is an object type, then args... must no arguments are allowed. + * If T is an array of unknown bound, then args... must be a single size_t specifying + * the length of the array to allocate. + * + * Requires: + * - T must be default-initializable + * - If T is an array of unknown bounds, then args... must be a single size_t + * - Otherwise, if T is a non-array object type, args... must be empty + * - Otherwise, this function is excluded from overload resolution + */ +template , + typename = decltype(Impl::make(std::false_type{}, std::declval()...))> +std::unique_ptr make_unique_for_overwrite(Args&&... args) { + return Impl::make(std::false_type{}, std::forward(args)...); +} } // namespace stdx BSONCXX_INLINE_NAMESPACE_END } // namespace bsoncxx -#else -#error "Cannot find a valid polyfill for make_unique" -#endif - #include diff --git a/src/bsoncxx/test/CMakeLists.txt b/src/bsoncxx/test/CMakeLists.txt index 5db9fb949a..f9b3dac3dd 100644 --- a/src/bsoncxx/test/CMakeLists.txt +++ b/src/bsoncxx/test/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(test_bson json.cpp oid.cpp view_or_value.cpp + make_unique.test.cpp ) # Common target properties for test executables. @@ -72,4 +73,5 @@ set_dist_list(src_bsoncxx_test_DIST oid.cpp to_string.hh view_or_value.cpp + make_unique.test.cpp ) diff --git a/src/bsoncxx/test/make_unique.test.cpp b/src/bsoncxx/test/make_unique.test.cpp new file mode 100644 index 0000000000..73ef238521 --- /dev/null +++ b/src/bsoncxx/test/make_unique.test.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +#include +#include + +namespace { + +struct something { + something(int val) : value(val) {} + int value; +}; + +TEST_CASE("Create a unique_ptr") { + auto ptr = bsoncxx::stdx::make_unique(12); + REQUIRE(ptr); + CHECK(*ptr == 12); + + auto thing = bsoncxx::stdx::make_unique(5); + REQUIRE(thing); + CHECK(thing->value == 5); +} + +TEST_CASE("Create a unique_ptr") { + const unsigned length = 12; + auto ptr = bsoncxx::stdx::make_unique(length); + REQUIRE(ptr); + // All elements are direct-initialized, which produces '0' for `int` + CHECK(ptr[0] == 0); + auto res = std::equal_range(ptr.get(), ptr.get() + length, 0); + CHECK(res.first == ptr.get()); + CHECK(res.second == (ptr.get() + length)); + + ptr = bsoncxx::stdx::make_unique_for_overwrite(length); + std::fill_n(ptr.get(), length, 42); + CHECK(std::all_of(ptr.get(), ptr.get() + length, [](int n) { return n == 42; })); +} + +} // namespace