// Copyright 2017 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once #if __cplusplus >= 201703L && __has_include_next() #include_next #else /* Copyright 2017 The TensorFlow Authors. All Rights Reserved. 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. ==============================================================================*/ #include #include #include #include #include #ifndef __has_feature #define __has_feature(x) 0 #endif #if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) #define GTL_HAS_EXCEPTIONS #endif namespace gtl { // A value of type gtl::optional holds either a value of T or an // "empty" value. When it holds a value of T, it stores it as a direct // subobject, so sizeof(optional) is approximately sizeof(T)+1. The interface // is based on the upcoming std::optional, and gtl::optional is // designed to be cheaply drop-in replaceable by std::optional, once it is // rolled out. // // This implementation is based on the specification in the latest draft as of // 2017-01-05, section 20.6. // // Differences between gtl::optional and std::optional include: // - constexpr not used for nonconst member functions. // (dependency on some differences between C++11 and C++14.) // - nullopt and in_place are not constexpr. We need the inline variable // support in C++17 for external linkage. // - optional::swap() and swap() relies on std::is_(nothrow_)swappable // which is introduced in C++17. So we assume is_swappable is always true // and is_nothrow_swappable is same as std::is_trivial. // - make_optional cannot be constexpr due to absence of guaranteed copy // elision. // // Synopsis: // // #include "tensorflow/core/lib/gtl/optional.h" // // tensorflow::gtl::optional f() { // string result; // if (...) { // ... // result = ...; // return result; // } else { // ... // return tensorflow::gtl::nullopt; // } // } // // int main() { // tensorflow::gtl::optional optstr = f(); // if (optstr) { // // non-empty // print(optstr.value()); // } else { // // empty // error(); // } // } template class optional; // The tag constant `in_place` is used as the first parameter of an optional // constructor to indicate that the remaining arguments should be forwarded // to the underlying T constructor. struct in_place_t {}; extern const in_place_t in_place; // The tag constant `nullopt` is used to indicate an empty optional in // certain functions, such as construction or assignment. struct nullopt_t { struct init_t {}; static init_t init; // It must not be default-constructible to avoid ambiguity for opt = {}. // Note the non-const reference, it is to eliminate ambiguity for code like: // struct S { int value; }; // // void Test() { // optional opt; // opt = {{}}; // } explicit constexpr nullopt_t(init_t& /*unused*/) {} // NOLINT }; extern const nullopt_t nullopt; class bad_optional_access : public std::exception { public: virtual const char* what() const noexcept { return "bad_optional_access"; } }; [[noreturn]] inline void throw_bad_optional_access() { #ifdef GTL_HAS_EXCEPTIONS throw bad_optional_access{}; #else std::terminate(); #endif } namespace internal_optional { // define forward locally because std::forward is not constexpr until C++14 template constexpr T&& forward(typename std::remove_reference::type& t) noexcept { // NOLINT(runtime/references) return static_cast(t); } struct empty_struct {}; // This class stores the data in optional. // It is specialized based on whether T is trivially destructible. // This is the specialization for non trivially destructible type. template ::value> class optional_data_dtor_base { protected: // Whether there is data or not. bool engaged_; // data storage union { empty_struct dummy_; T data_; }; void destruct() noexcept { if (engaged_) { data_.~T(); engaged_ = false; } } // dummy_ must be initialized for constexpr constructor constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{} {} template constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) : engaged_(true), data_(internal_optional::forward(args)...) {} ~optional_data_dtor_base() { destruct(); } }; // Specialization for trivially destructible type. template class optional_data_dtor_base { protected: // Whether there is data or not. bool engaged_; // data storage union { empty_struct dummy_; T data_; }; void destruct() noexcept { engaged_ = false; } // dummy_ must be initialized for constexpr constructor constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{} {} template constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) : engaged_(true), data_(internal_optional::forward(args)...) {} ~optional_data_dtor_base() = default; }; template class optional_data : public optional_data_dtor_base { protected: using base = optional_data_dtor_base; using base::base; T* pointer() { return &this->data_; } constexpr const T* pointer() const { return &this->data_; } template void construct(Args&&... args) { new (pointer()) T(std::forward(args)...); this->engaged_ = true; } template void assign(U&& u) { if (this->engaged_) { this->data_ = std::forward(u); } else { construct(std::forward(u)); } } optional_data() = default; optional_data(const optional_data& rhs) { if (rhs.engaged_) { construct(rhs.data_); } } optional_data(optional_data&& rhs) noexcept( std::is_nothrow_move_constructible::value) { if (rhs.engaged_) { construct(std::move(rhs.data_)); } } optional_data& operator=(const optional_data& rhs) { if (rhs.engaged_) { assign(rhs.data_); } else { this->destruct(); } return *this; } optional_data& operator=(optional_data&& rhs) noexcept( std::is_nothrow_move_assignable::value&& std::is_nothrow_move_constructible::value) { if (rhs.engaged_) { assign(std::move(rhs.data_)); } else { this->destruct(); } return *this; } }; // ordered by level of restriction, from low to high. // copyable implies movable. enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; // base class for enabling/disabling copy/move constructor. template class optional_ctor_base; template <> class optional_ctor_base { public: constexpr optional_ctor_base() = default; optional_ctor_base(const optional_ctor_base&) = default; optional_ctor_base(optional_ctor_base&&) = default; optional_ctor_base& operator=(const optional_ctor_base&) = default; optional_ctor_base& operator=(optional_ctor_base&&) = default; }; template <> class optional_ctor_base { public: constexpr optional_ctor_base() = default; optional_ctor_base(const optional_ctor_base&) = delete; optional_ctor_base(optional_ctor_base&&) = default; optional_ctor_base& operator=(const optional_ctor_base&) = default; optional_ctor_base& operator=(optional_ctor_base&&) = default; }; template <> class optional_ctor_base { public: constexpr optional_ctor_base() = default; optional_ctor_base(const optional_ctor_base&) = delete; optional_ctor_base(optional_ctor_base&&) = delete; optional_ctor_base& operator=(const optional_ctor_base&) = default; optional_ctor_base& operator=(optional_ctor_base&&) = default; }; // base class for enabling/disabling copy/move assignment. template class optional_assign_base; template <> class optional_assign_base { public: constexpr optional_assign_base() = default; optional_assign_base(const optional_assign_base&) = default; optional_assign_base(optional_assign_base&&) = default; optional_assign_base& operator=(const optional_assign_base&) = default; optional_assign_base& operator=(optional_assign_base&&) = default; }; template <> class optional_assign_base { public: constexpr optional_assign_base() = default; optional_assign_base(const optional_assign_base&) = default; optional_assign_base(optional_assign_base&&) = default; optional_assign_base& operator=(const optional_assign_base&) = delete; optional_assign_base& operator=(optional_assign_base&&) = default; }; template <> class optional_assign_base { public: constexpr optional_assign_base() = default; optional_assign_base(const optional_assign_base&) = default; optional_assign_base(optional_assign_base&&) = default; optional_assign_base& operator=(const optional_assign_base&) = delete; optional_assign_base& operator=(optional_assign_base&&) = delete; }; template constexpr copy_traits get_ctor_copy_traits() { return std::is_copy_constructible::value ? copy_traits::copyable : std::is_move_constructible::value ? copy_traits::movable : copy_traits::non_movable; } template constexpr copy_traits get_assign_copy_traits() { return std::is_copy_assignable::value && std::is_copy_constructible::value ? copy_traits::copyable : std::is_move_assignable::value && std::is_move_constructible::value ? copy_traits::movable : copy_traits::non_movable; } // Whether T is constructible or convertible from optional. template struct is_constructible_convertible_from_optional : std::integral_constant< bool, std::is_constructible&>::value || std::is_constructible&&>::value || std::is_constructible&>::value || std::is_constructible&&>::value || std::is_convertible&, T>::value || std::is_convertible&&, T>::value || std::is_convertible&, T>::value || std::is_convertible&&, T>::value> {}; // Whether T is constructible or convertible or assignable from optional. template struct is_constructible_convertible_assignable_from_optional : std::integral_constant< bool, is_constructible_convertible_from_optional::value || std::is_assignable&>::value || std::is_assignable&&>::value || std::is_assignable&>::value || std::is_assignable&&>::value> {}; } // namespace internal_optional template class optional : private internal_optional::optional_data, private internal_optional::optional_ctor_base< internal_optional::get_ctor_copy_traits()>, private internal_optional::optional_assign_base< internal_optional::get_assign_copy_traits()> { using data_base = internal_optional::optional_data; public: typedef T value_type; // [optional.ctor], constructors // A default constructed optional holds the empty value, NOT a default // constructed T. constexpr optional() noexcept {} // An optional initialized with `nullopt` holds the empty value. constexpr optional(nullopt_t) noexcept {} // NOLINT(runtime/explicit) // Copy constructor, standard semantics. optional(const optional& src) = default; // Move constructor, standard semantics. optional(optional&& src) = default; // optional(in_place, arg1, arg2, arg3) constructs a non-empty optional // with an in-place constructed value of T(arg1,arg2,arg3). // TODO(b/34201852): Add std::is_constructible SFINAE. template constexpr explicit optional(in_place_t, Args&&... args) : data_base(in_place_t(), internal_optional::forward(args)...) {} // optional(in_place, {arg1, arg2, arg3}) constructs a non-empty optional // with an in-place list-initialized value of T({arg1, arg2, arg3}). template &, Args&&...>::value>::type> constexpr explicit optional(in_place_t, std::initializer_list il, Args&&... args) : data_base(in_place_t(), il, internal_optional::forward(args)...) { } template < typename U = T, typename std::enable_if< std::is_constructible::value && !std::is_same::type>::value && !std::is_same, typename std::decay::type>::value && std::is_convertible::value, bool>::type = false> constexpr optional(U&& v) // NOLINT : data_base(in_place_t(), internal_optional::forward(v)) {} template < typename U = T, typename std::enable_if< std::is_constructible::value && !std::is_same::type>::value && !std::is_same, typename std::decay::type>::value && !std::is_convertible::value, bool>::type = false> explicit constexpr optional(U&& v) : data_base(in_place_t(), internal_optional::forward(v)) {} // Converting copy constructor (implicit) template < typename U, typename std::enable_if< std::is_constructible::value && !internal_optional::is_constructible_convertible_from_optional< T, U>::value && std::is_convertible::value, bool>::type = false> optional(const optional& rhs) { // NOLINT if (rhs) { this->construct(*rhs); } } // Converting copy constructor (explicit) template < typename U, typename std::enable_if< std::is_constructible::value && !internal_optional::is_constructible_convertible_from_optional< T, U>::value && !std::is_convertible::value, bool>::type = false> explicit optional(const optional& rhs) { if (rhs) { this->construct(*rhs); } } // Converting move constructor (implicit) template < typename U, typename std::enable_if< std::is_constructible::value && !internal_optional::is_constructible_convertible_from_optional< T, U>::value && std::is_convertible::value, bool>::type = false> optional(optional&& rhs) { // NOLINT if (rhs) { this->construct(std::move(*rhs)); } } // Converting move constructor (explicit) template < typename U, typename std::enable_if< std::is_constructible::value && !internal_optional::is_constructible_convertible_from_optional< T, U>::value && !std::is_convertible::value, bool>::type = false> explicit optional(optional&& rhs) { if (rhs) { this->construct(std::move(*rhs)); } } // [optional.dtor], destructor, trivial if T is trivially destructible. ~optional() = default; // [optional.assign], assignment // Assignment from nullopt: opt = nullopt optional& operator=(nullopt_t) noexcept { this->destruct(); return *this; } // Copy assigment, standard semantics. optional& operator=(const optional& src) = default; // Move assignment, standard semantics. optional& operator=(optional&& src) = default; // Value assignment template < typename U = T, typename = typename std::enable_if< !std::is_same, typename std::decay::type>::value && (!std::is_scalar::value || !std::is_same::type>::value) && std::is_constructible::value && std::is_assignable::value>::type> optional& operator=(U&& v) { this->assign(std::forward(v)); return *this; } template ::value && std::is_assignable::value && !internal_optional:: is_constructible_convertible_assignable_from_optional< T, U>::value>::type> optional& operator=(const optional& rhs) { if (rhs) { this->assign(*rhs); } else { this->destruct(); } return *this; } template ::value && std::is_assignable::value && !internal_optional:: is_constructible_convertible_assignable_from_optional< T, U>::value>::type> optional& operator=(optional&& rhs) { if (rhs) { this->assign(std::move(*rhs)); } else { this->destruct(); } return *this; } // [optional.mod], modifiers // Destroys the inner T value if one is present. void reset() noexcept { this->destruct(); } // Emplace reconstruction. (Re)constructs the underlying T in-place with the // given arguments forwarded: // // optional opt; // opt.emplace(arg1,arg2,arg3); (Constructs Foo(arg1,arg2,arg3)) // // If the optional is non-empty, and the `args` refer to subobjects of the // current object, then behavior is undefined. This is because the current // object will be destructed before the new object is constructed with `args`. // template ::value>::type> void emplace(Args&&... args) { this->destruct(); this->construct(std::forward(args)...); } // Emplace reconstruction with initializer-list. See immediately above. template &, Args&&...>::value>::type> void emplace(std::initializer_list il, Args&&... args) { this->destruct(); this->construct(il, std::forward(args)...); } // [optional.swap], swap // Swap, standard semantics. void swap(optional& rhs) noexcept( std::is_nothrow_move_constructible::value&& std::is_trivial::value) { if (*this) { if (rhs) { using std::swap; swap(**this, *rhs); } else { rhs.construct(std::move(**this)); this->destruct(); } } else { if (rhs) { this->construct(std::move(*rhs)); rhs.destruct(); } else { // no effect (swap(disengaged, disengaged)) } } } // [optional.observe], observers // You may use `*opt`, and `opt->m`, to access the underlying T value and T's // member `m`, respectively. If the optional is empty, behavior is // undefined. constexpr const T* operator->() const { return this->pointer(); } T* operator->() { assert(this->engaged_); return this->pointer(); } constexpr const T& operator*() const & { return reference(); } T& operator*() & { assert(this->engaged_); return reference(); } constexpr const T&& operator*() const && { return std::move(reference()); } T&& operator*() && { assert(this->engaged_); return std::move(reference()); } // In a bool context an optional will return false if and only if it is // empty. // // if (opt) { // // do something with opt.value(); // } else { // // opt is empty // } // constexpr explicit operator bool() const noexcept { return this->engaged_; } // Returns false if and only if *this is empty. constexpr bool has_value() const noexcept { return this->engaged_; } // Use `opt.value()` to get a reference to underlying value. The constness // and lvalue/rvalue-ness of `opt` is preserved to the view of the T // subobject. const T& value() const & { if (!*this) throw_bad_optional_access(); return reference(); } T& value() & { if (!*this) throw_bad_optional_access(); return reference(); } T&& value() && { // NOLINT(build/c++11) if (!*this) throw_bad_optional_access(); return std::move(reference()); } const T&& value() const && { // NOLINT(build/c++11) if (!*this) throw_bad_optional_access(); return std::move(reference()); } // Use `opt.value_or(val)` to get either the value of T or the given default // `val` in the empty case. template constexpr T value_or(U&& v) const & { return static_cast(*this) ? **this : static_cast(std::forward(v)); } template T value_or(U&& v) && { // NOLINT(build/c++11) return static_cast(*this) ? std::move(**this) : static_cast(std::forward(v)); } private: // Private accessors for internal storage viewed as reference to T. constexpr const T& reference() const { return *this->pointer(); } T& reference() { return *(this->pointer()); } // T constaint checks. You can't have an optional of nullopt_t, in_place_t or // a reference. static_assert( !std::is_same::type>::value, "optional is not allowed."); static_assert( !std::is_same::type>::value, "optional is not allowed."); static_assert(!std::is_reference::value, "optional is not allowed."); }; // [optional.specalg] // Swap, standard semantics. // This function shall not participate in overload resolution unless // is_move_constructible_v is true and is_swappable_v is true. // NOTE: we assume is_swappable is always true. There will be a compiling error // if T is actually not Swappable. template ::value, bool>::type = false> void swap(optional& a, optional& b) noexcept(noexcept(a.swap(b))) { a.swap(b); } // NOTE: make_optional cannot be constexpr in C++11 because the copy/move // constructor is not constexpr and we don't have guaranteed copy elision // util C++17. But they are still declared constexpr for consistency with // the standard. // make_optional(v) creates a non-empty optional where the type T is deduced // from v. Can also be explicitly instantiated as make_optional(v). template constexpr optional::type> make_optional(T&& v) { return optional::type>(std::forward(v)); } template constexpr optional make_optional(Args&&... args) { return optional(in_place_t(), internal_optional::forward(args)...); } template constexpr optional make_optional(std::initializer_list il, Args&&... args) { return optional(in_place_t(), il, internal_optional::forward(args)...); } // Relational operators. Empty optionals are considered equal to each // other and less than non-empty optionals. Supports relations between // optional and optional, between optional and T, and between // optional and nullopt. // Note: We're careful to support T having non-bool relationals. // Relational operators [optional.relops] // The C++17 (N4606) "Returns:" statements are translated into code // in an obvious way here, and the original text retained as function docs. // Returns: If bool(x) != bool(y), false; otherwise if bool(x) == false, true; // otherwise *x == *y. template constexpr bool operator==(const optional& x, const optional& y) { return static_cast(x) != static_cast(y) ? false : static_cast(x) == false ? true : *x == *y; } // Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false; // otherwise *x != *y. template constexpr bool operator!=(const optional& x, const optional& y) { return static_cast(x) != static_cast(y) ? true : static_cast(x) == false ? false : *x != *y; } // Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y. template constexpr bool operator<(const optional& x, const optional& y) { return !y ? false : !x ? true : *x < *y; } // Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y. template constexpr bool operator>(const optional& x, const optional& y) { return !x ? false : !y ? true : *x > *y; } // Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y. template constexpr bool operator<=(const optional& x, const optional& y) { return !x ? true : !y ? false : *x <= *y; } // Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y. template constexpr bool operator>=(const optional& x, const optional& y) { return !y ? true : !x ? false : *x >= *y; } // Comparison with nullopt [optional.nullops] // The C++17 (N4606) "Returns:" statements are used directly here. template constexpr bool operator==(const optional& x, nullopt_t) noexcept { return !x; } template constexpr bool operator==(nullopt_t, const optional& x) noexcept { return !x; } template constexpr bool operator!=(const optional& x, nullopt_t) noexcept { return static_cast(x); } template constexpr bool operator!=(nullopt_t, const optional& x) noexcept { return static_cast(x); } template constexpr bool operator<(const optional& x, nullopt_t) noexcept { return false; } template constexpr bool operator<(nullopt_t, const optional& x) noexcept { return static_cast(x); } template constexpr bool operator<=(const optional& x, nullopt_t) noexcept { return !x; } template constexpr bool operator<=(nullopt_t, const optional& x) noexcept { return true; } template constexpr bool operator>(const optional& x, nullopt_t) noexcept { return static_cast(x); } template constexpr bool operator>(nullopt_t, const optional& x) noexcept { return false; } template constexpr bool operator>=(const optional& x, nullopt_t) noexcept { return true; } template constexpr bool operator>=(nullopt_t, const optional& x) noexcept { return !x; } // Comparison with T [optional.comp_with_t] // The C++17 (N4606) "Equivalent to:" statements are used directly here. template constexpr bool operator==(const optional& x, const T& v) { return static_cast(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return static_cast(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return static_cast(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return static_cast(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return static_cast(x) ? *x < v : true; } template constexpr bool operator<(const T& v, const optional& x) { return static_cast(x) ? v < *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return static_cast(x) ? *x <= v : true; } template constexpr bool operator<=(const T& v, const optional& x) { return static_cast(x) ? v <= *x : false; } template constexpr bool operator>(const optional& x, const T& v) { return static_cast(x) ? *x > v : false; } template constexpr bool operator>(const T& v, const optional& x) { return static_cast(x) ? v > *x : true; } template constexpr bool operator>=(const optional& x, const T& v) { return static_cast(x) ? *x >= v : false; } template constexpr bool operator>=(const T& v, const optional& x) { return static_cast(x) ? v >= *x : true; } } // namespace gtl namespace std { template struct hash<::gtl::optional> { size_t operator()(const ::gtl::optional& opt) const { if (opt) { return hash()(*opt); } else { return static_cast(0x297814aaad196e6dULL); } } }; using ::gtl::optional; using ::gtl::bad_optional_access; using ::gtl::nullopt_t; using ::gtl::nullopt; using ::gtl::make_optional; } // namespace std #endif