diff options
Diffstat (limited to 'include/EASTL/variant.h')
-rw-r--r-- | include/EASTL/variant.h | 1588 |
1 files changed, 0 insertions, 1588 deletions
diff --git a/include/EASTL/variant.h b/include/EASTL/variant.h deleted file mode 100644 index a7af97b..0000000 --- a/include/EASTL/variant.h +++ /dev/null @@ -1,1588 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright (c) Electronic Arts Inc. All rights reserved. -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////// -// Implements the class template variant represents a type-safe union. An -// instance of variant at any given time either holds a value of one of its -// alternative types, or it holds no value. -// -// As with unions, if a variant holds a value of some object type T, the object -// representation of T is allocated directly within the object representation of -// the variant itself. -// -// Variant is not allowed to allocate additional (dynamic) memory. -// -// A variant is not permitted to hold references, arrays, or the type void. -// Empty variants are also ill-formed (variant<monostate> can be used instead). -// -// A variant is permitted to hold the same type more than once, and to hold -// differently cv-qualified versions of the same type. As with unions, the -// default-initialized variant holds a value of its first alternative, unless -// that alternative is not default-constructible (in which case default -// constructor won't compile: the helper class monostate can be used to make -// such variants default-constructible) -// -// Given defect 2901, the eastl::variant implementation does not provide the -// specified allocator-aware functions. This will be re-evaluated when the LWG -// addresses this issue in future standardization updates. -// LWG Defect 2901: https://cplusplus.github.io/LWG/issue2901 -// -// Allocator-extended constructors -// template <class Alloc> variant(allocator_arg_t, const Alloc&); -// template <class Alloc> variant(allocator_arg_t, const Alloc&, const variant&); -// template <class Alloc> variant(allocator_arg_t, const Alloc&, variant&&); -// template <class Alloc, class T> variant(allocator_arg_t, const Alloc&, T&&); -// template <class Alloc, class T, class... Args> variant(allocator_arg_t, const Alloc&, in_place_type_t<T>, Args&&...); -// template <class Alloc, class T, class U, class... Args> variant(allocator_arg_t, const Alloc&, in_place_type_t<T>, initializer_list<U>, Args&&...); -// template <class Alloc, size_t I, class... Args> variant(allocator_arg_t, const Alloc&, in_place_index_t<I>, Args&&...); -// template <class Alloc, size_t I, class U, class... Args> variant(allocator_arg_t, const Alloc&, in_place_index_t<I>, initializer_list<U>, Args&&...); -// -// 20.7.12, allocator-related traits -// template <class T, class Alloc> struct uses_allocator; -// template <class... Types, class Alloc> struct uses_allocator<variant<Types...>, Alloc>; -// -// eastl::variant doesn't support: -// * recursive variant support -// * strong exception guarantees as specified (we punted on the assignment problem). -// if an exception is thrown during assignment its undefined behaviour in our implementation. -// -// Reference: -// * http://en.cppreference.com/w/cpp/utility/variant -// * https://thenewcpp.wordpress.com/2012/02/15/variadic-templates-part-3-or-how-i-wrote-a-variant-class/ -/////////////////////////////////////////////////////////////////////////// - -#ifndef EASTL_VARIANT_H -#define EASTL_VARIANT_H - -#include <EASTL/internal/config.h> -#include <EASTL/internal/in_place_t.h> -#include <EASTL/internal/integer_sequence.h> -#include <EASTL/meta.h> -#include <EASTL/utility.h> -#include <EASTL/functional.h> -#include <EASTL/initializer_list.h> -#include <EASTL/tuple.h> -#include <EASTL/type_traits.h> -#include <EASTL/array.h> - -#if EASTL_EXCEPTIONS_ENABLED - #include <stdexcept> - #include <exception> -#endif - - -#if defined(EA_PRAGMA_ONCE_SUPPORTED) - #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. -#endif - -#ifndef EA_COMPILER_CPP14_ENABLED - static_assert(false, "eastl::variant requires a C++14 compatible compiler (at least) "); -#endif - -EA_DISABLE_VC_WARNING(4625) // copy constructor was implicitly defined as deleted - -namespace eastl -{ - namespace internal - { - /////////////////////////////////////////////////////////////////////////// - // default_construct_if_supported<T> - // - // Utility class to remove default constructor calls for types that - // do not support default construction. - // - // We can remove these utilities when C++17 'constexpr if' is available. - // - template<typename T, bool = eastl::is_default_constructible_v<T>> - struct default_construct_if_supported - { - static void call(T* pThis) - { - new (pThis) T(); - } - }; - - template<typename T> - struct default_construct_if_supported<T, false> - { - static void call(T*) {} // intentionally blank - }; - - /////////////////////////////////////////////////////////////////////////// - // destroy_if_supported<T> - // - // Utility class to remove default constructor calls for types that - // do not support default construction. - // - // We can remove these utilities when C++17 'constexpr if' is available. - // - template<typename T, bool = eastl::is_destructible_v<T>> - struct destroy_if_supported - { - static void call(T* pThis) - { - pThis->~T(); - } - }; - - template<typename T> - struct destroy_if_supported<T, false> - { - static void call(T* pThis) {} // intentionally blank - }; - - /////////////////////////////////////////////////////////////////////////// - // copy_if_supported<T> - // - // Utility class to remove copy constructor calls for types that - // do not support copying. - // - // We can remove these utilities when C++17 'constexpr if' is available. - // - template<typename T, bool = eastl::is_copy_constructible_v<T>> - struct copy_if_supported - { - static void call(T* pThis, T* pOther) - { - new (pThis) T(*pOther); - } - }; - - template<typename T> - struct copy_if_supported<T, false> - { - static void call(T* pThis, T* pOther) {} // intentionally blank - }; - - /////////////////////////////////////////////////////////////////////////// - // move_if_supported<T> - // - // Utility class to remove move constructor calls for types that - // do not support moves. - // - // We can remove these utilities when C++17 'constexpr if' is available. - // - template<typename T, bool = eastl::is_move_constructible_v<T>> - struct move_if_supported - { - static void call(T* pThis, T* pOther) - { - new (pThis) T(eastl::move(*pOther)); - } - }; - - template<typename T> - struct move_if_supported<T, false> - { - static void call(T* pThis, T* pOther) {} // intentionally blank - }; - } // namespace internal - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.3, variant_npos - // - EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t variant_npos = size_t(-1); - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.10, class bad_variant_access - // - #if EASTL_EXCEPTIONS_ENABLED - struct bad_variant_access : public std::logic_error - { - bad_variant_access() : std::logic_error("eastl::bad_variant_access exception") {} - virtual ~bad_variant_access() EA_NOEXCEPT {} - }; - #endif - - - /////////////////////////////////////////////////////////////////////////// - // TODO(rparolin): JUST COPY/PASTE THIS CODE - // - inline void CheckVariantCondition(bool b) - { - EA_UNUSED(b); - #if EASTL_EXCEPTIONS_ENABLED - if (!b) - throw bad_variant_access(); - #elif EASTL_ASSERT_ENABLED - EASTL_ASSERT_MSG(b, "eastl::bad_variant_access assert"); - #endif - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.7, class monostate - // - // Unit type intended for use as a well-behaved empty alternative in - // variant. A variant of non-default-constructible types may list monostate - // as its first alternative: this makes the variant itself default-contructible. - // - struct monostate {}; - - // 20.7.8, monostate relational operators -#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) - EA_CONSTEXPR std::strong_ordering operator<=>(monostate, monostate) EA_NOEXCEPT { return std::strong_ordering::equal; } -#else - EA_CONSTEXPR bool operator> (monostate, monostate) EA_NOEXCEPT { return false; } - EA_CONSTEXPR bool operator< (monostate, monostate) EA_NOEXCEPT { return false; } - EA_CONSTEXPR bool operator!=(monostate, monostate) EA_NOEXCEPT { return false; } - EA_CONSTEXPR bool operator<=(monostate, monostate) EA_NOEXCEPT { return true; } - EA_CONSTEXPR bool operator>=(monostate, monostate) EA_NOEXCEPT { return true; } -#endif - EA_CONSTEXPR bool operator==(monostate, monostate) EA_NOEXCEPT { return true; } - - // 20.7.11, hash support - template <class T> struct hash; - template <> struct hash<monostate> - { size_t operator()(monostate) const { return static_cast<size_t>(-0x42); } }; - - - /////////////////////////////////////////////////////////////////////////// - // variant_storage - // - // This is a utility class to simplify the implementation of a storage type - // for a distriminted union. This utility handles the alignment, size - // requirements, and data access required by the variant type. - // - template<bool IsTriviallyDestructible, class... Types> - struct variant_storage; - - - // variant_storage - // - // specialization for non-trivial types (must call constructors and destructors) - // - template<class... Types> - struct variant_storage<false, Types...> - { - enum class StorageOp - { - DEFAULT_CONSTRUCT, - DESTROY, - COPY, - MOVE - }; - - // handler function - using storage_handler_ptr = void(*)(StorageOp, void*, void*); - using aligned_storage_impl_t = aligned_union_t<16, Types...>; - - aligned_storage_impl_t mBuffer; - storage_handler_ptr mpHandler = nullptr; - - template<typename VariantStorageT> - inline void DoOp(StorageOp op, VariantStorageT&& other) // bind to both rvalue and lvalues - { - if(mpHandler) - DoOp(StorageOp::DESTROY); - - if (other.mpHandler) - mpHandler = other.mpHandler; - - if(mpHandler) - mpHandler(op, (void*)&mBuffer, (void*)&other.mBuffer); - } - - inline void DoOp(StorageOp op) - { - if(mpHandler) - mpHandler(op, &mBuffer, nullptr); - } - - template<typename T> - static void DoOpImpl(StorageOp op, T* pThis, T* pOther) - { - switch (op) - { - case StorageOp::DEFAULT_CONSTRUCT: - { - internal::default_construct_if_supported<T>::call(pThis); - } - break; - - case StorageOp::DESTROY: - { - internal::destroy_if_supported<T>::call(pThis); - } - break; - - case StorageOp::COPY: - { - internal::copy_if_supported<T>::call(pThis, pOther); - } - break; - - case StorageOp::MOVE: - { - internal::move_if_supported<T>::call(pThis, pOther); - } - break; - - default: {} break; - }; - } - - public: - variant_storage() - { - DoOp(StorageOp::DEFAULT_CONSTRUCT); - } - - ~variant_storage() - { - DoOp(StorageOp::DESTROY); - } - - variant_storage(const variant_storage& other) - { - DoOp(StorageOp::COPY, other); - } - - variant_storage(variant_storage&& other) - { - DoOp(StorageOp::MOVE, other); - } - - variant_storage& operator=(const variant_storage& other) - { - DoOp(StorageOp::COPY, other); - return *this; - } - - variant_storage& operator=(variant_storage&& other) - { - DoOp(StorageOp::MOVE, eastl::move(other)); - return *this; - } - - template <typename T, typename... Args> - void set_as(Args&&... args) - { - // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which - // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. - static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); - - using RT = remove_reference_t<T>; - - new (&mBuffer) RT(eastl::forward<Args>(args)...); - - mpHandler = (storage_handler_ptr)&DoOpImpl<RT>; - } - - template <typename T, typename U, typename... Args> - void set_as(std::initializer_list<U> il, Args&&... args) - { - // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which - // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. - static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); - - using RT = remove_reference_t<T>; - - new (&mBuffer) RT(il, eastl::forward<Args>(args)...); - - mpHandler = (storage_handler_ptr)&DoOpImpl<RT>; - } - - template<typename T> - T get_as() - { - static_assert(eastl::is_pointer_v<T>, "T must be a pointer type"); - return reinterpret_cast<T>(&mBuffer); - } - - template<typename T> - const T get_as() const - { - static_assert(eastl::is_pointer_v<T>, "T must be a pointer type"); - return reinterpret_cast<const T>(reinterpret_cast<uintptr_t>(&mBuffer)); - } - - void destroy() - { - DoOp(StorageOp::DESTROY); - } - }; - - - // variant_storage - // - // specialization for trivial types - // - template<class... Types> - struct variant_storage<true, Types...> - { - using aligned_storage_impl_t = aligned_union_t<16, Types...>; - aligned_storage_impl_t mBuffer; - - public: - - // NOTE(rparolin): Since this is the specialization for trivial types can we potentially remove all the - // defaulted special constructors. Consider removing this. - // - // variant_storage() = default; - // ~variant_storage() = default; - // variant_storage(const variant_storage& other) = default; - // variant_storage(variant_storage&& other) = default; - // variant_storage& operator=(const variant_storage& other) = default; - // variant_storage& operator=(variant_storage&& other) = default; - - template <typename T, typename... Args> - void set_as(Args&&... args) - { - // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which - // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. - static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); - new (&mBuffer) remove_reference_t<T>(eastl::forward<Args>(args)...); - - // mpHandler = ...; // member does not exist in this template specialization - } - - template <typename T, typename U, typename... Args> - void set_as(std::initializer_list<U> il, Args&&... args) - { - // NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which - // variant_storage used to store types. The size selected should be large enough to hold the largest type in - // the user provided variant type-list. - static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size"); - new (&mBuffer) remove_reference_t<T>(il, eastl::forward<Args>(args)...); - - // mpHandler = ...; // member does not exist in this template specialization - } - - template<typename T> - T get_as() - { - static_assert(eastl::is_pointer_v<T>, "T must be a pointer type"); - return reinterpret_cast<T>(&mBuffer); - } - - template<typename T> - const T get_as() const - { - static_assert(eastl::is_pointer_v<T>, "T must be a pointer type"); - return reinterpret_cast<const T>(reinterpret_cast<uintptr_t>(&mBuffer)); - } - - void destroy() {} - }; - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2, forward-declaration for types that depend on the variant - // - template <class... Types> - class variant; - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.3, variant_size, variant_size_v helper classes - // - template <class T> struct variant_size; - template <class T> struct variant_size<const T> : integral_constant<size_t, variant_size<T>::value> {}; - template <class T> struct variant_size<volatile T> : integral_constant<size_t, variant_size<T>::value> {}; - template <class T> struct variant_size<const volatile T> : integral_constant<size_t, variant_size<T>::value> {}; - template <class... Types> struct variant_size<variant<Types...>> : integral_constant<size_t, sizeof...(Types)> {}; - - // variant_size_v template alias - template <typename T> - EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t variant_size_v = variant_size<T>::value; - - - /////////////////////////////////////////////////////////////////////////// - // variant_alternative_helper - // - // This helper does the heavy lifting of traversing the variadic type list - // and retrieving the type at the user provided index. - // - template <size_t I, typename... Ts> - struct variant_alternative_helper; - - template <size_t I, typename Head, typename... Tail> - struct variant_alternative_helper<I, Head, Tail...> - { typedef typename variant_alternative_helper<I - 1, Tail...>::type type; }; - - template <typename Head, typename... Tail> - struct variant_alternative_helper<0, Head, Tail...> - { typedef Head type; }; - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.4, variant_alternative - // - template <size_t I, class T> struct variant_alternative; - template <size_t I, class... Types> struct variant_alternative<I, variant<Types...>> : variant_alternative_helper<I, Types...> {}; - - // ISO required cv-qualifer specializations - template <size_t I, class T> struct variant_alternative<I, const T> : add_const<typename variant_alternative<I, T>::type> {}; - template <size_t I, class T> struct variant_alternative<I, volatile T> : add_volatile<typename variant_alternative<I, T>::type> {}; - template <size_t I, class T> struct variant_alternative<I, const volatile T> : add_cv<typename variant_alternative<I, T>::type> {}; - - // variant_alternative_t template alias - template <size_t I, class T> using variant_alternative_t = typename variant_alternative<I, T>::type; - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.11, hash support - // - template <class... Types> - struct hash<variant<Types...> > - { size_t operator()(const variant<Types...>& val) const { return static_cast<size_t>(-0x42); } }; - - - /////////////////////////////////////////////////////////////////////////// - // get_if - // - template <size_t I, class... Types> - EA_CONSTEXPR add_pointer_t<variant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>* pv) EA_NOEXCEPT - { - static_assert(I < sizeof...(Types), "get_if is ill-formed if I is not a valid index in the variant typelist"); - using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - return (!pv || pv->index() != I) ? nullptr : pv->mStorage.template get_as<return_type>(); - } - - template <size_t I, class... Types> - EA_CONSTEXPR add_pointer_t<const variant_alternative_t<I, variant<Types...>>> get_if(const variant<Types...>* pv) EA_NOEXCEPT - { - static_assert(I < sizeof...(Types), "get_if is ill-formed if I is not a valid index in the variant typelist"); - using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - return (!pv || pv->index() != I) ? nullptr : pv->mStorage.template get_as<return_type>(); - } - - template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR add_pointer_t<T> get_if(variant<Types...>* pv) EA_NOEXCEPT - { - return get_if<I>(pv); - } - - template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR add_pointer_t<const T> get_if(const variant<Types...>* pv) EA_NOEXCEPT - { - return get_if<I>(pv); - } - - - /////////////////////////////////////////////////////////////////////////// - // get - // - template <size_t I, class... Types> - EA_CONSTEXPR variant_alternative_t<I, variant<Types...>>& get(variant<Types...>& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - return *v.mStorage.template get_as<return_type>(); - } - - template <size_t I, class... Types> - EA_CONSTEXPR variant_alternative_t<I, variant<Types...>>&& get(variant<Types...>&& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - return eastl::move(*v.mStorage.template get_as<return_type>()); - } - - template <size_t I, class... Types> - EA_CONSTEXPR const variant_alternative_t<I, variant<Types...>>& get(const variant<Types...>& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - return *v.mStorage.template get_as<return_type>(); - } - - template <size_t I, class... Types> - EA_CONSTEXPR const variant_alternative_t<I, variant<Types...>>&& get(const variant<Types...>&& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>; - - return eastl::move(*v.mStorage.template get_as<return_type>()); - } - - template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR T& get(variant<Types...>& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - return get<I>(v); - } - - template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR T&& get(variant<Types...>&& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - return get<I>(eastl::move(v)); - } - - template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR const T& get(const variant<Types...>& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - return get<I>(v); - } - - template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR const T&& get(const variant<Types...>&& v) - { - static_assert(I < sizeof...(Types), "get is ill-formed if I is not a valid index in the variant typelist"); - return get<I>(v); - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.4, value access - // - template <class T, class... Types, ssize_t I = meta::get_type_index_v<T, Types...>> - EA_CONSTEXPR bool holds_alternative(const variant<Types...>& v) EA_NOEXCEPT - { - // ssize_t template parameter because the value can be negative - return I == variant_npos ? false : (v.index() == I); - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2, variant - // - template <class... Types> - class variant - { - static_assert(sizeof...(Types) > 0, "variant must have at least 1 type (empty variants are ill-formed)"); - static_assert(disjunction_v<is_void<Types>...> == false, "variant does not allow void as an alternative type"); - static_assert(disjunction_v<is_reference<Types>...> == false, "variant does not allow references as an alternative type"); - static_assert(disjunction_v<is_array<Types>...> == false, "variant does not allow arrays as an alternative type"); - - using variant_index_t = size_t; - using variant_storage_t = variant_storage<conjunction_v<is_trivially_destructible<Types>...>, Types...>; - using T_0 = variant_alternative_t<0, variant<Types...>>; // alias for the 1st type in the variadic pack - - /////////////////////////////////////////////////////////////////////////// - // variant data members - // - variant_index_t mIndex; - variant_storage_t mStorage; - - public: - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.1, constructors - // - - // Only participates in overload resolution when the first alternative is default constructible - template <typename TT0 = T_0, typename = enable_if_t<is_default_constructible_v<TT0>>> - EA_CONSTEXPR variant() EA_NOEXCEPT : mIndex(variant_npos), mStorage() - { - mIndex = static_cast<variant_index_t>(0); - mStorage.template set_as<T_0>(); - } - - // Only participates in overload resolution if is_copy_constructible_v<T_i> is true for all T_i in Types.... - template <bool enable = conjunction_v<is_copy_constructible<Types>...>, - typename = enable_if_t<enable>> // add a dependent type to enable sfinae - variant(const variant& other) - { - if (this != &other) - { - mIndex = other.mIndex; - mStorage = other.mStorage; - } - } - - // Only participates in overload resolution if is_move_constructible_v<T_i> is true for all T_i in Types... - template <bool enable = conjunction_v<is_move_constructible<Types>...>, typename = enable_if_t<enable>> // add a dependent type to enable sfinae - EA_CONSTEXPR variant(variant&& other) EA_NOEXCEPT(conjunction_v<is_move_constructible<Types>...>) - : mIndex(variant_npos), mStorage() - { - if(this != &other) - { - mIndex = other.mIndex; - mStorage = eastl::move(other.mStorage); - } - } - - // Conversion constructor - template <typename T, - typename T_j = meta::overload_resolution_t<T, meta::overload_set<Types...>>, - typename = enable_if_t<!is_same_v<decay_t<T>, variant>>, - size_t I = meta::get_type_index_v<decay_t<T_j>, Types...>> - EA_CONSTEXPR variant(T&& t) EA_NOEXCEPT(is_nothrow_constructible_v<T_j, T>) - : mIndex(variant_npos), mStorage() - { - static_assert(I >= 0, "T not found in type-list."); - static_assert((meta::type_count_v<T_j, Types...> == 1), "function overload is not unique - duplicate types in type list"); - - mIndex = static_cast<variant_index_t>(I); - mStorage.template set_as<T_j>(eastl::forward<T>(t)); - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.1, in_place_t constructors - // - template < - class T, - class... Args, - class = enable_if_t<conjunction_v<meta::duplicate_type_check<T, Types...>, is_constructible<T, Args...>>, T>> - EA_CPP14_CONSTEXPR explicit variant(in_place_type_t<T>, Args&&... args) - : variant(in_place<meta::get_type_index_v<T, Types...>>, eastl::forward<Args>(args)...) - {} - - template < - class T, - class U, - class... Args, - class = enable_if_t<conjunction_v<meta::duplicate_type_check<T, Types...>, is_constructible<T, Args...>>, T>> - EA_CPP14_CONSTEXPR explicit variant(in_place_type_t<T>, std::initializer_list<U> il, Args&&... args) - : variant(in_place<meta::get_type_index_v<T, Types...>>, il, eastl::forward<Args>(args)...) - {} - - template <size_t I, - class... Args, - class = enable_if_t<conjunction_v<integral_constant<bool, (I < sizeof...(Types))>, - is_constructible<meta::get_type_at_t<I, Types...>, Args...>>>> - EA_CPP14_CONSTEXPR explicit variant(in_place_index_t<I>, Args&&... args) - : mIndex(I) - { - mStorage.template set_as<meta::get_type_at_t<I, Types...>>(eastl::forward<Args>(args)...); - } - - template <size_t I, - class U, - class... Args, - class = enable_if_t<conjunction_v<integral_constant<bool, (I < sizeof...(Types))>, - is_constructible<meta::get_type_at_t<I, Types...>, Args...>>>> - EA_CPP14_CONSTEXPR explicit variant(in_place_index_t<I>, std::initializer_list<U> il, Args&&... args) - : mIndex(I) - { - mStorage.template set_as<meta::get_type_at_t<I, Types...>>(il, eastl::forward<Args>(args)...); - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.2, destructor - // - ~variant() = default; - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.4, modifiers - // - - // Equivalent to emplace<I>(std::forward<Args>(args)...), where I is the zero-based index of T in Types.... - // This overload only participates in overload resolution if std::is_constructible_v<T, Args...> is true, and T - // occurs exactly once in Types... - template < - class T, - class... Args, - size_t I = meta::get_type_index_v<T, Types...>, - typename = enable_if_t<conjunction_v<is_constructible<T, Args...>, meta::duplicate_type_check<T, Types...>>>> - decltype(auto) emplace(Args&&... args) - { - return emplace<I>(eastl::forward<Args>(args)...); - } - - // Equivalent to emplace<I>(il, std::forward<Args>(args)...), where I is the zero-based index of T in Types.... - // This overload only participates in overload resolution if std::is_constructible_v<T, - // std::initializer_list<U>&, Args...> is true, and T occurs exactly once in Types... - template <class T, - class U, - class... Args, - size_t I = meta::get_type_index_v<T, Types...>, - typename = enable_if_t<conjunction_v<is_constructible<T, std::initializer_list<U>&, Args...>, - meta::duplicate_type_check<T, Types...>>>> - decltype(auto) emplace(std::initializer_list<U> il, Args&&... args) - { - return emplace<I>(il, eastl::forward<T>(args)...); - } - - // First, destroys the currently contained value (if any). Then direct-initializes the contained value as if - // constructing a value of type T_I with the arguments std::forward<Args>(args).... If an exception is thrown, - // *this may become valueless_by_exception. This overload only participates in overload resolution if - // std::is_constructible_v<T_I, Args...> is true. The behavior is undefined if I is not less than - // sizeof...(Types). - // - template <size_t I, - class... Args, - typename T = meta::get_type_at_t<I, Types...>, - typename = - enable_if_t<conjunction_v<is_constructible<T, Args...>, meta::duplicate_type_check<T, Types...>>>> - variant_alternative_t<I, variant>& emplace(Args&&... args) - { - if (!valueless_by_exception()) - { - mStorage.destroy(); - - #if EASTL_EXCEPTIONS_ENABLED - mIndex = static_cast<variant_index_t>(variant_npos); - #endif - } - - mStorage.template set_as<T>(eastl::forward<Args>(args)...); - mIndex = static_cast<variant_index_t>(I); - return *reinterpret_cast<T*>(&mStorage.mBuffer); - } - - // First, destroys the currently contained value (if any). Then direct-initializes the contained value as if - // constructing a value of type T_I with the arguments il, std::forward<Args>(args).... If an exception is - // thrown, *this may become valueless_by_exception. This overload only participates in overload resolution if - // std::is_constructible_v<T_I, initializer_list<U>&, Args...> is true. The behavior is undefined if I is not - // less than sizeof...(Types). - // - template <size_t I, - class U, - class... Args, - typename T = meta::get_type_at_t<I, Types...>, - typename = enable_if_t<conjunction_v<is_constructible<T, std::initializer_list<U>&, Args...>, meta::duplicate_type_check<T, Types...>>>> - variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il, Args&&... args) - { - if (!valueless_by_exception()) - { - mStorage.destroy(); - - #if EASTL_EXCEPTIONS_ENABLED - mIndex = static_cast<variant_index_t>(variant_npos); - #endif - } - - mStorage.template set_as<T>(il, eastl::forward<Args>(args)...); - mIndex = static_cast<variant_index_t>(I); - return *reinterpret_cast<T*>(&mStorage.mBuffer); - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.3, assignment - // - template <class T, - typename T_j = meta::overload_resolution_t<T, meta::overload_set<Types...>>, - ssize_t I = meta::get_type_index_v<decay_t<T_j>, Types...>, - typename = enable_if_t<!eastl::is_same_v<decay_t<T>, variant> && eastl::is_assignable_v<T_j&, T> && - eastl::is_constructible_v<T_j, T>>> - EA_CPP14_CONSTEXPR variant& operator=(T&& t) - EA_NOEXCEPT(conjunction_v<is_nothrow_assignable<T_j&, T>, is_nothrow_constructible<T_j, T>>) - { - static_assert(I >= 0, "T not found in type-list."); - static_assert((meta::type_count_v<T_j, Types...> == 1), - "function overload is not unique - duplicate types in type list"); - - if (!valueless_by_exception()) - mStorage.destroy(); - - mIndex = static_cast<variant_index_t>(I); - mStorage.template set_as<T_j>(eastl::forward<T>(t)); - return *this; - } - - - // Only participates in overload resolution if is_copy_constructible_v<T_i> && is_copy_assignable_v<T_i> is true - // for all T_i in Types.... - template <bool enable = conjunction_v<conjunction<is_copy_constructible<Types>...>, - conjunction<is_copy_assignable<Types>...>>, - typename = enable_if_t<enable>> // add a dependent type to enable sfinae - variant& operator=(const variant& other) - { - if (this != &other) - { - mIndex = other.mIndex; - mStorage = other.mStorage; - } - return *this; - } - - // Only participates in overload resolution if is_move_constructible_v<T_i> && is_move_assignable_v<T_i> is true for all T_i in Types.... - template <bool enable = conjunction_v<conjunction<is_move_constructible<Types>...>, - conjunction<is_move_assignable<Types>...>>, - typename = enable_if_t<enable>> // add a dependent type to enable sfinae - variant& operator=(variant&& other) - EA_NOEXCEPT(conjunction_v<conjunction<is_nothrow_move_constructible<Types>...>, - conjunction<is_nothrow_move_assignable<Types>...>>) - { - if (this != &other) - { - mIndex = eastl::move(other.mIndex); - mStorage = eastl::move(other.mStorage); - } - return *this; - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.5, value status - // - EA_CONSTEXPR size_t index() const EA_NOEXCEPT - { - #if EASTL_EXCEPTIONS_ENABLED - return valueless_by_exception() ? variant_npos : mIndex; - #else - return mIndex; - #endif - } - - EA_CONSTEXPR bool valueless_by_exception() const EA_NOEXCEPT - { - #if EASTL_EXCEPTIONS_ENABLED - return mIndex == variant_npos; - #else - return false; - #endif - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.2.6, swap - // - void swap(variant& other) - EA_NOEXCEPT(conjunction_v<is_nothrow_move_constructible<Types>..., is_nothrow_swappable<Types>...>) - { - eastl::swap(mIndex, other.mIndex); - eastl::swap(mStorage, other.mStorage); - } - - private: - // NOTE(rparolin): get_if accessors require internal access to the variant storage class - template <size_t I, class... Types2> friend EA_CONSTEXPR add_pointer_t< variant_alternative_t<I, variant<Types2...>>> get_if( variant<Types2...>* pv) EA_NOEXCEPT; - template <size_t I, class... Types2> friend EA_CONSTEXPR add_pointer_t<const variant_alternative_t<I, variant<Types2...>>> get_if(const variant<Types2...>* pv) EA_NOEXCEPT; - - // NOTE(rparolin): get accessors require internal access to the variant storage class - template <size_t I, class... Types2> friend EA_CONSTEXPR variant_alternative_t<I, variant<Types2...>>& get(variant<Types2...>& v); - template <size_t I, class... Types2> friend EA_CONSTEXPR variant_alternative_t<I, variant<Types2...>>&& get(variant<Types2...>&& v); - template <size_t I, class... Types2> friend EA_CONSTEXPR const variant_alternative_t<I, variant<Types2...>>& get(const variant<Types2...>& v); - template <size_t I, class... Types2> friend EA_CONSTEXPR const variant_alternative_t<I, variant<Types2...>>&& get(const variant<Types2...>&& v); - }; - - /////////////////////////////////////////////////////////////////////////// - // 20.7.9, swap - // - template <class... Types> - void swap(variant<Types...>& lhs, variant<Types...>& rhs) - EA_NOEXCEPT(EA_NOEXCEPT(lhs.swap(rhs))) - { - lhs.swap(rhs); - } - - - // visit is a bit convoluted, in order to fulfill a few requirements: - // - It must support visiting multiple variants using a single visitor and a single function call. The - // visitor in this case should have one function for each possible combination of types: - // - // struct MyVisitor { - // void operator()(int, int); - // void operator()(string, string); - // void operator()(int, string); - // void operator()(string, int); - // }; - // - // variant<int, string> a = 42; - // variant<int, string> b = "hello"; - // visit(MyVisitor{}, a, b); // calls MyVisitor::operator()(int, string) - // - // - It must be declared constexpr - // - It must be constant-time for the case of visiting a single variant - // - // - 20.7.7 states that variant visitation requires all combinations of visitors to return the same type. - // - // NOTE(mwinkler): - // Visit creates an N-Dimensional matrix whereby each dimension is M wide. - // Where N == sizeof...(Variants) and M == variant_size_v<Variant> - // - // variant<int, bool, float> v; - // visit(Visitor{}, v, v); - // - // This creates a 3x3 matrix of potential visitors. - // The argument indices into the variants are as follows. - // [0, 0], [0, 1], [0, 2] - // [1, 0], [1, 1], [1, 2] - // [2, 0], [2, 1], [2, 2] - // - // These indices are compile-time constants but the variants have a runtime index. - // Therefore we must instantiate an NxNxN... matrix of function pointers who are - // templated on the indices based on their position in the matrix and then - // at runtime index into the array to call the correct function pointer that can - // get the correct alternatives in the variants. - // - // There are a couple of ways to do this. We can construct the matrix bottom up or top down. - // - // Constructing a matrix bottom up would look something as follows. - // - // make_visitor_matrix_recurse(eastl::index_sequence<>{}, eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variants>>>{}...); - // - // make_visitor_matrix_recurse(eastl::index_sequence<Is...>) { return templated function pointer on Is... } - // - // make_visitor_matrix_recurse(eastl::index_sequence<Is...>, eastl::index_sequence<Js...>, RestIndex... rest) - // return make_array(make_visitor_matrix_recurse(eastl::index_sequence<Is..., Js>{}, rest...)...); - // - // Essentially we construct the matrix bottom up, row by row of indices and return an array of function pointers. - // The end result is a NxNxN... array on the stack which can be indexed by each variant in order as follows, - // array[v0.index()][v1.index()][vn.index()](); - // - // The downside with this approach is the massive NxNxN... array that is created on the stack. - // - // The other approach is to build the matrix top down and use tail recursion to ensure there is only one - // N sized array on the stack. The downside here is the extra function calls, but we feel this approach provides - // a good balance between performance and memory usage. - // - // We construct the matrix top down by first creating an N sized array that is indexed by the first variant. - // This calls a function that recursively creates another N sized array that is indexed by the second variant. - // The recursion continues until we reach the base case which is the last variant. At this point we know - // the compile-time value of the N indices needed to get each alternative from each variant to invoke the visitor upon. - // Essentially we create a tree of function pointers like so. - // - // - // +------------------------------------------------------------------+ - // | | - // | 0 1 N | - // | | - // | | - // +----+---------------------------+---------------------------------+ - // | | - // | | - // | | - // | | - // | | - // +--------------------------+-----------------+ +----+------------------------------------+ - // | | | | - // |0,0 0,1 0,N| |1,0 1,1 1,N| - // | | | | - // | | | | - // +--------------------------------------------+ +-----------------------------------------+ - // - // Essentially each call creates a N sized array of function pointers that is the concatention of the indices known so far - // and the index of itself in the array whereby the leaf function pointer does the final invoke of the visitor. - // - - // Since decltype() is not one of the contexts where an overloaded function can be used without arguments; - // We use this function to deduce the function pointer types. - // We also return an eastl::array<> since we cannot return C-style arrays as value types. - template <typename T> - static EA_CONSTEXPR array<decay_t<T>, 1> make_visitor_array(T&& t) - { - return { { eastl::forward<T>(t) } }; - } - - template <typename T, typename... Ts> - static EA_CONSTEXPR array<decay_t<T>, sizeof...(Ts) + 1> make_visitor_array(T&& t, Ts&&... ts) - { - static_assert(conjunction_v<is_same<decay_t<T>, decay_t<Ts>>...>, "`visit` variant visitation requires that all visitors have the same return type!"); - - return { { eastl::forward<T>(t), eastl::forward<Ts>(ts)... } }; - } - - - template <size_t N, typename Variant, typename... Variants, eastl::enable_if_t<N == 0, int> = 0> - static EA_CONSTEXPR decltype(auto) get_variant_n(Variant&& variant, Variants&&... variants) - { - return eastl::forward<Variant>(variant); - } - - template <size_t N, typename Variant, typename... Variants, eastl::enable_if_t<N != 0, int> = 0> - static EA_CONSTEXPR decltype(auto) get_variant_n(Variant&& variant, Variants&&... variants) - { - return get_variant_n<N - 1>(eastl::forward<Variants>(variants)...); - } - - - template <typename Visitor, typename Index, typename Array, typename... Variants> - static EA_CONSTEXPR decltype(auto) call_visitor_at_index(Array&& array, Index index, Visitor&& visitor, Variants&&... variants) - { - return array[static_cast<typename Array::size_type>(index)](eastl::forward<Visitor>(visitor), eastl::forward<Variants>(variants)...); - } - - template <size_t VariantsIndex, typename Visitor, typename Array, typename... Variants> - static EA_CONSTEXPR decltype(auto) call_visitor_at(Array&& array, Visitor&& visitor, Variants&&... variants) - { - return call_visitor_at_index(eastl::forward<Array>(array), - get_variant_n<VariantsIndex>(eastl::forward<Variants>(variants)...).index(), - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - - // abstracts calling visit on 2 or more variants - template <typename VariantIndexSequence, typename Visitor, typename... Variants> - struct visitor_caller_n; - - template <typename Visitor, typename... Variants, size_t... VariantIndices> - struct visitor_caller_n<index_sequence<VariantIndices...>, Visitor, Variants...> - { - using return_type = invoke_result_t<Visitor, variant_alternative_t<0, remove_reference_t<Variants>>...>; - - template <size_t... VariantArgIndices> - static EA_CONSTEXPR return_type invoke_visitor_leaf(Visitor&& visitor, Variants&&... variants) - { - return eastl::invoke(eastl::forward<Visitor>(visitor), - eastl::get<VariantArgIndices>(eastl::forward<Variants>(variants))...); - } - - template <size_t... VariantArgIndices> - static EA_CONSTEXPR auto make_invoke_visitor_leaf(index_sequence<VariantArgIndices...>) - { - return &invoke_visitor_leaf<VariantArgIndices...>; - } - - - template <size_t... VariantArgIndices> - static EA_CONSTEXPR return_type invoke_visitor_recurse(Visitor&& visitor, Variants&&... variants) - { - return call(index_sequence<VariantArgIndices...>{}, - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - template <size_t... VariantArgIndices> - static EA_CONSTEXPR auto make_invoke_visitor_recurse(index_sequence<VariantArgIndices...>) - { - return &invoke_visitor_recurse<VariantArgIndices...>; - } - - - template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 == sizeof...(Variants), int> = 0> - static EA_CPP14_CONSTEXPR decltype(auto) call(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) - { - EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_leaf(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); - - return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 != sizeof...(Variants), int> = 0> - static EA_CPP14_CONSTEXPR decltype(auto) call(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) - { - EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_recurse(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); - - return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - }; - - template <typename VariantIndexSequence, typename Visitor, typename... Variants> - static EA_CONSTEXPR decltype(auto) call_initial_n(VariantIndexSequence, Visitor&& visitor, Variants&&... variants) - { - return visitor_caller_n<VariantIndexSequence, Visitor, Variants...>::call(index_sequence<>{}, eastl::forward<Visitor>(visitor), eastl::forward<Variants>(variants)...); - } - - - // abstracts calling visit on 2 or more variants with return types convertible to R - template <typename R, typename VariantIndexSequence, typename Visitor, typename... Variants> - struct visitor_caller_n_r; - - template <typename R, size_t... VariantIndices, typename Visitor, typename... Variants> - struct visitor_caller_n_r<R, index_sequence<VariantIndices...>, Visitor, Variants...> - { - template <typename R_, size_t... VariantArgIndices> - struct visitor_leaf_r - { - static EA_CONSTEXPR R_ invoke_visitor_leaf_r(Visitor&& visitor, Variants&&... variants) - { - return eastl::invoke(eastl::forward<Visitor>(visitor), - eastl::get<VariantArgIndices>(eastl::forward<Variants>(variants))...); - } - }; - - // void return type must discard the return values of the visitor even if the visitor returns a value. - template <size_t... VariantArgIndices> - struct visitor_leaf_r<void, VariantArgIndices...> - { - static EA_CONSTEXPR void invoke_visitor_leaf_r(Visitor&& visitor, Variants&&... variants) - { - eastl::invoke(eastl::forward<Visitor>(visitor), - eastl::get<VariantArgIndices>(eastl::forward<Variants>(variants))...); - } - }; - template <size_t... VariantArgIndices> struct visitor_leaf_r<const void, VariantArgIndices...> : public visitor_leaf_r<void, VariantArgIndices...> {}; - template <size_t... VariantArgIndices> struct visitor_leaf_r<volatile void, VariantArgIndices...> : public visitor_leaf_r<void, VariantArgIndices...> {}; - template <size_t... VariantArgIndices> struct visitor_leaf_r<const volatile void, VariantArgIndices...> : public visitor_leaf_r<void, VariantArgIndices...> {}; - - template <typename R_, size_t... VariantArgIndices> - static EA_CONSTEXPR auto make_invoke_visitor_leaf_r(index_sequence<VariantArgIndices...>) - { - return &visitor_leaf_r<R_, VariantArgIndices...>::invoke_visitor_leaf_r; - } - - - template <typename R_, size_t... VariantArgIndices> - struct visitor_recurse_r - { - static EA_CONSTEXPR R_ invoke_visitor_recurse_r(Visitor&& visitor, Variants&&... variants) - { - return call_r(index_sequence<VariantArgIndices...>{}, - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - }; - - template <typename R_, size_t... VariantArgIndices> - static EA_CONSTEXPR auto make_invoke_visitor_recurse_r(index_sequence<VariantArgIndices...>) - { - return &visitor_recurse_r<R_, VariantArgIndices...>::invoke_visitor_recurse_r; - } - - - template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 == sizeof...(Variants), int> = 0> - static EA_CPP14_CONSTEXPR decltype(auto) call_r(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) - { - EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_leaf_r<R>(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); - - return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - template <typename VariantArgIndexSequence, enable_if_t<internal::index_sequence_size_v<VariantArgIndexSequence> + 1 != sizeof...(Variants), int> = 0> - static EA_CPP14_CONSTEXPR decltype(auto) call_r(VariantArgIndexSequence, Visitor&& visitor, Variants&&... variants) - { - EA_CPP14_CONSTEXPR auto callers = make_visitor_array(make_invoke_visitor_recurse_r<R>(meta::double_pack_expansion_t<VariantArgIndexSequence, VariantIndices>{})...); - - return call_visitor_at<internal::index_sequence_size_v<VariantArgIndexSequence>>(eastl::move(callers), - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - }; - - template <typename R, typename VariantIndexSequence, typename Visitor, typename... Variants> - static EA_CONSTEXPR decltype(auto) call_initial_n_r(VariantIndexSequence, Visitor&& visitor, Variants&&... variants) - { - return visitor_caller_n_r<R, VariantIndexSequence, Visitor, Variants...>::call_r(index_sequence<>{}, eastl::forward<Visitor>(visitor), eastl::forward<Variants>(variants)...); - } - - - // abstracts calling visit on a single variant - struct visitor_caller_one - { - - template <typename Visitor, typename Variant, size_t I> - static EA_CONSTEXPR decltype(auto) invoke_visitor(Visitor&& visitor, Variant&& variant) - { - return eastl::invoke(eastl::forward<Visitor>(visitor), - eastl::get<I>(eastl::forward<Variant>(variant))); - } - - template <typename Visitor, typename Variant, size_t... VariantArgIndices> - static EA_CPP14_CONSTEXPR decltype(auto) call_index(Visitor&& visitor, Variant&& variant, index_sequence<VariantArgIndices...>) - { - EA_CPP14_CONSTEXPR auto callers = make_visitor_array((&invoke_visitor<Visitor, Variant, VariantArgIndices>)...); - - return call_visitor_at_index(eastl::move(callers), eastl::forward<Variant>(variant).index(), - eastl::forward<Visitor>(visitor), eastl::forward<Variant>(variant)); - } - - template <typename Visitor, typename Variant> - static EA_CONSTEXPR decltype(auto) call(Visitor&& visitor, Variant&& variant) - { - return call_index(eastl::forward<Visitor>(visitor), - eastl::forward<Variant>(variant), - make_index_sequence<variant_size_v<decay_t<Variant>>>{}); - } - - }; - - template <typename R> - struct visitor_r - { - template <typename Visitor, typename Variant, size_t I> - static EA_CONSTEXPR R invoke_visitor_r(Visitor&& visitor, Variant&& variant) - { - return eastl::invoke(eastl::forward<Visitor>(visitor), - eastl::get<I>(eastl::forward<Variant>(variant))); - } - }; - - // void return type must discard the return values of the visitor even if the visitor returns a value. - template <> - struct visitor_r<void> - { - template <typename Visitor, typename Variant, size_t I> - static EA_CONSTEXPR void invoke_visitor_r(Visitor&& visitor, Variant&& variant) - { - eastl::invoke(eastl::forward<Visitor>(visitor), - eastl::get<I>(eastl::forward<Variant>(variant))); - } - }; - - template<> struct visitor_r<const void> : public visitor_r<void> {}; - template<> struct visitor_r<volatile void> : public visitor_r<void> {}; - template<> struct visitor_r<const volatile void> : public visitor_r<void> {}; - - // abstracts calling visit on a single variant with return types convertible to R - struct visitor_caller_one_r - { - template <typename R, typename Visitor, typename Variant, size_t... VariantArgIndices> - static EA_CPP14_CONSTEXPR decltype(auto) call_index_r(Visitor&& visitor, Variant&& variant, eastl::index_sequence<VariantArgIndices...>) - { - EA_CPP14_CONSTEXPR auto callers = make_visitor_array(&visitor_r<R>::template invoke_visitor_r<Visitor, Variant, VariantArgIndices>...); - - return callers[static_cast<typename decltype(callers)::size_type>(eastl::forward<Variant>(variant).index())](eastl::forward<Visitor>(visitor), - eastl::forward<Variant>(variant)); - } - - template <typename R, typename Visitor, typename Variant> - static EA_CONSTEXPR decltype(auto) call_r(Visitor&& visitor, Variant&& variant) - { - return call_index_r<R>(eastl::forward<Visitor>(visitor), eastl::forward<Variant>(variant), eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variant>>>()); - } - - }; - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.6, visitation - // - // Example: - // struct MyVisitor - // { - // auto operator()(int) {}; - // auto operator()(long) {}; - // auto operator()(string) {}; - // }; - // - // variant<int, long, string> v = "Hello, Variant"; - // visit(MyVisitor{}, v); // calls MyVisitor::operator()(string) {} - // - - template <typename... Variants> - static EA_CPP14_CONSTEXPR void visit_throw_bad_variant_access(Variants&&... variants) - { - #if EASTL_EXCEPTIONS_ENABLED - using bool_array_type = bool[]; - bool badAccess = false; - - (void)bool_array_type{ (badAccess |= eastl::forward<Variants>(variants).valueless_by_exception(), false)... }; - - if (badAccess) - { - throw bad_variant_access(); - } - #endif - } - - template <typename... Variants> - static EA_CONSTEXPR void visit_static_assert_check(Variants&&... variants) - { - static_assert(sizeof...(Variants) > 0, "`visit` at least one variant instance must be passed as an argument to the visit function"); - - using variant_type = decay_t<meta::get_type_at_t<0, Variants...>>; - static_assert(conjunction_v<is_same<variant_type, decay_t<Variants>>...>, - "`visit` all variants passed to eastl::visit() must have the same type"); - } - - - // visit - // - template <typename Visitor, typename Variant> - EA_CPP14_CONSTEXPR decltype(auto) visit(Visitor&& visitor, Variant&& variant) - { - visit_static_assert_check(eastl::forward<Variant>(variant)); - - visit_throw_bad_variant_access(eastl::forward<Variant>(variant)); - - return visitor_caller_one::call(eastl::forward<Visitor>(visitor), - eastl::forward<Variant>(variant)); - } - - template <typename Visitor, typename... Variants> - EA_CPP14_CONSTEXPR decltype(auto) visit(Visitor&& visitor, Variants&&... variants) - { - visit_static_assert_check(eastl::forward<Variants>(variants)...); - - visit_throw_bad_variant_access(eastl::forward<Variants>(variants)...); - - return call_initial_n(make_index_sequence<variant_size_v<decay_t<meta::get_type_at_t<0, Variants...>>>>{}, - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - - } - - template <typename R, typename Visitor, typename Variant, eastl::enable_if_t<!eastl::is_same_v<R, Visitor>, int> = 0> - EA_CPP14_CONSTEXPR R visit(Visitor&& visitor, Variant&& variant) - { - visit_static_assert_check(eastl::forward<Variant>(variant)); - - visit_throw_bad_variant_access(eastl::forward<Variant>(variant)); - - return visitor_caller_one_r::call_r<R>(eastl::forward<Visitor>(visitor), - eastl::forward<Variant>(variant)); - } - - template <typename R, typename Visitor, typename... Variants, eastl::enable_if_t<!eastl::is_same_v<R, Visitor>, int> = 0> - EA_CPP14_CONSTEXPR R visit(Visitor&& visitor, Variants&&... variants) - { - visit_static_assert_check(eastl::forward<Variants>(variants)...); - - visit_throw_bad_variant_access(eastl::forward<Variants>(variants)...); - - return call_initial_n_r<R>(make_index_sequence<variant_size_v<decay_t<meta::get_type_at_t<0, Variants...>>>>{}, - eastl::forward<Visitor>(visitor), - eastl::forward<Variants>(variants)...); - } - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.5, relational operators - // - namespace internal - { - - // For relational operators we do not need to create the NxN matrix of comparisons since we know already - // that both the lhs and rhs variants have the same index. We just need to compare the value of the types at that - // index for equality. Therefore the visitation is simpler than visit() for relational operators. - // - struct variant_relational_comparison - { - template <typename Compare, size_t I, typename Variant> - static EA_CONSTEXPR bool invoke_relational_visitor(const Variant& lhs, const Variant& rhs) - { - return eastl::invoke(Compare{}, eastl::get<I>(lhs), eastl::get<I>(rhs)); - } - - template <typename Compare, typename Variant, size_t... VariantArgIndices> - static EA_CPP14_CONSTEXPR bool call_index(const Variant& lhs, const Variant& rhs, eastl::index_sequence<VariantArgIndices...>) - { - using invoke_relational_visitor_func_ptr = bool (*)(const Variant&, const Variant&); - - EA_CPP14_CONSTEXPR invoke_relational_visitor_func_ptr visitors[] = { static_cast<invoke_relational_visitor_func_ptr>(&invoke_relational_visitor<Compare, VariantArgIndices, Variant>)... }; - - return visitors[lhs.index()](lhs, rhs); - } - - template <typename Compare, typename Variant> - static EA_CONSTEXPR bool call(const Variant& lhs, const Variant& rhs) - { - return call_index<Compare>(lhs, rhs, eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variant>>>()); - } - -#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) - template <typename Compare, size_t I, typename Variant> - static EA_CONSTEXPR std::compare_three_way_result_t<Variant> invoke_relational_visitor_three_way(const Variant& lhs, const Variant& rhs) - { - return eastl::invoke(Compare{}, eastl::get<I>(lhs), eastl::get<I>(rhs)); - } - - template <typename Compare, typename Variant, size_t... VariantArgIndices> - static EA_CONSTEXPR std::compare_three_way_result_t<Variant> call_index_three_way(const Variant& lhs, const Variant& rhs, eastl::index_sequence<VariantArgIndices...>) - { - using invoke_relational_visitor_func_ptr = std::compare_three_way_result_t<Variant> (*)(const Variant&, const Variant&); - - EA_CONSTEXPR invoke_relational_visitor_func_ptr visitors[] = {static_cast<invoke_relational_visitor_func_ptr>(&invoke_relational_visitor_three_way<Compare, VariantArgIndices, Variant>)...}; - - return visitors[lhs.index()](lhs, rhs); - } - - template <typename Compare, typename Variant> - static EA_CONSTEXPR std::compare_three_way_result_t<Variant> call_three_way(const Variant& lhs, const Variant& rhs) - { - return call_index_three_way<Compare>(lhs, rhs, eastl::make_index_sequence<eastl::variant_size_v<eastl::decay_t<Variant>>>()); - } -#endif - }; - - template <typename Compare, typename Variant> - static EA_CONSTEXPR bool CompareVariantRelational(const Variant& lhs, const Variant& rhs) - { - return variant_relational_comparison::call<Compare>(lhs, rhs); - } - -#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) - template <typename Compare, typename Variant> - static EA_CONSTEXPR std::compare_three_way_result_t<Variant> CompareVariantRelationalThreeWay(const Variant& lhs, const Variant& rhs) - { - return variant_relational_comparison::call_three_way<Compare>(lhs, rhs); - } -#endif - - } // namespace internal - - - /////////////////////////////////////////////////////////////////////////// - // 20.7.5, relational operators - // - template <class... Types> - EA_CPP14_CONSTEXPR bool operator==(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - if (lhs.index() != rhs.index()) return false; - if (lhs.valueless_by_exception()) return true; - - return internal::CompareVariantRelational<eastl::equal_to<>>(lhs, rhs); - } - - template <class... Types> - EA_CPP14_CONSTEXPR bool operator!=(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - if (lhs.index() != rhs.index()) return true; - if (lhs.valueless_by_exception()) return false; - - return internal::CompareVariantRelational<eastl::not_equal_to<>>(lhs, rhs); - } - - template <class... Types> - EA_CPP14_CONSTEXPR bool operator<(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - 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 internal::CompareVariantRelational<eastl::less<>>(lhs, rhs); - } - - template <class... Types> - EA_CPP14_CONSTEXPR bool operator>(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - 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 internal::CompareVariantRelational<eastl::greater<>>(lhs, rhs); - } - - template <class... Types> - EA_CPP14_CONSTEXPR bool operator<=(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - 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 internal::CompareVariantRelational<eastl::less_equal<>>(lhs, rhs); - } - - template <class... Types> - EA_CPP14_CONSTEXPR bool operator>=(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - 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 internal::CompareVariantRelational<eastl::greater_equal<>>(lhs, rhs); - } - -#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON) - template <class... Types> requires (std::three_way_comparable<Types> && ...) - EA_CONSTEXPR std::common_comparison_category_t<std::compare_three_way_result_t<Types>...> operator<=>(const variant<Types...>& lhs, const variant<Types...>& rhs) - { - if (lhs.valueless_by_exception() && rhs.valueless_by_exception()) return std::strong_ordering::equal; - if (lhs.valueless_by_exception()) return std::strong_ordering::less; - if (rhs.valueless_by_exception()) return std::strong_ordering::greater; - if (auto result = (lhs.index() <=> rhs.index()); result != 0) return result; - - return internal::CompareVariantRelationalThreeWay<std::compare_three_way>(lhs, rhs); - - } -#endif - -} // namespace eastl - -EA_RESTORE_VC_WARNING() - -#endif // EASTL_VARIANT_H |