diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 50c04c0f18..b5a92a5c1b 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -1,7 +1,3 @@ -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - if(CMAKE_SYSTEM_NAME MATCHES "Windows") add_definitions(-DNOMINMAX) add_definitions(-DUNICODE) @@ -13,11 +9,29 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows") add_definitions(-D_CRT_SECURE_NO_DEPRECATE) endif() -# enable the latest C++ standard feature set, -# and also disable MSVC specific extensions -# to be even more standards compliant. -check_and_add_flag(CPPLATEST /std:c++latest) -check_and_add_flag(STANDARD_COMPLIANCE /permissive-) +if(CMAKE_C_COMPILER_ID MATCHES "MSVC") + # enable the latest C++ standard feature set, + # and also disable MSVC specific extensions + # to be even more standards compliant. + check_and_add_flag(CPPLATEST /std:c++latest) + check_and_add_flag(STANDARD_COMPLIANCE /permissive-) +else() + # Enable C++17, but fall back to C++14 if it isn't available. + # CMAKE_CXX_STANDARD cannot be used here because we require C++14 or newer, not any standard. + check_and_add_flag(CXX17 -std=c++17) + if(NOT FLAG_CXX_CXX17) + check_and_add_flag(CXX1Z -std=c++1z) + if (NOT FLAG_CXX_CXX1Z) + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + endif() + endif() + + # These compat headers must not be in the include path when building with MSVC, + # because it currently does not support __has_include_next / #include_next. + include_directories(SYSTEM Core/Common/Compat) +endif() # These aren't actually needed for C11/C++11 # but some dependencies require them (LLVM, libav). diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index d11eed1ec6..6e8ff6ec64 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -227,4 +227,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index ee4f949251..267fde6d83 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -324,4 +324,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Common/Compat/in_place.h b/Source/Core/Common/Compat/in_place.h new file mode 100644 index 0000000000..1758216d22 --- /dev/null +++ b/Source/Core/Common/Compat/in_place.h @@ -0,0 +1,43 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#include + +namespace std +{ +struct in_place_t +{ + explicit in_place_t() = default; +}; + +template +struct in_place_index_t +{ + explicit in_place_index_t() = default; +}; + +template +struct in_place_type_t +{ + explicit in_place_type_t() = default; +}; + +constexpr in_place_t in_place{}; + +template +constexpr in_place_index_t in_place_index{}; + +template +constexpr in_place_type_t in_place_type{}; + +} // namespace std diff --git a/Source/Core/Common/Compat/optional b/Source/Core/Common/Compat/optional new file mode 100644 index 0000000000..4f7598da6d --- /dev/null +++ b/Source/Core/Common/Compat/optional @@ -0,0 +1,907 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#if __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