// Copyright 2009-present MongoDB, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #if defined(BSONCXX_POLY_USE_STD) #include namespace bsoncxx { namespace v1 { namespace stdx { using std::in_place; using std::in_place_t; using std::make_optional; using std::nullopt; using std::nullopt_t; using std::optional; } // namespace stdx } // namespace v1 } // namespace bsoncxx #elif defined(BSONCXX_POLY_USE_IMPLS) #include #include #include #include #include #include #include #include #include #include #include namespace bsoncxx { namespace v1 { namespace stdx { template class optional; BSONCXX_PRIVATE_WARNINGS_PUSH(); BSONCXX_PRIVATE_WARNINGS_DISABLE(Clang("-Wweak-vtables")); // Exception type thrown upon attempted access to a value-less optional via a throwing accessor // API. class bad_optional_access : public std::exception { public: char const* what() const noexcept override { return "bad_optional_access()"; } }; BSONCXX_PRIVATE_WARNINGS_POP(); // Tag type to represent an empty optional value. struct nullopt_t { explicit constexpr nullopt_t(std::nullptr_t) noexcept {} }; // Tag constant to construct or compare with an empty optional value. static constexpr nullopt_t nullopt{nullptr}; // Tag used to call the emplacement-constructor of optional. static constexpr struct in_place_t { } in_place; namespace detail { // Terminates the program when an illegal use of optional is attempted. [[noreturn]] inline void terminate_disengaged_optional(char const* what) noexcept { (void)std::fprintf(stderr, "%s: Invalid attempted use of disengaged optional\n", what); std::terminate(); } // Throws bad_optional_access for throwing optional member functions. [[noreturn]] inline void throw_bad_optional() { throw bad_optional_access(); } // Base class of std::optional. Implementation detail, defined later. template struct optional_base_class; // Base case: Things are not optionals. template std::true_type not_an_optional_f(T const&); // More-specialized if given an optional or any class derived from a template // specialization thereof. template std::false_type not_an_optional_f(optional const&); // Utility trait to detect specializations of stdx::optional. template struct not_an_optional : decltype(not_an_optional_f(std::declval())){}; template struct enable_opt_conversion : bsoncxx::detail::conjunction< std::is_constructible, bsoncxx::detail::disjunction< std::is_same, bsoncxx::detail::negation&>, std::is_constructible const&>, std::is_constructible&&>, std::is_constructible const&&>, std::is_convertible&, T>, std::is_convertible const&, T>, std::is_convertible&&, T>, std::is_convertible const&&, T>>>>> {}; template struct enable_opt_value_conversion : bsoncxx::detail::conjunction< std::is_constructible, bsoncxx::detail::negation>, bsoncxx::detail::negation>>, bsoncxx::detail::disjunction< bsoncxx::detail::negation>, detail::not_an_optional>>> {}; } // namespace detail template class optional : bsoncxx::detail::equality_operators, bsoncxx::detail::ordering_operators, public detail::optional_base_class::type { public: using value_type = T; using reference = bsoncxx::detail::add_lvalue_reference_t; using const_reference = bsoncxx::detail::add_lvalue_reference_t>; using rvalue_reference = bsoncxx::detail::add_rvalue_reference_t; using const_rvalue_reference = bsoncxx::detail::add_rvalue_reference_t>; using pointer = bsoncxx::detail::add_pointer_t; using const_pointer = bsoncxx::detail::add_pointer_t; // Constructors [1]. optional() = default; constexpr optional(nullopt_t) noexcept {} // Ctor [2] and [3] are provided by base classes. optional(optional const&) = default; optional(optional&&) = default; // Same with assignments optional& operator=(optional const&) = default; optional& operator=(optional&&) = default; ~optional() = default; // In-place constructors template BSONCXX_PRIVATE_CONSTEXPR_CXX14 explicit optional(in_place_t, Args&&... args) noexcept( noexcept(T(BSONCXX_PRIVATE_FWD(args)...))) { this->emplace(BSONCXX_PRIVATE_FWD(args)...); } template BSONCXX_PRIVATE_CONSTEXPR_CXX14 explicit optional(in_place_t, std::initializer_list il, Args&&... args) noexcept( noexcept(T(il, BSONCXX_PRIVATE_FWD(args)...))) { this->emplace(il, BSONCXX_PRIVATE_FWD(args)...); } // Explicit converting constructor. Only available if implicit conversion is // not possible. template < typename U = T, bsoncxx::detail::requires_t< int, detail::enable_opt_value_conversion, bsoncxx::detail::negation>> = 0> BSONCXX_PRIVATE_CONSTEXPR_CXX14 explicit optional(U&& arg) noexcept(std::is_nothrow_constructible::value) : optional(in_place, BSONCXX_PRIVATE_FWD(arg)) {} // Implicit converting constructor. Only available if implicit conversion is // possible. template < typename U = T, bsoncxx::detail::requires_t, std::is_convertible> = 0> BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional(U&& arg) noexcept(std::is_nothrow_constructible::value) : optional(in_place, BSONCXX_PRIVATE_FWD(arg)) {} template < typename U, bsoncxx::detail::requires_t< int, detail::enable_opt_conversion, bsoncxx::detail::negation>> = 0> BSONCXX_PRIVATE_CONSTEXPR_CXX14 explicit optional(optional const& other) noexcept( std::is_nothrow_constructible>::value) { if (other.has_value()) { this->emplace(*other); } } template < typename U, bsoncxx::detail::requires_t< int, detail::enable_opt_conversion, std::is_convertible> = 0> BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional(optional const& other) noexcept( std::is_nothrow_constructible>::value) { if (other.has_value()) { this->emplace(*other); } } template < typename U, bsoncxx::detail::requires_t< int, detail::enable_opt_conversion, bsoncxx::detail::negation>> = 0> BSONCXX_PRIVATE_CONSTEXPR_CXX14 explicit optional(optional&& other) noexcept( std::is_nothrow_constructible>::value) { if (other.has_value()) { this->emplace(*BSONCXX_PRIVATE_FWD(other)); } } template < typename U, bsoncxx::detail::requires_t, std::is_convertible> = 0> BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional(optional&& other) noexcept( std::is_nothrow_constructible>::value) { if (other.has_value()) { this->emplace(*BSONCXX_PRIVATE_FWD(other)); } } constexpr bool has_value() const noexcept { return this->_has_value; } constexpr explicit operator bool() const noexcept { return this->has_value(); } // Unchecked dereference operators. BSONCXX_PRIVATE_CONSTEXPR_CXX14 reference operator*() & noexcept { _assert_has_value("operator*() &"); return this->_storage.value; } BSONCXX_PRIVATE_CONSTEXPR_CXX14 const_reference operator*() const& noexcept { _assert_has_value("operator*() const&"); return this->_storage.value; } BSONCXX_PRIVATE_CONSTEXPR_CXX14 rvalue_reference operator*() && noexcept { _assert_has_value("operator*() &&"); return static_cast(**this); } BSONCXX_PRIVATE_CONSTEXPR_CXX14 const_rvalue_reference operator*() const&& noexcept { _assert_has_value("operator*() const&&"); return static_cast(**this); } // (Unchecked) member-access operators. BSONCXX_PRIVATE_CONSTEXPR_CXX14 pointer operator->() noexcept { _assert_has_value("operator->()"); return std::addressof(**this); } BSONCXX_PRIVATE_CONSTEXPR_CXX14 const_pointer operator->() const noexcept { _assert_has_value("operator->() const"); return std::addressof(**this); } // Checked accessors. BSONCXX_PRIVATE_CONSTEXPR_CXX14 reference value() & { _throw_if_empty(); return **this; } BSONCXX_PRIVATE_CONSTEXPR_CXX14 const_reference value() const& { _throw_if_empty(); return **this; } BSONCXX_PRIVATE_CONSTEXPR_CXX14 rvalue_reference value() && { _throw_if_empty(); return static_cast(**this); } BSONCXX_PRIVATE_CONSTEXPR_CXX14 const_rvalue_reference value() const&& { _throw_if_empty(); return static_cast(**this); } // Checked value-or-alternative. template BSONCXX_PRIVATE_CONSTEXPR_CXX14 value_type value_or(U&& dflt) const& { if (has_value()) { return **this; } else { return static_cast(BSONCXX_PRIVATE_FWD(dflt)); } } template BSONCXX_PRIVATE_CONSTEXPR_CXX14 value_type value_or(U&& dflt) && { if (has_value()) { return *std::move(*this); } else { return static_cast(BSONCXX_PRIVATE_FWD(dflt)); } } private: BSONCXX_PRIVATE_CONSTEXPR_CXX14 void _assert_has_value(char const* msg) const noexcept { if (!this->has_value()) { detail::terminate_disengaged_optional(msg); } } BSONCXX_PRIVATE_CONSTEXPR_CXX14 void _throw_if_empty() const { if (!this->has_value()) { detail::throw_bad_optional(); } } }; // Construct an optional by decay-copying the given value into a new optional>. // // @param value The value being made into an optional. template BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional> make_optional(T&& value) noexcept( std::is_nothrow_constructible, T&&>::value) { return optional>(BSONCXX_PRIVATE_FWD(value)); } // Emplace-construct a new optional of the given type with the given constructor arguments. // // @tparam T The type to be constructed // @param args Constructor arguments template BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional make_optional(Args&&... args) noexcept( std::is_nothrow_constructible::value) { return optional(in_place, BSONCXX_PRIVATE_FWD(args)...); } // Emplace-construct a new optional of the given type with the given arguments (accepts an init-list // as the first argument). template BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional make_optional(std::initializer_list il, Args&&... args) noexcept( std::is_nothrow_constructible, Args&&...>::value) { return optional(in_place, il, BSONCXX_PRIVATE_FWD(args)...); } namespace detail { // Union template that defines the storage for an optional's data. template ::value> union storage_for { // Placeholder member for disengaged optional char nothing; // Member that holds the actual value T value; // Default-construct activates the placeholder storage_for() noexcept : nothing(0) {} // Empty special members allow the union to be used in semiregular contexts, // but it is the responsibility of the using class to implement them properly ~storage_for() {} storage_for(storage_for const&) = delete; storage_for& operator=(storage_for const&) = delete; }; template union storage_for { char nothing; T value; storage_for() noexcept : nothing(0) {} storage_for(storage_for const&) = delete; storage_for& operator=(storage_for const&) = delete; }; // Whether a type is copyable, moveable, or immobile. enum copymove_classification { copyable, movable, immobile, }; // Classify the constructibility of the given type. template < typename T, bool CanCopy = std::is_copy_constructible::value, bool CanMove = std::is_move_constructible::value> constexpr copymove_classification classify_construct() { return CanCopy ? copyable : CanMove ? movable : immobile; } // Classify the assignability of the given type. template < typename T, bool CanCopy = std::is_copy_assignable::value, bool CanMove = std::is_move_assignable::value> constexpr copymove_classification classify_assignment() { return CanCopy ? copyable : CanMove ? movable : immobile; } // Common base class for optional storage implementation // // @tparam T template class optional_common_base; // Define the special member constructors for optional. template ()> struct optional_construct_base; // Define the special member assignment operators for optional. template ()> struct optional_assign_base; template struct optional_destruct_helper; template using optional_destruct_base = typename optional_destruct_helper::value>::template base; template struct optional_assign_base : optional_construct_base {}; template struct optional_assign_base : optional_construct_base { // Constructors defer to base. optional_assign_base() = default; optional_assign_base(optional_assign_base const&) = default; optional_assign_base(optional_assign_base&&) = default; ~optional_assign_base() = default; // Disallow copies. BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional_assign_base& operator=(optional_assign_base const&) = delete; // Allow move-assignment. BSONCXX_PRIVATE_CONSTEXPR_CXX14 optional_assign_base& operator=(optional_assign_base&&) = default; }; template struct optional_assign_base : optional_construct_base { optional_assign_base() = default; optional_assign_base(optional_assign_base const&) = default; optional_assign_base(optional_assign_base&&) = default; ~optional_assign_base() = default; // No assignment at all optional_assign_base& operator=(optional_assign_base const&) = delete; optional_assign_base& operator=(optional_assign_base&&) = delete; }; template struct optional_construct_base : optional_destruct_base {}; template struct optional_construct_base : optional_destruct_base { optional_construct_base() = default; optional_construct_base(optional_construct_base const&) = delete; optional_construct_base(optional_construct_base&&) = default; optional_construct_base& operator=(optional_construct_base const&) = default; optional_construct_base& operator=(optional_construct_base&&) = default; }; template struct optional_construct_base : optional_destruct_base { optional_construct_base() = default; optional_construct_base(optional_construct_base const&) = delete; optional_construct_base& operator=(optional_construct_base const&) = default; optional_construct_base& operator=(optional_construct_base&&) = default; }; template <> struct optional_destruct_helper { template struct base : optional_common_base { // Special members defer to base. base() = default; base(base const&) = default; base(base&&) = default; base& operator=(base const&) = default; base& operator=(base&&) = default; ~base() { // Here we destroy the contained object during destruction. this->reset(); } }; }; template <> struct optional_destruct_helper { // Just fall-through to the common base, which has no special destructor. template using base = optional_common_base; }; // Optional's ADL-only operators are defined here. struct optional_operators_base { template friend BSONCXX_PRIVATE_CONSTEXPR_CXX14 auto tag_invoke(bsoncxx::detail::equal_to, optional const& left, optional const& right) noexcept -> bsoncxx::detail::requires_t> { if (left.has_value() != right.has_value()) { return false; } BSONCXX_PRIVATE_WARNINGS_PUSH(); BSONCXX_PRIVATE_WARNINGS_DISABLE(GNU("-Wfloat-equal")); return !left.has_value() || *left == *right; BSONCXX_PRIVATE_WARNINGS_POP(); } template friend constexpr auto tag_invoke(bsoncxx::detail::equal_to, optional const& left, U const& right) noexcept -> bsoncxx::detail::requires_t, bsoncxx::detail::is_equality_comparable> { BSONCXX_PRIVATE_WARNINGS_PUSH(); BSONCXX_PRIVATE_WARNINGS_DISABLE(GNU("-Wfloat-equal")); return left.has_value() && *left == right; BSONCXX_PRIVATE_WARNINGS_POP(); } template friend constexpr bool tag_invoke(bsoncxx::detail::equal_to, optional const& opt, nullopt_t) noexcept { return !opt.has_value(); } template BSONCXX_PRIVATE_CONSTEXPR_CXX14 friend auto tag_invoke(bsoncxx::detail::compare_three_way compare, optional const& left, optional const& right) -> bsoncxx::detail::requires_t< bsoncxx::detail::strong_ordering, bsoncxx::detail::is_totally_ordered_with> { if (left.has_value()) { if (right.has_value()) { return compare(*left, *right); } else { // Non-null is greater than any null. return bsoncxx::detail::strong_ordering::greater; } } else { if (right.has_value()) { // Null is less than any non-null. return bsoncxx::detail::strong_ordering::less; } else { // Both are null. return bsoncxx::detail::strong_ordering::equal; } } } template BSONCXX_PRIVATE_CONSTEXPR_CXX14 friend auto tag_invoke(bsoncxx::detail::compare_three_way compare, optional const& left, U const& right) -> bsoncxx::detail::requires_t< bsoncxx::detail::strong_ordering, not_an_optional, bsoncxx::detail::is_totally_ordered_with> { if (left.has_value()) { return compare(*left, right); } // Null optional is less-than any non-null value. return bsoncxx::detail::strong_ordering::less; } template constexpr friend bsoncxx::detail::strong_ordering tag_invoke(bsoncxx::detail::compare_three_way compare, optional const& left, nullopt_t) { return compare(left.has_value(), false); } }; // An ADL-visible swap() should only be available for swappable objects template ::value> struct optional_swap_mixin {}; template struct optional_swap_mixin { BSONCXX_PRIVATE_CONSTEXPR_CXX14 friend void swap(optional& left, optional& right) noexcept( std::is_nothrow_move_constructible::value && bsoncxx::detail::is_nothrow_swappable::value) { left.swap(right); } }; // Common base class of all optionals. template class optional_common_base : optional_operators_base, optional_swap_mixin { using storage_type = detail::storage_for>; public: optional_common_base() = default; ~optional_common_base() = default; optional_common_base(optional_common_base const& other) noexcept(std::is_nothrow_copy_constructible::value) { if (other._has_value) { this->emplace(other._storage.value); } } optional_common_base(optional_common_base&& other) noexcept(std::is_nothrow_move_constructible::value) { if (other._has_value) { this->_emplace_construct_anew(std::move(other)._storage.value); } } optional_common_base& operator=(optional_common_base const& other) noexcept( std::is_nothrow_copy_assignable::value) { this->_assign(BSONCXX_PRIVATE_FWD(other)); return *this; } optional_common_base& operator=(optional_common_base&& other) noexcept(std::is_nothrow_move_assignable::value) { this->_assign(BSONCXX_PRIVATE_FWD(other)); return *this; } // If the optional is holding a value, destroy that value and set ourselves null. void reset() noexcept { if (this->_has_value) { this->_storage.value.~T(); } this->_has_value = false; } // If the optional is holding a value, destroy that value. Construct a new value in-place using // the given arguments. template T& emplace(Args&&... args) { this->reset(); this->_emplace_construct_anew(BSONCXX_PRIVATE_FWD(args)...); return this->_storage.value; } // If the optional is holding a value, destroy that value. Construct a new value in-place using // the given arguments. template T& emplace(std::initializer_list il, Args&&... args) { this->reset(); this->_emplace_construct_anew(il, BSONCXX_PRIVATE_FWD(args)...); return this->_storage.value; } // Special swap for optional values that removes need for a temporary. BSONCXX_PRIVATE_CONSTEXPR_CXX14 void swap(optional_common_base& other) noexcept( std::is_nothrow_move_constructible::value && bsoncxx::detail::is_nothrow_swappable::value) { if (other._has_value) { if (this->_has_value) { using std::swap; // Defer to the underlying swap. swap(this->_storage.value, other._storage.value); } else { // "steal" the other's value. this->emplace(std::move(other._storage.value)); other.reset(); } } else if (this->_has_value) { other.emplace(std::move(this->_storage.value)); this->reset(); } else { // Neither optional has a value, so do nothing. } } private: friend optional; storage_type _storage; bool _has_value = false; // In-place construct a new value from the given arguments. Assumes that the optional does not // have a live value. template void _emplace_construct_anew(Args&&... args) noexcept(std::is_nothrow_constructible::value) { new (std::addressof(this->_storage.value)) T(BSONCXX_PRIVATE_FWD(args)...); this->_has_value = true; } // Perform the semantics of the assignment operator. template void _assign(U&& other_storage) { if (other_storage._has_value) { // We are receiving a value. if (this->_has_value) { // We already have a value. Invoke the underlying assignment. this->_storage.value = BSONCXX_PRIVATE_FWD(other_storage)._storage.value; } else { // We don't have a value. Use the constructor. this->_emplace_construct_anew(BSONCXX_PRIVATE_FWD(other_storage)._storage.value); } } else { // We are receiving nullopt. Destroy our value, if present. this->reset(); } } }; template struct optional_base_class { using type = optional_assign_base; }; template < typename T, bool CanHash = std::is_default_constructible>>::value> struct optional_hash; // Hash is "disabled" if the underlying type is not hashable (disabled = cannot construct the hash // invocable). template struct optional_hash { optional_hash() = delete; optional_hash(optional_hash const&) = delete; }; template struct optional_hash { using Td = bsoncxx::detail::remove_const_t; constexpr std::size_t operator()(optional const& opt) const noexcept(noexcept(std::hash()(std::declval()))) { return opt.has_value() ? std::hash()(*opt) : std::hash()(nullptr); } }; } // namespace detail } // namespace stdx } // namespace v1 } // namespace bsoncxx namespace std { template struct hash> : bsoncxx::v1::stdx::detail::optional_hash {}; } // namespace std #else #error "Cannot find a valid polyfill for optional" #endif #include /// /// @file /// Provides `std::optional`-related polyfills for library API usage. /// /// @note The API and ABI compatibility of this polyfill is determined by polyfill build /// configuration variables and the `BSONCXX_POLY_USE_*` macros provided by @ref /// bsoncxx/v1/config/config.hpp. /// /// @see /// - [Choosing a C++17 Polyfill](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/polyfill-selection/) /// #if defined(BSONCXX_PRIVATE_DOXYGEN_PREPROCESSOR) namespace bsoncxx { namespace v1 { namespace stdx { /// /// A polyfill for `std::optional`. /// /// @note The API and ABI compatibility of this polyfill is determined by polyfill build /// configuration variables and the `BSONCXX_POLY_USE_*` macros provided by @ref /// bsoncxx/v1/config/config.hpp. /// template class optional {}; } // namespace stdx } // namespace v1 } // namespace bsoncxx #endif // defined(BSONCXX_PRIVATE_DOXYGEN_PREPROCESSOR)