From b08653d69dc940ad65d96ccf2f2bf440a91920aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 7 May 2017 13:29:53 +0200 Subject: [PATCH 1/4] Common: Add a std::optional implementation std::optional makes a few things a bit neater and less error prone. However, we still cannot use C++17 (unfortunately), so this commit adds an implementation of std::optional that we can use right now. Based on https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/lib/gtl/optional.h which seems to be fairly similar to C++17's and standards compliant. It's one of the few implementations that handle propagating type traits like copy constructibility, just like libc++/libstdc++. --- Source/CMakeLists.txt | 32 +- Source/Core/Common/Common.vcxproj | 2 +- Source/Core/Common/Common.vcxproj.filters | 2 +- Source/Core/Common/Compat/in_place.h | 43 + Source/Core/Common/Compat/optional | 907 ++++++++++++++++++++++ 5 files changed, 975 insertions(+), 11 deletions(-) create mode 100644 Source/Core/Common/Compat/in_place.h create mode 100644 Source/Core/Common/Compat/optional 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 From 545006f6665407a5dc2cdebddcbb9e4a668a50ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 26 May 2017 19:18:24 +0200 Subject: [PATCH 2/4] Use std::optional for ESFormats/SharedContentMap --- Source/Core/Core/IOS/ES/Formats.cpp | 13 +++++++------ Source/Core/Core/IOS/ES/Formats.h | 3 ++- Source/Core/Core/IOS/ES/NandUtils.cpp | 4 ++-- Source/Core/Core/IOS/ES/TitleManagement.cpp | 6 +++--- Source/Core/DiscIO/NANDContentLoader.cpp | 2 +- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 97eba84154..224776dcaf 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -419,12 +419,13 @@ SharedContentMap::SharedContentMap(Common::FromWhichRoot root) : m_root(root) SharedContentMap::~SharedContentMap() = default; -std::string SharedContentMap::GetFilenameFromSHA1(const std::array& sha1) const +std::optional +SharedContentMap::GetFilenameFromSHA1(const std::array& sha1) const { const auto it = std::find_if(m_entries.begin(), m_entries.end(), [&sha1](const auto& entry) { return entry.sha1 == sha1; }); if (it == m_entries.end()) - return "unk"; + return {}; const std::string id_string(it->id.begin(), it->id.end()); return Common::RootUserPath(m_root) + StringFromFormat("/shared1/%s.app", id_string.c_str()); @@ -442,9 +443,9 @@ std::vector> SharedContentMap::GetHashes() const std::string SharedContentMap::AddSharedContent(const std::array& sha1) { - std::string filename = GetFilenameFromSHA1(sha1); - if (filename != "unk") - return filename; + auto filename = GetFilenameFromSHA1(sha1); + if (filename) + return *filename; const std::string id = StringFromFormat("%08x", m_last_id); Entry entry; @@ -455,7 +456,7 @@ std::string SharedContentMap::AddSharedContent(const std::array& sha1) WriteEntries(); filename = Common::RootUserPath(m_root) + StringFromFormat("/shared1/%s.app", id.c_str()); m_last_id++; - return filename; + return *filename; } bool SharedContentMap::DeleteSharedContent(const std::array& sha1) diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index e90cb089b4..6d53baf1fc 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -217,7 +218,7 @@ public: explicit SharedContentMap(Common::FromWhichRoot root); ~SharedContentMap(); - std::string GetFilenameFromSHA1(const std::array& sha1) const; + std::optional GetFilenameFromSHA1(const std::array& sha1) const; std::string AddSharedContent(const std::array& sha1); bool DeleteSharedContent(const std::array& sha1); std::vector> GetHashes() const; diff --git a/Source/Core/Core/IOS/ES/NandUtils.cpp b/Source/Core/Core/IOS/ES/NandUtils.cpp index fd586010e5..70c03bbb05 100644 --- a/Source/Core/Core/IOS/ES/NandUtils.cpp +++ b/Source/Core/Core/IOS/ES/NandUtils.cpp @@ -152,8 +152,8 @@ std::vector GetStoredContentsFromTMD(const TMDReader& tmd) [&tmd, &shared](const auto& content) { if (content.IsShared()) { - const std::string path = shared.GetFilenameFromSHA1(content.sha1); - return path != "unk" && File::Exists(path); + const auto path = shared.GetFilenameFromSHA1(content.sha1); + return path && File::Exists(*path); } return File::Exists( Common::GetTitleContentPath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) + diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index 487b799d99..1bfd229a96 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -665,8 +665,8 @@ IPCCommandResult ES::ExportTitleDone(Context& context, const IOCtlVRequest& requ ReturnCode ES::DeleteSharedContent(const std::array& sha1) const { IOS::ES::SharedContentMap map{Common::FromWhichRoot::FROM_SESSION_ROOT}; - const std::string content_path = map.GetFilenameFromSHA1(sha1); - if (content_path == "unk") + const auto content_path = map.GetFilenameFromSHA1(sha1); + if (!content_path) return ES_EINVAL; // Check whether the shared content is used by a system title. @@ -689,7 +689,7 @@ ReturnCode ES::DeleteSharedContent(const std::array& sha1) const return ES_EINVAL; // Delete the shared content and update the content map. - if (!File::Delete(content_path)) + if (!File::Delete(*content_path)) return FS_ENOENT; if (!map.DeleteSharedContent(sha1)) diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index e6aca0ce79..b55efc413b 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -208,7 +208,7 @@ void CNANDContentLoader::InitializeContentEntries(const std::vector& data_ap { std::string filename; if (content.IsShared()) - filename = shared_content.GetFilenameFromSHA1(content.sha1); + filename = *shared_content.GetFilenameFromSHA1(content.sha1); else filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.id); From 62d08e2d172a072d4547f26a6b2740658054fc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 26 May 2017 21:29:15 +0200 Subject: [PATCH 3/4] DiscIO: Fix the wrong NAND root being used The whole NANDContentLoader stuff is truly awful and will be removed as soon as possible. For now, this fixes a bug that was exposed by std::optional::operator*. --- Source/Core/DiscIO/NANDContentLoader.cpp | 14 ++++++++------ Source/Core/DiscIO/NANDContentLoader.h | 10 +++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index b55efc413b..e9700ac1e2 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -94,7 +94,8 @@ bool CNANDContentDataBuffer::GetRange(u32 start, u32 size, u8* buffer) return true; } -CNANDContentLoader::CNANDContentLoader(const std::string& content_name) +CNANDContentLoader::CNANDContentLoader(const std::string& content_name, Common::FromWhichRoot from) + : m_root(from) { m_Valid = Initialize(content_name); } @@ -185,7 +186,7 @@ void CNANDContentLoader::InitializeContentEntries(const std::vector& data_ap u32 data_app_offset = 0; const std::vector title_key = m_ticket.GetTitleKey(); - IOS::ES::SharedContentMap shared_content{Common::FromWhichRoot::FROM_SESSION_ROOT}; + IOS::ES::SharedContentMap shared_content{m_root}; for (size_t i = 0; i < contents.size(); ++i) { @@ -223,14 +224,15 @@ CNANDContentManager::~CNANDContentManager() { } -const CNANDContentLoader& CNANDContentManager::GetNANDLoader(const std::string& content_path) +const CNANDContentLoader& CNANDContentManager::GetNANDLoader(const std::string& content_path, + Common::FromWhichRoot from) { auto it = m_map.find(content_path); if (it != m_map.end()) return *it->second; return *m_map - .emplace_hint(it, std::make_pair(content_path, - std::make_unique(content_path))) + .emplace_hint(it, std::make_pair(content_path, std::make_unique( + content_path, from))) ->second; } @@ -238,7 +240,7 @@ const CNANDContentLoader& CNANDContentManager::GetNANDLoader(u64 title_id, Common::FromWhichRoot from) { std::string path = Common::GetTitleContentPath(title_id, from); - return GetNANDLoader(path); + return GetNANDLoader(path, from); } void CNANDContentManager::ClearCache() diff --git a/Source/Core/DiscIO/NANDContentLoader.h b/Source/Core/DiscIO/NANDContentLoader.h index af4ff4909c..7dee6b488c 100644 --- a/Source/Core/DiscIO/NANDContentLoader.h +++ b/Source/Core/DiscIO/NANDContentLoader.h @@ -75,7 +75,7 @@ struct SNANDContent class CNANDContentLoader final { public: - explicit CNANDContentLoader(const std::string& content_name); + explicit CNANDContentLoader(const std::string& content_name, Common::FromWhichRoot from); ~CNANDContentLoader(); bool IsValid() const; @@ -90,6 +90,7 @@ private: bool m_Valid = false; bool m_IsWAD = false; + Common::FromWhichRoot m_root; std::string m_Path; IOS::ES::TMDReader m_tmd; IOS::ES::TicketReader m_ticket; @@ -107,8 +108,11 @@ public: return instance; } - const CNANDContentLoader& GetNANDLoader(const std::string& content_path); - const CNANDContentLoader& GetNANDLoader(u64 title_id, Common::FromWhichRoot from); + const CNANDContentLoader& + GetNANDLoader(const std::string& content_path, + Common::FromWhichRoot from = Common::FROM_CONFIGURED_ROOT); + const CNANDContentLoader& + GetNANDLoader(u64 title_id, Common::FromWhichRoot from = Common::FROM_CONFIGURED_ROOT); void ClearCache(); private: From e38a66fe1b27c1898c357613aaba223c7d2b4ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 26 May 2017 23:14:52 +0200 Subject: [PATCH 4/4] Common: Add a std::variant implementation Based on https://github.com/mpark/variant (which is based on libc++). --- Source/Core/Common/Compat/variant | 2356 +++++++++++++++++++++++++++++ 1 file changed, 2356 insertions(+) create mode 100644 Source/Core/Common/Compat/variant diff --git a/Source/Core/Common/Compat/variant b/Source/Core/Common/Compat/variant new file mode 100644 index 0000000000..dfe6bd0ed7 --- /dev/null +++ b/Source/Core/Common/Compat/variant @@ -0,0 +1,2356 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#if __has_include_next() +#include_next +#else +// 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) + +/* + variant synopsis + +namespace std { + + // 20.7.2, class template variant + template + class variant { + public: + + // 20.7.2.1, constructors + constexpr variant() noexcept(see below); + variant(const variant&); + variant(variant&&) noexcept(see below); + + template constexpr variant(T&&) noexcept(see below); + + template + constexpr explicit variant(::std::in_place_type_t, Args&&...); + + template + constexpr explicit variant( + ::std::in_place_type_t, initializer_list, Args&&...); + + template + constexpr explicit variant(::std::in_place_index_t, Args&&...); + + template + constexpr explicit variant( + ::std::in_place_index_t, initializer_list, Args&&...); + + // 20.7.2.2, destructor + ~variant(); + + // 20.7.2.3, assignment + variant& operator=(const variant&); + variant& operator=(variant&&) noexcept(see below); + + template variant& operator=(T&&) noexcept(see below); + + // 20.7.2.4, modifiers + template + T& emplace(Args&&...); + + template + T& emplace(initializer_list, Args&&...); + + template + variant_alternative& emplace(Args&&...); + + template + variant_alternative& emplace(initializer_list, Args&&...); + + // 20.7.2.5, value status + constexpr bool valueless_by_exception() const noexcept; + constexpr size_t index() const noexcept; + + // 20.7.2.6, swap + void swap(variant&) noexcept(see below); + }; + + // 20.7.3, variant helper classes + template struct variant_size; // undefined + + template + constexpr size_t variant_size_v = variant_size::value; + + template struct variant_size; + template struct variant_size; + template struct variant_size; + + template + struct variant_size>; + + template struct variant_alternative; // undefined + + template + using variant_alternative_t = typename variant_alternative::type; + + template struct variant_alternative; + template struct variant_alternative; + template struct variant_alternative; + + template + struct variant_alternative>; + + constexpr size_t variant_npos = -1; + + // 20.7.4, value access + template + constexpr bool holds_alternative(const variant&) noexcept; + + template + constexpr variant_alternative_t>& + get(variant&); + + template + constexpr variant_alternative_t>&& + get(variant&&); + + template + constexpr variant_alternative_t> const& + get(const variant&); + + template + constexpr variant_alternative_t> const&& + get(const variant&&); + + template + constexpr T& get(variant&); + + template + constexpr T&& get(variant&&); + + template + constexpr const T& get(const variant&); + + template + constexpr const T&& get(const variant&&); + + template + constexpr add_pointer_t>> + get_if(variant*) noexcept; + + template + constexpr add_pointer_t>> + get_if(const variant*) noexcept; + + template + constexpr add_pointer_t + get_if(variant*) noexcept; + + template + constexpr add_pointer_t + get_if(const variant*) noexcept; + + // 20.7.5, relational operators + template + constexpr bool operator==(const variant&, const variant&); + + template + constexpr bool operator!=(const variant&, const variant&); + + template + constexpr bool operator<(const variant&, const variant&); + + template + constexpr bool operator>(const variant&, const variant&); + + template + constexpr bool operator<=(const variant&, const variant&); + + template + constexpr bool operator>=(const variant&, const variant&); + + // 20.7.6, visitation + template + constexpr see below visit(Visitor&&, Variants&&...); + + // 20.7.7, class monostate + struct monostate; + + // 20.7.8, monostate relational operators + constexpr bool operator<(monostate, monostate) noexcept; + constexpr bool operator>(monostate, monostate) noexcept; + constexpr bool operator<=(monostate, monostate) noexcept; + constexpr bool operator>=(monostate, monostate) noexcept; + constexpr bool operator==(monostate, monostate) noexcept; + constexpr bool operator!=(monostate, monostate) noexcept; + + // 20.7.9, specialized algorithms + template + void swap(variant&, variant&) noexcept(see below); + + // 20.7.10, class bad_variant_access + class bad_variant_access; + + // 20.7.11, hash support + template struct hash; + template struct hash>; + template <> struct hash; + +} // namespace std + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "in_place.h" + +// 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) + +#ifndef MPARK_CONFIG_HPP +#define MPARK_CONFIG_HPP + +// MSVC 2015 Update 3. +#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024215) +#error "MPark.Variant requires C++11 support." +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_builtin(__builtin_addressof) || __GNUC__ >= 7 || defined(_MSC_VER) +#define MPARK_BUILTIN_ADDRESSOF +#endif + +#if __has_builtin(__type_pack_element) +#define MPARK_TYPE_PACK_ELEMENT +#endif + +#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 +#define MPARK_CPP14_CONSTEXPR +#endif + +#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) +#define MPARK_EXCEPTIONS +#endif + +#if defined(__cpp_generic_lambdas) || defined(_MSC_VER) +#define MPARK_GENERIC_LAMBDAS +#endif + +#if defined(__cpp_lib_integer_sequence) +#define MPARK_INTEGER_SEQUENCE +#endif + +#if defined(__cpp_return_type_deduction) || defined(_MSC_VER) +#define MPARK_RETURN_TYPE_DEDUCTION +#endif + +#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER) +#define MPARK_TRANSPARENT_OPERATORS +#endif + +#if defined(__cpp_variable_templates) || defined(_MSC_VER) +#define MPARK_VARIABLE_TEMPLATES +#endif + +#endif // MPARK_CONFIG_HPP + +// 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) + +#ifndef MPARK_LIB_HPP +#define MPARK_LIB_HPP + +#include +#include +#include +#include + +#define RETURN(...) \ + noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { return __VA_ARGS__; } +namespace mpark +{ +namespace lib +{ +template +struct identity +{ + using type = T; +}; + +inline namespace cpp14 +{ +template +struct array +{ + constexpr const T& operator[](std::size_t index) const { return data[index]; } + T data[N == 0 ? 1 : N]; +}; + +template +using add_pointer_t = typename std::add_pointer::type; + +template +using common_type_t = typename std::common_type::type; + +template +using decay_t = typename std::decay::type; + +template +using enable_if_t = typename std::enable_if::type; + +template +using remove_const_t = typename std::remove_const::type; + +template +using remove_reference_t = typename std::remove_reference::type; + +template +inline constexpr T&& forward(remove_reference_t& t) noexcept +{ + return static_cast(t); +} + +template +inline constexpr T&& forward(remove_reference_t&& t) noexcept +{ + static_assert(!std::is_lvalue_reference::value, "can not forward an rvalue as an lvalue"); + return static_cast(t); +} + +template +constexpr remove_reference_t&& move(T&& t) noexcept +{ + return static_cast&&>(t); +} +#ifdef MPARK_INTEGER_SEQUENCE +template +using integer_sequence = std::integer_sequence; + +template +using index_sequence = std::index_sequence; + +template +using make_index_sequence = std::make_index_sequence; + +template +using index_sequence_for = std::index_sequence_for; +#else +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept { return sizeof...(Is); } +}; + +template +using index_sequence = integer_sequence; + +template +struct make_index_sequence_concat; + +template +struct make_index_sequence_concat, index_sequence> + : identity> +{ +}; + +template +struct make_index_sequence_impl; + +template +using make_index_sequence = typename make_index_sequence_impl::type; + +template +struct make_index_sequence_impl + : make_index_sequence_concat, make_index_sequence> +{ +}; + +template <> +struct make_index_sequence_impl<0> : identity> +{ +}; + +template <> +struct make_index_sequence_impl<1> : identity> +{ +}; + +template +using index_sequence_for = make_index_sequence; +#endif + +// +#ifdef MPARK_TRANSPARENT_OPERATORS +using equal_to = std::equal_to<>; +#else +struct equal_to +{ + template + inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const + RETURN(lib::forward(lhs) == lib::forward(rhs)) +}; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS +using not_equal_to = std::not_equal_to<>; +#else +struct not_equal_to +{ + template + inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const + RETURN(lib::forward(lhs) != lib::forward(rhs)) +}; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS +using less = std::less<>; +#else +struct less +{ + template + inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const + RETURN(lib::forward(lhs) < lib::forward(rhs)) +}; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS +using greater = std::greater<>; +#else +struct greater +{ + template + inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const + RETURN(lib::forward(lhs) > lib::forward(rhs)) +}; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS +using less_equal = std::less_equal<>; +#else +struct less_equal +{ + template + inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const + RETURN(lib::forward(lhs) <= lib::forward(rhs)) +}; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS +using greater_equal = std::greater_equal<>; +#else +struct greater_equal +{ + template + inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const + RETURN(lib::forward(lhs) >= lib::forward(rhs)) +}; +#endif +} // namespace cpp14 + +inline namespace cpp17 +{ +// +template +using bool_constant = std::integral_constant; + +template +using void_t = void; + +namespace detail +{ +namespace swappable +{ +using std::swap; + +template +struct is_swappable_impl +{ +private: + template (), std::declval()))> + inline static std::true_type test(int); + + template + inline static std::false_type test(...); + +public: + using type = decltype(test(0)); +}; + +template +using is_swappable = typename is_swappable_impl::type; + +template ::value> +struct is_nothrow_swappable +{ + static constexpr bool value = noexcept(swap(std::declval(), std::declval())); +}; + +template +struct is_nothrow_swappable : std::false_type +{ +}; + +} // namespace swappable +} // namespace detail + +template +using is_swappable = detail::swappable::is_swappable; + +template +using is_nothrow_swappable = detail::swappable::is_nothrow_swappable; + +// +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif +template +inline constexpr auto invoke(F&& f, As&&... as) RETURN(lib::forward(f)(lib::forward(as)...)) +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + template + inline constexpr auto invoke(T B::*pmv, D&& d) RETURN(lib::forward(d).*pmv) + + template + inline constexpr auto invoke(Pmv pmv, Ptr&& ptr) RETURN((*lib::forward(ptr)).*pmv) + + template + inline constexpr auto invoke(T B::*pmf, D&& d, As&&... as) + RETURN((lib::forward(d).*pmf)(lib::forward(as)...)) + + template + inline constexpr auto invoke(Pmf pmf, Ptr&& ptr, As&&... as) + RETURN(((*lib::forward(ptr)).*pmf)(lib::forward(as)...)) + + namespace detail +{ + template + struct invoke_result + { + }; + + template + struct invoke_result(), std::declval()...))>, F, + Args...> + : identity(), std::declval()...))> + { + }; + +} // namespace detail + +template +using invoke_result = detail::invoke_result; + +template +using invoke_result_t = typename invoke_result::type; + +namespace detail +{ +template +struct is_invocable : std::false_type +{ +}; + +template +struct is_invocable>, F, Args...> : std::true_type +{ +}; + +template +struct is_invocable_r : std::false_type +{ +}; + +template +struct is_invocable_r>, R, F, Args...> + : std::is_convertible, R> +{ +}; + +} // namespace detail + +template +using is_invocable = detail::is_invocable; + +template +using is_invocable_r = detail::is_invocable_r; + +// +#ifdef MPARK_BUILTIN_ADDRESSOF +template +inline constexpr T* addressof(T& arg) +{ + return __builtin_addressof(arg); +} +#else +namespace detail +{ +namespace has_addressof_impl +{ +struct fail; + +template +inline fail operator&(T&&); + +template +inline static constexpr bool impl() +{ + return (std::is_class::value || std::is_union::value) && + !std::is_same()), fail>::value; +} + +} // namespace has_addressof_impl + +template +using has_addressof = bool_constant()>; + +template +inline constexpr T* addressof(T& arg, std::true_type) +{ + return std::addressof(arg); +} + +template +inline constexpr T* addressof(T& arg, std::false_type) +{ + return &arg; +} + +} // namespace detail + +template +inline constexpr T* addressof(T& arg) +{ + return detail::addressof(arg, detail::has_addressof{}); +} +#endif + +template +inline constexpr T* addressof(const T&&) = delete; + +} // namespace cpp17 + +template +struct remove_all_extents : identity +{ +}; + +template +struct remove_all_extents> : remove_all_extents +{ +}; + +template +using remove_all_extents_t = typename remove_all_extents::type; + +template +using size_constant = std::integral_constant; + +template +using bool_sequence = integer_sequence; + +template +struct indexed_type : size_constant, identity +{ +}; + +template +using all = std::is_same, bool_sequence>; + +#ifdef MPARK_TYPE_PACK_ELEMENT +template +using type_pack_element_t = __type_pack_element; +#else +template +struct type_pack_element_impl +{ +private: + template + struct set; + + template + struct set> : indexed_type... + { + }; + + template + inline static std::enable_if impl(indexed_type); + + inline static std::enable_if impl(...); + +public: + using type = decltype(impl(set>{})); +}; + +template +using type_pack_element = typename type_pack_element_impl::type; + +template +using type_pack_element_t = typename type_pack_element::type; +#endif + +} // namespace lib +} // namespace mpark + +#undef RETURN + +#endif // MPARK_LIB_HPP + +namespace mpark +{ + +#ifdef MPARK_RETURN_TYPE_DEDUCTION + +#define AUTO auto +#define AUTO_RETURN(...) \ + { \ + return __VA_ARGS__; \ + } + +#define AUTO_REFREF auto&& +#define AUTO_REFREF_RETURN(...) \ + { \ + return __VA_ARGS__; \ + } + +#define DECLTYPE_AUTO decltype(auto) +#define DECLTYPE_AUTO_RETURN(...) \ + { \ + return __VA_ARGS__; \ + } + +#else + +#define AUTO auto +#define AUTO_RETURN(...) \ + ->lib::decay_t { return __VA_ARGS__; } +#define AUTO_REFREF auto +#define AUTO_REFREF_RETURN(...) \ + ->decltype((__VA_ARGS__)) \ + { \ + static_assert(std::is_reference::value, ""); \ + return __VA_ARGS__; \ + } + +#define DECLTYPE_AUTO auto +#define DECLTYPE_AUTO_RETURN(...) \ + ->decltype(__VA_ARGS__) { return __VA_ARGS__; } +#endif + +class bad_variant_access : public std::exception +{ +public: + virtual const char* what() const noexcept { return "bad_variant_access"; } +}; + +[[noreturn]] inline void throw_bad_variant_access() +{ +#ifdef MPARK_EXCEPTIONS + throw bad_variant_access{}; +#else + std::terminate(); +#endif +} + +template +class variant; + +template +struct variant_size; + +#ifdef MPARK_VARIABLE_TEMPLATES +template +constexpr std::size_t variant_size_v = variant_size::value; +#endif + +template +struct variant_size : variant_size +{ +}; + +template +struct variant_size : variant_size +{ +}; + +template +struct variant_size : variant_size +{ +}; + +template +struct variant_size> : lib::size_constant +{ +}; + +template +struct variant_alternative; + +template +using variant_alternative_t = typename variant_alternative::type; + +template +struct variant_alternative : std::add_const> +{ +}; + +template +struct variant_alternative : std::add_volatile> +{ +}; + +template +struct variant_alternative : std::add_cv> +{ +}; + +template +struct variant_alternative> : lib::identity> +{ + static_assert(I < sizeof...(Ts), "`variant_alternative` index out of range."); +}; + +constexpr std::size_t variant_npos = static_cast(-1); + +namespace detail +{ +inline constexpr bool all() +{ + return true; +} + +template +inline constexpr bool all(bool b, Bs... bs) +{ + return b && all(bs...); +} + +constexpr std::size_t not_found = static_cast(-1); +constexpr std::size_t ambiguous = static_cast(-2); + +#ifdef MPARK_CPP14_CONSTEXPR +template +inline constexpr std::size_t find_index() +{ + constexpr lib::array matches = {{std::is_same::value...}}; + std::size_t result = not_found; + for (std::size_t i = 0; i < sizeof...(Ts); ++i) + { + if (matches[i]) + { + if (result != not_found) + { + return ambiguous; + } + result = i; + } + } + return result; +} +#else +inline constexpr std::size_t find_index_impl(std::size_t result, std::size_t) +{ + return result; +} + +template +inline constexpr std::size_t find_index_impl(std::size_t result, std::size_t idx, bool b, Bs... bs) +{ + return b ? (result != not_found ? ambiguous : find_index_impl(idx, idx + 1, bs...)) : + find_index_impl(result, idx + 1, bs...); +} + +template +inline constexpr std::size_t find_index() +{ + return find_index_impl(not_found, 0, std::is_same::value...); +} +#endif + +template +using find_index_sfinae_impl = + lib::enable_if_t>; + +template +using find_index_sfinae = find_index_sfinae_impl()>; + +template +struct find_index_checked_impl : lib::size_constant +{ + static_assert(I != not_found, "the specified type is not found."); + static_assert(I != ambiguous, "the specified type is ambiguous."); +}; + +template +using find_index_checked = find_index_checked_impl()>; + +struct valueless_t +{ +}; + +enum class Trait +{ + TriviallyAvailable, + Available, + Unavailable +}; + +template class IsTriviallyAvailable, + template class IsAvailable> +inline constexpr Trait trait() +{ + return IsTriviallyAvailable::value ? Trait::TriviallyAvailable : IsAvailable::value ? + Trait::Available : + Trait::Unavailable; +} + +#ifdef MPARK_CPP14_CONSTEXPR +template +inline constexpr Trait common_trait(Traits... traits) +{ + Trait result = Trait::TriviallyAvailable; + for (Trait t : {traits...}) + { + if (static_cast(t) > static_cast(result)) + { + result = t; + } + } + return result; +} +#else +inline constexpr Trait common_trait_impl(Trait result) +{ + return result; +} + +template +inline constexpr Trait common_trait_impl(Trait result, Trait t, Traits... ts) +{ + return static_cast(t) > static_cast(result) ? common_trait_impl(t, ts...) : + common_trait_impl(result, ts...); +} + +template +inline constexpr Trait common_trait(Traits... ts) +{ + return common_trait_impl(Trait::TriviallyAvailable, ts...); +} +#endif + +template +struct traits +{ + static constexpr Trait copy_constructible_trait = common_trait( + trait()...); + + static constexpr Trait move_constructible_trait = common_trait( + trait()...); + + static constexpr Trait copy_assignable_trait = + common_trait(copy_constructible_trait, move_constructible_trait, + trait()...); + + static constexpr Trait move_assignable_trait = + common_trait(move_constructible_trait, + trait()...); + + static constexpr Trait destructible_trait = + common_trait(trait()...); +}; + +namespace access +{ +struct recursive_union +{ +#ifdef MPARK_RETURN_TYPE_DEDUCTION + template + inline static constexpr auto&& get_alt(V&& v, ::std::in_place_index_t<0>) + { + return lib::forward(v).head_; + } + + template + inline static constexpr auto&& get_alt(V&& v, ::std::in_place_index_t) + { + return get_alt(lib::forward(v).tail_, ::std::in_place_index_t{}); + } +#else + template + struct get_alt_impl + { + template + inline constexpr AUTO_REFREF operator()(V&& v) const + AUTO_REFREF_RETURN(get_alt_impl{}(lib::forward(v).tail_)) + }; + + template + struct get_alt_impl<0, Dummy> + { + template + inline constexpr AUTO_REFREF operator()(V&& v) const + AUTO_REFREF_RETURN(lib::forward(v).head_) + }; + + template + inline static constexpr AUTO_REFREF get_alt(V&& v, ::std::in_place_index_t) + AUTO_REFREF_RETURN(get_alt_impl{}(lib::forward(v))) +#endif +}; + +struct base +{ + template + inline static constexpr AUTO_REFREF get_alt(V&& v) + AUTO_REFREF_RETURN(recursive_union::get_alt(lib::forward(v).data_, + ::std::in_place_index_t{})) +}; + +struct variant +{ + template + inline static constexpr AUTO_REFREF get_alt(V&& v) + AUTO_REFREF_RETURN(base::get_alt(lib::forward(v).impl_)) +}; + +} // namespace access + +namespace visitation +{ +struct base +{ +private: + template + inline static constexpr const T& at(const T& elem) + { + return elem; + } + + template + inline static constexpr const lib::remove_all_extents_t& at(const lib::array& elems, + std::size_t i, Is... is) + { + return at(elems[i], is...); + } + + template + inline static constexpr int visit_visitor_return_type_check() + { + static_assert(all(std::is_same::value...), + "`mpark::visit` requires the visitor to have a single " + "return type."); + return 0; + } + + template + inline static constexpr lib::array...>, sizeof...(Fs)> + make_farray(Fs&&... fs) + { + using result = lib::array...>, sizeof...(Fs)>; + return visit_visitor_return_type_check...>(), + result{{lib::forward(fs)...}}; + } + + template + struct dispatcher + { + template + struct impl + { + inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs) + DECLTYPE_AUTO_RETURN(lib::invoke(static_cast(f), + access::base::get_alt(static_cast(vs))...)) + }; + }; + + template + inline static constexpr AUTO make_dispatch(lib::index_sequence) + AUTO_RETURN(&dispatcher::template impl::dispatch) + + template + inline static constexpr AUTO make_fdiagonal_impl() AUTO_RETURN( + make_dispatch(lib::index_sequence::value...>{})) + + template + inline static constexpr AUTO make_fdiagonal_impl(lib::index_sequence) + AUTO_RETURN(make_farray(make_fdiagonal_impl()...)) + + template + inline static constexpr /* auto * */ auto make_fdiagonal() + -> decltype(make_fdiagonal_impl( + lib::make_index_sequence::size()>{})) + { + static_assert(all((lib::decay_t::size() == lib::decay_t::size())...), + "all of the variants must be the same size."); + return make_fdiagonal_impl(lib::make_index_sequence::size()>{}); + } + +#ifdef MPARK_RETURN_TYPE_DEDUCTION + template + inline static constexpr auto make_fmatrix_impl(lib::index_sequence is) + { + return make_dispatch(is); + } + + template + inline static constexpr auto make_fmatrix_impl(lib::index_sequence, + lib::index_sequence, Ls... ls) + { + return make_farray(make_fmatrix_impl(lib::index_sequence{}, ls...)...); + } + + template + inline static constexpr auto make_fmatrix() + { + return make_fmatrix_impl(lib::index_sequence<>{}, + lib::make_index_sequence::size()>{}...); + } +#else + template + struct make_fmatrix_impl + { + template + struct impl; + + template + struct impl> + { + inline constexpr AUTO operator()() const + AUTO_RETURN(make_dispatch(lib::index_sequence{})) + }; + + template + struct impl, lib::index_sequence, Ls...> + { + inline constexpr AUTO operator()() const + AUTO_RETURN(make_farray(impl, Ls...>{}()...)) + }; + }; + + template + inline static constexpr AUTO make_fmatrix() + AUTO_RETURN( + typename make_fmatrix_impl::template impl< + lib::index_sequence<>, + lib::make_index_sequence::size()>...>{}()) +#endif + +public: + template + inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, Visitor&& visitor, + Vs&&... vs) + DECLTYPE_AUTO_RETURN( + at(make_fdiagonal(vs)))...>(), + index)(lib::forward(visitor), as_base(lib::forward(vs))...)) + + template + inline static constexpr DECLTYPE_AUTO + visit_alt(Visitor&& visitor, Vs&&... vs) DECLTYPE_AUTO_RETURN( + at(make_fmatrix(vs)))...>(), + vs.index()...)(lib::forward(visitor), as_base(lib::forward(vs))...)) +}; + +struct variant +{ +private: + template + struct visit_exhaustive_visitor_check + { + static_assert(lib::is_invocable::value, + "`mpark::visit` requires the visitor to be exhaustive."); + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + inline constexpr DECLTYPE_AUTO operator()(Visitor&& visitor, Values&&... values) const + DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward(visitor), + lib::forward(values)...)) +#ifdef _MSC_VER +#pragma warning(pop) +#endif + }; + + template + struct value_visitor + { + Visitor&& visitor_; + + template + inline constexpr DECLTYPE_AUTO operator()(Alts&&... alts) const DECLTYPE_AUTO_RETURN( + visit_exhaustive_visitor_check(alts).value))...>{}( + lib::forward(visitor_), lib::forward(alts).value...)) + }; + + template + inline static constexpr AUTO make_value_visitor(Visitor&& visitor) + AUTO_RETURN(value_visitor{lib::forward(visitor)}) + + public + : template + inline static constexpr DECLTYPE_AUTO + visit_alt_at(std::size_t index, Visitor&& visitor, Vs&&... vs) + DECLTYPE_AUTO_RETURN(base::visit_alt_at(index, lib::forward(visitor), + lib::forward(vs).impl_...)) + + template + inline static constexpr DECLTYPE_AUTO visit_alt(Visitor&& visitor, Vs&&... vs) + DECLTYPE_AUTO_RETURN(base::visit_alt(lib::forward(visitor), + lib::forward(vs).impl_...)) + + template + inline static constexpr DECLTYPE_AUTO + visit_value_at(std::size_t index, Visitor&& visitor, Vs&&... vs) + DECLTYPE_AUTO_RETURN(visit_alt_at(index, + make_value_visitor(lib::forward(visitor)), + lib::forward(vs)...)) + + template + inline static constexpr DECLTYPE_AUTO + visit_value(Visitor&& visitor, Vs&&... vs) + DECLTYPE_AUTO_RETURN(visit_alt(make_value_visitor(lib::forward(visitor)), + lib::forward(vs)...)) +}; + +} // namespace visitation + +template +struct alt +{ + using value_type = T; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + template + inline explicit constexpr alt(::std::in_place_t, Args&&... args) + : value(lib::forward(args)...) + { + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + T value; +}; + +template +union recursive_union; + +template +union recursive_union +{ +}; + +#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor) \ + template \ + union recursive_union \ + { \ + public: \ + inline explicit constexpr recursive_union(valueless_t) noexcept : dummy_{} {} \ + template \ + inline explicit constexpr recursive_union(::std::in_place_index_t<0>, Args&&... args) \ + : head_(::std::in_place_t{}, lib::forward(args)...) \ + { \ + } \ + \ + template \ + inline explicit constexpr recursive_union(::std::in_place_index_t, Args&&... args) \ + : tail_(::std::in_place_index_t{}, lib::forward(args)...) \ + { \ + } \ + \ + recursive_union(const recursive_union&) = default; \ + recursive_union(recursive_union&&) = default; \ + \ + destructor \ + \ + recursive_union& \ + operator=(const recursive_union&) = default; \ + recursive_union& operator=(recursive_union&&) = default; \ + \ + private: \ + char dummy_; \ + alt head_; \ + recursive_union tail_; \ + \ + friend struct access::recursive_union; \ + } + +MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable, ~recursive_union() = default;); +MPARK_VARIANT_RECURSIVE_UNION(Trait::Available, ~recursive_union(){}); +MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable, ~recursive_union() = delete;); + +#undef MPARK_VARIANT_RECURSIVE_UNION + +using index_t = unsigned int; + +template +class base +{ +public: + inline explicit constexpr base(valueless_t tag) noexcept : data_(tag), + index_(static_cast(-1)) + { + } + + template + inline explicit constexpr base(::std::in_place_index_t, Args&&... args) + : data_(::std::in_place_index_t{}, lib::forward(args)...), index_(I) + { + } + + inline constexpr bool valueless_by_exception() const noexcept + { + return index_ == static_cast(-1); + } + + inline constexpr std::size_t index() const noexcept + { + return valueless_by_exception() ? variant_npos : index_; + } + +protected: + friend inline constexpr base& as_base(base& b) { return b; } + friend inline constexpr const base& as_base(const base& b) { return b; } + friend inline constexpr base&& as_base(base&& b) { return lib::move(b); } + friend inline constexpr const base&& as_base(const base&& b) { return lib::move(b); } + inline static constexpr std::size_t size() { return sizeof...(Ts); } + recursive_union data_; + index_t index_; + + friend struct access::base; + friend struct visitation::base; +}; + +struct dtor +{ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + template + inline void operator()(Alt& alt) const noexcept + { + alt.~Alt(); + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif +}; + +#if defined(_MSC_VER) && _MSC_VER < 1910 +#define INHERITING_CTOR(type, base) \ + template \ + inline explicit constexpr type(Args&&... args) : base(lib::forward(args)...) \ + { \ + } +#else +#define INHERITING_CTOR(type, base) using base::base; +#endif + +template +class destructor; + +#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ + template \ + class destructor, destructible_trait> : public base \ + { \ + using super = base; \ + \ + public: \ + INHERITING_CTOR(destructor, super) \ + using super::operator=; \ + \ + destructor(const destructor&) = default; \ + destructor(destructor&&) = default; \ + definition destructor& operator=(const destructor&) = default; \ + destructor& operator=(destructor&&) = default; \ + \ + protected: \ + destroy \ + } + +MPARK_VARIANT_DESTRUCTOR(Trait::TriviallyAvailable, ~destructor() = default; + , inline void destroy() noexcept { + this->index_ = static_cast(-1); + }); + +MPARK_VARIANT_DESTRUCTOR(Trait::Available, ~destructor() { destroy(); }, + inline void destroy() noexcept { + if (!this->valueless_by_exception()) + { + visitation::base::visit_alt(dtor{}, *this); + } + this->index_ = static_cast(-1); + }); + +MPARK_VARIANT_DESTRUCTOR(Trait::Unavailable, ~destructor() = delete; + , inline void destroy() noexcept = delete;); + +#undef MPARK_VARIANT_DESTRUCTOR + +template +class constructor : public destructor +{ + using super = destructor; + +public: + INHERITING_CTOR(constructor, super) + using super::operator=; + +protected: +#ifndef MPARK_GENERIC_LAMBDAS + struct ctor + { + template + inline void operator()(LhsAlt& lhs_alt, RhsAlt&& rhs_alt) const + { + constructor::construct_alt(lhs_alt, lib::forward(rhs_alt).value); + } + }; +#endif + + template + inline static T& construct_alt(alt& a, Args&&... args) + { + ::new (static_cast(lib::addressof(a))) + alt(::std::in_place_t{}, lib::forward(args)...); + return a.value; + } + + template + inline static void generic_construct(constructor& lhs, Rhs&& rhs) + { + lhs.destroy(); + if (!rhs.valueless_by_exception()) + { + visitation::base::visit_alt_at(rhs.index(), +#ifdef MPARK_GENERIC_LAMBDAS + [](auto& lhs_alt, auto&& rhs_alt) { + constructor::construct_alt( + lhs_alt, lib::forward(rhs_alt).value); + } +#else + ctor {} +#endif + , + lhs, lib::forward(rhs)); + lhs.index_ = rhs.index_; + } + } +}; + +template +class move_constructor; + +#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ + template \ + class move_constructor, move_constructible_trait> \ + : public constructor> \ + { \ + using super = constructor>; \ + \ + public: \ + INHERITING_CTOR(move_constructor, super) \ + using super::operator=; \ + \ + move_constructor(const move_constructor&) = default; \ + definition ~move_constructor() = default; \ + move_constructor& operator=(const move_constructor&) = default; \ + move_constructor& operator=(move_constructor&&) = default; \ + } + +MPARK_VARIANT_MOVE_CONSTRUCTOR(Trait::TriviallyAvailable, + move_constructor(move_constructor&& that) = default;); + +MPARK_VARIANT_MOVE_CONSTRUCTOR(Trait::Available, + move_constructor(move_constructor&& that) noexcept( + all(std::is_nothrow_move_constructible::value...)) + : move_constructor(valueless_t{}) { + this->generic_construct(*this, lib::move(that)); + }); + +MPARK_VARIANT_MOVE_CONSTRUCTOR(Trait::Unavailable, move_constructor(move_constructor&&) = delete;); + +#undef MPARK_VARIANT_MOVE_CONSTRUCTOR + +template +class copy_constructor; + +#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ + template \ + class copy_constructor, copy_constructible_trait> \ + : public move_constructor> \ + { \ + using super = move_constructor>; \ + \ + public: \ + INHERITING_CTOR(copy_constructor, super) \ + using super::operator=; \ + \ + definition copy_constructor(copy_constructor&&) = default; \ + ~copy_constructor() = default; \ + copy_constructor& operator=(const copy_constructor&) = default; \ + copy_constructor& operator=(copy_constructor&&) = default; \ + } + +MPARK_VARIANT_COPY_CONSTRUCTOR(Trait::TriviallyAvailable, + copy_constructor(const copy_constructor& that) = default;); + +MPARK_VARIANT_COPY_CONSTRUCTOR(Trait::Available, copy_constructor(const copy_constructor& that) + : copy_constructor(valueless_t{}) { + this->generic_construct(*this, that); + }); + +MPARK_VARIANT_COPY_CONSTRUCTOR(Trait::Unavailable, + copy_constructor(const copy_constructor&) = delete;); + +#undef MPARK_VARIANT_COPY_CONSTRUCTOR + +template +class assignment : public copy_constructor +{ + using super = copy_constructor; + +public: + INHERITING_CTOR(assignment, super) + using super::operator=; + + template + inline /* auto & */ auto emplace(Args&&... args) + -> decltype(this->construct_alt(access::base::get_alt(*this), lib::forward(args)...)) + { + this->destroy(); + auto& result = + this->construct_alt(access::base::get_alt(*this), lib::forward(args)...); + this->index_ = I; + return result; + } + +protected: +#ifndef MPARK_GENERIC_LAMBDAS + template + struct assigner + { + template + inline void operator()(ThisAlt& this_alt, ThatAlt&& that_alt) const + { + self->assign_alt(this_alt, lib::forward(that_alt).value, + std::is_lvalue_reference{}); + } + assignment* self; + }; +#endif + + template + inline void assign_alt(alt& a, Arg&& arg, lib::bool_constant tag) + { + if (this->index() == I) + { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + a.value = lib::forward(arg); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + else + { + struct + { + void operator()(std::true_type) const { this_->emplace(T(lib::forward(arg_))); } + void operator()(std::false_type) const { this_->emplace(lib::forward(arg_)); } + assignment* this_; + Arg&& arg_; + } impl{this, lib::forward(arg)}; + impl(tag); + } + } + + template + inline void generic_assign(That&& that) + { + if (this->valueless_by_exception() && that.valueless_by_exception()) + { + // do nothing. + } + else if (that.valueless_by_exception()) + { + this->destroy(); + } + else + { + visitation::base::visit_alt_at( + that.index(), +#ifdef MPARK_GENERIC_LAMBDAS + [this](auto& this_alt, auto&& that_alt) { + this->assign_alt(this_alt, lib::forward(that_alt).value, + std::is_lvalue_reference{}); + } +#else + assigner { this } +#endif + , + *this, lib::forward(that)); + } + } +}; + +template +class move_assignment; + +#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ + template \ + class move_assignment, move_assignable_trait> : public assignment> \ + { \ + using super = assignment>; \ + \ + public: \ + INHERITING_CTOR(move_assignment, super) \ + using super::operator=; \ + \ + move_assignment(const move_assignment&) = default; \ + move_assignment(move_assignment&&) = default; \ + ~move_assignment() = default; \ + move_assignment& operator=(const move_assignment&) = default; \ + definition \ + } + +MPARK_VARIANT_MOVE_ASSIGNMENT(Trait::TriviallyAvailable, + move_assignment& operator=(move_assignment&& that) = default;); + +MPARK_VARIANT_MOVE_ASSIGNMENT(Trait::Available, + move_assignment& operator=(move_assignment&& that) noexcept( + all((std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value)...)) { + this->generic_assign(lib::move(that)); + return *this; + }); + +MPARK_VARIANT_MOVE_ASSIGNMENT(Trait::Unavailable, + move_assignment& operator=(move_assignment&&) = delete;); + +#undef MPARK_VARIANT_MOVE_ASSIGNMENT + +template +class copy_assignment; + +#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ + template \ + class copy_assignment, copy_assignable_trait> \ + : public move_assignment> \ + { \ + using super = move_assignment>; \ + \ + public: \ + INHERITING_CTOR(copy_assignment, super) \ + using super::operator=; \ + \ + copy_assignment(const copy_assignment&) = default; \ + copy_assignment(copy_assignment&&) = default; \ + ~copy_assignment() = default; \ + definition copy_assignment& operator=(copy_assignment&&) = default; \ + } + +MPARK_VARIANT_COPY_ASSIGNMENT(Trait::TriviallyAvailable, + copy_assignment& operator=(const copy_assignment& that) = default;); + +MPARK_VARIANT_COPY_ASSIGNMENT(Trait::Available, + copy_assignment& operator=(const copy_assignment& that) { + this->generic_assign(that); + return *this; + }); + +MPARK_VARIANT_COPY_ASSIGNMENT(Trait::Unavailable, + copy_assignment& operator=(const copy_assignment&) = delete;); + +#undef MPARK_VARIANT_COPY_ASSIGNMENT + +template +class impl : public copy_assignment> +{ + using super = copy_assignment>; + +public: + INHERITING_CTOR(impl, super) + using super::operator=; + + template + inline void assign(Arg&& arg) + { + this->assign_alt(access::base::get_alt(*this), lib::forward(arg), std::false_type{}); + } + + inline void swap(impl& that) + { + if (this->valueless_by_exception() && that.valueless_by_exception()) + { + // do nothing. + } + else if (this->index() == that.index()) + { + visitation::base::visit_alt_at(this->index(), +#ifdef MPARK_GENERIC_LAMBDAS + [](auto& this_alt, auto& that_alt) { + using std::swap; + swap(this_alt.value, that_alt.value); + } +#else + swapper {} +#endif + , + *this, that); + } + else + { + impl* lhs = this; + impl* rhs = lib::addressof(that); + if (lhs->move_nothrow() && !rhs->move_nothrow()) + { + std::swap(lhs, rhs); + } + impl tmp(lib::move(*rhs)); +#ifdef MPARK_EXCEPTIONS + // EXTENSION: When the move construction of `lhs` into `rhs` throws + // and `tmp` is nothrow move constructible then we move `tmp` back + // into `rhs` and provide the strong exception safety guarantee. + try + { + this->generic_construct(*rhs, lib::move(*lhs)); + } + catch (...) + { + if (tmp.move_nothrow()) + { + this->generic_construct(*rhs, lib::move(tmp)); + } + throw; + } +#else + this->generic_construct(*rhs, lib::move(*lhs)); +#endif + this->generic_construct(*lhs, lib::move(tmp)); + } + } + +private: +#ifndef MPARK_GENERIC_LAMBDAS + struct swapper + { + template + inline void operator()(ThisAlt& this_alt, ThatAlt& that_alt) const + { + using std::swap; + swap(this_alt.value, that_alt.value); + } + }; +#endif + + inline constexpr bool move_nothrow() const + { + return this->valueless_by_exception() || + lib::array{ + {std::is_nothrow_move_constructible::value...}}[this->index()]; + } +}; + +template +struct overload; + +template <> +struct overload<> +{ + void operator()() const {} +}; + +template +struct overload : overload +{ + using overload::operator(); + lib::identity operator()(T) const { return {}; } +}; + +template +using best_match_t = typename lib::invoke_result_t, T&&>::type; + +} // detail + +template +class variant +{ + static_assert(0 < sizeof...(Ts), "variant must consist of at least one alternative."); + + static_assert(lib::all::value...>::value, + "variant can not have an array type as an alternative."); + + static_assert(lib::all::value...>::value, + "variant can not have a reference type as an alternative."); + + static_assert(lib::all::value...>::value, + "variant can not have a void type as an alternative."); + +public: + template , + lib::enable_if_t::value, int> = 0> + inline constexpr variant() noexcept(std::is_nothrow_default_constructible::value) + : impl_(::std::in_place_index_t<0>{}) + { + } + + variant(const variant&) = default; + variant(variant&&) = default; + + template , variant>::value, int> = 0, + typename T = detail::best_match_t, + std::size_t I = detail::find_index_sfinae::value, + lib::enable_if_t::value, int> = 0> + inline constexpr variant(Arg&& arg) noexcept(std::is_nothrow_constructible::value) + : impl_(::std::in_place_index_t{}, lib::forward(arg)) + { + } + + template , + lib::enable_if_t::value, int> = 0> + inline explicit constexpr variant(::std::in_place_index_t, Args&&... args) noexcept( + std::is_nothrow_constructible::value) + : impl_(::std::in_place_index_t{}, lib::forward(args)...) + { + } + + template , + lib::enable_if_t&, Args...>::value, + int> = 0> + inline explicit constexpr variant( + ::std::in_place_index_t, std::initializer_list il, + Args&&... args) noexcept(std::is_nothrow_constructible&, + Args...>::value) + : impl_(::std::in_place_index_t{}, il, lib::forward(args)...) + { + } + + template ::value, + lib::enable_if_t::value, int> = 0> + inline explicit constexpr variant(::std::in_place_type_t, Args&&... args) noexcept( + std::is_nothrow_constructible::value) + : impl_(::std::in_place_index_t{}, lib::forward(args)...) + { + } + + template ::value, + lib::enable_if_t&, Args...>::value, + int> = 0> + inline explicit constexpr variant( + ::std::in_place_type_t, std::initializer_list il, + Args&&... args) noexcept(std::is_nothrow_constructible&, + Args...>::value) + : impl_(::std::in_place_index_t{}, il, lib::forward(args)...) + { + } + + ~variant() = default; + + variant& operator=(const variant&) = default; + variant& operator=(variant&&) = default; + + template < + typename Arg, lib::enable_if_t, variant>::value, int> = 0, + typename T = detail::best_match_t, + std::size_t I = detail::find_index_sfinae::value, + lib::enable_if_t<(std::is_assignable::value && std::is_constructible::value), + int> = 0> + inline variant& operator=(Arg&& arg) noexcept((std::is_nothrow_assignable::value && + std::is_nothrow_constructible::value)) + { + impl_.template assign(lib::forward(arg)); + return *this; + } + + template , + lib::enable_if_t::value, int> = 0> + inline T& emplace(Args&&... args) + { + return impl_.template emplace(lib::forward(args)...); + } + + template , + lib::enable_if_t&, Args...>::value, + int> = 0> + inline T& emplace(std::initializer_list il, Args&&... args) + { + return impl_.template emplace(il, lib::forward(args)...); + } + + template ::value, + lib::enable_if_t::value, int> = 0> + inline T& emplace(Args&&... args) + { + return impl_.template emplace(lib::forward(args)...); + } + + template ::value, + lib::enable_if_t&, Args...>::value, + int> = 0> + inline T& emplace(std::initializer_list il, Args&&... args) + { + return impl_.template emplace(il, lib::forward(args)...); + } + + inline constexpr bool valueless_by_exception() const noexcept + { + return impl_.valueless_by_exception(); + } + + inline constexpr std::size_t index() const noexcept { return impl_.index(); } + template ::value && + lib::is_swappable::value)...>::value, + int> = 0> + inline void + swap(variant& that) noexcept(lib::all<(std::is_nothrow_move_constructible::value && + lib::is_nothrow_swappable::value)...>::value) + { + impl_.swap(that.impl_); + } + +private: + detail::impl impl_; + + friend struct detail::access::variant; + friend struct detail::visitation::variant; +}; + +template +inline constexpr bool holds_alternative(const variant& v) noexcept +{ + return v.index() == I; +} + +template +inline constexpr bool holds_alternative(const variant& v) noexcept +{ + return holds_alternative::value>(v); +} + +namespace detail +{ +template +struct generic_get_impl +{ + constexpr generic_get_impl(int) {} + constexpr AUTO_REFREF operator()(V&& v) const + AUTO_REFREF_RETURN(access::variant::get_alt(lib::forward(v)).value) +}; + +template +inline constexpr AUTO_REFREF generic_get(V&& v) AUTO_REFREF_RETURN(generic_get_impl( + holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))(lib::forward(v))) +} // namespace detail + +template +inline constexpr variant_alternative_t>& get(variant& v) +{ + return detail::generic_get(v); +} + +template +inline constexpr variant_alternative_t>&& get(variant&& v) +{ + return detail::generic_get(lib::move(v)); +} + +template +inline constexpr const variant_alternative_t>& get(const variant& v) +{ + return detail::generic_get(v); +} + +template +inline constexpr const variant_alternative_t>&& get(const variant&& v) +{ + return detail::generic_get(lib::move(v)); +} + +template +inline constexpr T& get(variant& v) +{ + return get::value>(v); +} + +template +inline constexpr T&& get(variant&& v) +{ + return get::value>(lib::move(v)); +} + +template +inline constexpr const T& get(const variant& v) +{ + return get::value>(v); +} + +template +inline constexpr const T&& get(const variant&& v) +{ + return get::value>(lib::move(v)); +} + +namespace detail +{ +template +inline constexpr /* auto * */ AUTO generic_get_if(V* v) noexcept AUTO_RETURN( + v&& holds_alternative(*v) ? lib::addressof(access::variant::get_alt(*v).value) : nullptr) + +} // namespace detail + +template +inline constexpr lib::add_pointer_t>> +get_if(variant* v) noexcept +{ + return detail::generic_get_if(v); +} + +template +inline constexpr lib::add_pointer_t>> +get_if(const variant* v) noexcept +{ + return detail::generic_get_if(v); +} + +template +inline constexpr lib::add_pointer_t get_if(variant* v) noexcept +{ + return get_if::value>(v); +} + +template +inline constexpr lib::add_pointer_t get_if(const variant* v) noexcept +{ + return get_if::value>(v); +} + +template +inline constexpr bool operator==(const variant& lhs, const variant& rhs) +{ + using detail::visitation::variant; + using lib::equal_to; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.index() != rhs.index()) + return false; + if (lhs.valueless_by_exception()) + return true; + return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs); +#else + return lhs.index() == rhs.index() && (lhs.valueless_by_exception() || + variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); +#endif +} + +template +inline constexpr bool operator!=(const variant& lhs, const variant& rhs) +{ + using detail::visitation::variant; + using lib::not_equal_to; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.index() != rhs.index()) + return true; + if (lhs.valueless_by_exception()) + return false; + return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs); +#else + return lhs.index() != rhs.index() || + (!lhs.valueless_by_exception() && + variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); +#endif +} + +template +inline constexpr bool operator<(const variant& lhs, const variant& rhs) +{ + using detail::visitation::variant; + using lib::less; +#ifdef MPARK_CPP14_CONSTEXPR + if (rhs.valueless_by_exception()) + return false; + if (lhs.valueless_by_exception()) + return true; + if (lhs.index() < rhs.index()) + return true; + if (lhs.index() > rhs.index()) + return false; + return variant::visit_value_at(lhs.index(), less{}, lhs, rhs); +#else + return !rhs.valueless_by_exception() && + (lhs.valueless_by_exception() || lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); +#endif +} + +template +inline constexpr bool operator>(const variant& lhs, const variant& rhs) +{ + using detail::visitation::variant; + using lib::greater; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.valueless_by_exception()) + return false; + if (rhs.valueless_by_exception()) + return true; + if (lhs.index() > rhs.index()) + return true; + if (lhs.index() < rhs.index()) + return false; + return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs); +#else + return !lhs.valueless_by_exception() && + (rhs.valueless_by_exception() || lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); +#endif +} + +template +inline constexpr bool operator<=(const variant& lhs, const variant& rhs) +{ + using detail::visitation::variant; + using lib::less_equal; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.valueless_by_exception()) + return true; + if (rhs.valueless_by_exception()) + return false; + if (lhs.index() < rhs.index()) + return true; + if (lhs.index() > rhs.index()) + return false; + return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs); +#else + return lhs.valueless_by_exception() || + (!rhs.valueless_by_exception() && + (lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); +#endif +} + +template +inline constexpr bool operator>=(const variant& lhs, const variant& rhs) +{ + using detail::visitation::variant; + using lib::greater_equal; +#ifdef MPARK_CPP14_CONSTEXPR + if (rhs.valueless_by_exception()) + return true; + if (lhs.valueless_by_exception()) + return false; + if (lhs.index() > rhs.index()) + return true; + if (lhs.index() < rhs.index()) + return false; + return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs); +#else + return rhs.valueless_by_exception() || + (!lhs.valueless_by_exception() && + (lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs)))); +#endif +} + +template +inline constexpr DECLTYPE_AUTO visit(Visitor&& visitor, Vs&&... vs) DECLTYPE_AUTO_RETURN( + (detail::all(!vs.valueless_by_exception()...) ? (void)0 : throw_bad_variant_access()), + detail::visitation::variant::visit_value(lib::forward(visitor), + lib::forward(vs)...)) + + struct monostate +{ +}; + +inline constexpr bool operator<(monostate, monostate) noexcept +{ + return false; +} + +inline constexpr bool operator>(monostate, monostate) noexcept +{ + return false; +} + +inline constexpr bool operator<=(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator>=(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator==(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator!=(monostate, monostate) noexcept +{ + return false; +} + +template +inline auto swap(variant& lhs, variant& rhs) noexcept(noexcept(lhs.swap(rhs))) + -> decltype(lhs.swap(rhs)) +{ + lhs.swap(rhs); +} + +namespace detail +{ +template +using enabled_type = T; + +namespace hash +{ +template +constexpr bool meets_requirements() +{ + return std::is_copy_constructible::value && std::is_move_constructible::value && + lib::is_invocable_r::value; +} + +template +constexpr bool is_enabled() +{ + using H = std::hash; + return meets_requirements() && std::is_default_constructible::value && + std::is_copy_assignable::value && std::is_move_assignable::value; +} + +} // namespace hash + +} // namespace detail + +#undef AUTO +#undef AUTO_RETURN + +#undef AUTO_REFREF +#undef AUTO_REFREF_RETURN + +#undef DECLTYPE_AUTO +#undef DECLTYPE_AUTO_RETURN + +} // namespace mpark + +namespace std +{ +template +struct hash, mpark::lib::enable_if_t>()...>::value>>> +{ + using argument_type = mpark::variant; + using result_type = std::size_t; + + inline result_type operator()(const argument_type& v) const + { + using mpark::detail::visitation::variant; + std::size_t result = v.valueless_by_exception() ? + 299792458 // Random value chosen by the universe upon creation + : + variant::visit_alt( +#ifdef MPARK_GENERIC_LAMBDAS + [](const auto& alt) { + using alt_type = mpark::lib::decay_t; + using value_type = + mpark::lib::remove_const_t; + return hash{}(alt.value); + } +#else + hasher {} +#endif + , + v); + return hash_combine(result, hash{}(v.index())); + } + +private: +#ifndef MPARK_GENERIC_LAMBDAS + struct hasher + { + template + inline std::size_t operator()(const Alt& alt) const + { + using alt_type = mpark::lib::decay_t; + using value_type = mpark::lib::remove_const_t; + return hash{}(alt.value); + } + }; +#endif + + static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) + { + return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); + } +}; + +template <> +struct hash +{ + using argument_type = mpark::monostate; + using result_type = std::size_t; + + inline result_type operator()(const argument_type&) const noexcept + { + return 66740831; // return a fundamentally attractive random value. + } +}; + +} // namespace std + +namespace std +{ +using mpark::variant; +using mpark::visit; +using mpark::holds_alternative; +using mpark::get; +using mpark::get_if; +using mpark::monostate; +using mpark::bad_variant_access; +using mpark::variant_size; +using mpark::variant_alternative; +using mpark::variant_alternative_t; +} // namespace std +#endif