| // Copyright 2018 The Abseil Authors. |
| // |
| // 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 |
| // |
| // https://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. |
| // |
| // Implementation details of absl/types/variant.h, pulled into a |
| // separate file to avoid cluttering the top of the API header with |
| // implementation details. |
| |
| #ifndef ABSL_TYPES_INTERNAL_VARIANT_H_ |
| #define ABSL_TYPES_INTERNAL_VARIANT_H_ |
| |
| #include <cassert> |
| #include <cstddef> |
| #include <cstdlib> |
| #include <memory> |
| #include <stdexcept> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "absl/base/config.h" |
| #include "absl/base/internal/identity.h" |
| #include "absl/base/internal/inline_variable.h" |
| #include "absl/base/internal/invoke.h" |
| #include "absl/base/macros.h" |
| #include "absl/base/optimization.h" |
| #include "absl/meta/type_traits.h" |
| #include "absl/types/bad_variant_access.h" |
| #include "absl/utility/utility.h" |
| |
| #if !defined(ABSL_USES_STD_VARIANT) |
| |
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| |
| template <class... Types> |
| class variant; |
| |
| ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, static_cast<size_t>(-1)); |
| |
| template <class T> |
| struct variant_size; |
| |
| template <std::size_t I, class T> |
| struct variant_alternative; |
| |
| namespace variant_internal { |
| |
| // NOTE: See specializations below for details. |
| template <std::size_t I, class T> |
| struct VariantAlternativeSfinae {}; |
| |
| // Requires: I < variant_size_v<T>. |
| // |
| // Value: The Ith type of Types... |
| template <std::size_t I, class T0, class... Tn> |
| struct VariantAlternativeSfinae<I, variant<T0, Tn...>> |
| : VariantAlternativeSfinae<I - 1, variant<Tn...>> {}; |
| |
| // Value: T0 |
| template <class T0, class... Ts> |
| struct VariantAlternativeSfinae<0, variant<T0, Ts...>> { |
| using type = T0; |
| }; |
| |
| template <std::size_t I, class T> |
| using VariantAlternativeSfinaeT = typename VariantAlternativeSfinae<I, T>::type; |
| |
| // NOTE: Requires T to be a reference type. |
| template <class T, class U> |
| struct GiveQualsTo; |
| |
| template <class T, class U> |
| struct GiveQualsTo<T&, U> { |
| using type = U&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<T&&, U> { |
| using type = U&&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<const T&, U> { |
| using type = const U&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<const T&&, U> { |
| using type = const U&&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<volatile T&, U> { |
| using type = volatile U&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<volatile T&&, U> { |
| using type = volatile U&&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<volatile const T&, U> { |
| using type = volatile const U&; |
| }; |
| |
| template <class T, class U> |
| struct GiveQualsTo<volatile const T&&, U> { |
| using type = volatile const U&&; |
| }; |
| |
| template <class T, class U> |
| using GiveQualsToT = typename GiveQualsTo<T, U>::type; |
| |
| // Convenience alias, since size_t integral_constant is used a lot in this file. |
| template <std::size_t I> |
| using SizeT = std::integral_constant<std::size_t, I>; |
| |
| using NPos = SizeT<variant_npos>; |
| |
| template <class Variant, class T, class = void> |
| struct IndexOfConstructedType {}; |
| |
| template <std::size_t I, class Variant> |
| struct VariantAccessResultImpl; |
| |
| template <std::size_t I, template <class...> class Variantemplate, class... T> |
| struct VariantAccessResultImpl<I, Variantemplate<T...>&> { |
| using type = typename absl::variant_alternative<I, variant<T...>>::type&; |
| }; |
| |
| template <std::size_t I, template <class...> class Variantemplate, class... T> |
| struct VariantAccessResultImpl<I, const Variantemplate<T...>&> { |
| using type = |
| const typename absl::variant_alternative<I, variant<T...>>::type&; |
| }; |
| |
| template <std::size_t I, template <class...> class Variantemplate, class... T> |
| struct VariantAccessResultImpl<I, Variantemplate<T...>&&> { |
| using type = typename absl::variant_alternative<I, variant<T...>>::type&&; |
| }; |
| |
| template <std::size_t I, template <class...> class Variantemplate, class... T> |
| struct VariantAccessResultImpl<I, const Variantemplate<T...>&&> { |
| using type = |
| const typename absl::variant_alternative<I, variant<T...>>::type&&; |
| }; |
| |
| template <std::size_t I, class Variant> |
| using VariantAccessResult = |
| typename VariantAccessResultImpl<I, Variant&&>::type; |
| |
| // NOTE: This is used instead of std::array to reduce instantiation overhead. |
| template <class T, std::size_t Size> |
| struct SimpleArray { |
| static_assert(Size != 0, ""); |
| T value[Size]; |
| }; |
| |
| template <class T> |
| struct AccessedType { |
| using type = T; |
| }; |
| |
| template <class T> |
| using AccessedTypeT = typename AccessedType<T>::type; |
| |
| template <class T, std::size_t Size> |
| struct AccessedType<SimpleArray<T, Size>> { |
| using type = AccessedTypeT<T>; |
| }; |
| |
| template <class T> |
| constexpr T AccessSimpleArray(const T& value) { |
| return value; |
| } |
| |
| template <class T, std::size_t Size, class... SizeT> |
| constexpr AccessedTypeT<T> AccessSimpleArray(const SimpleArray<T, Size>& table, |
| std::size_t head_index, |
| SizeT... tail_indices) { |
| return AccessSimpleArray(table.value[head_index], tail_indices...); |
| } |
| |
| // Note: Intentionally is an alias. |
| template <class T> |
| using AlwaysZero = SizeT<0>; |
| |
| template <class Op, class... Vs> |
| struct VisitIndicesResultImpl { |
| using type = absl::result_of_t<Op(AlwaysZero<Vs>...)>; |
| }; |
| |
| template <class Op, class... Vs> |
| using VisitIndicesResultT = typename VisitIndicesResultImpl<Op, Vs...>::type; |
| |
| template <class ReturnType, class FunctionObject, class EndIndices, |
| class BoundIndices> |
| struct MakeVisitationMatrix; |
| |
| template <class ReturnType, class FunctionObject, std::size_t... Indices> |
| constexpr ReturnType call_with_indices(FunctionObject&& function) { |
| static_assert( |
| std::is_same<ReturnType, decltype(std::declval<FunctionObject>()( |
| SizeT<Indices>()...))>::value, |
| "Not all visitation overloads have the same return type."); |
| return std::forward<FunctionObject>(function)(SizeT<Indices>()...); |
| } |
| |
| template <class ReturnType, class FunctionObject, std::size_t... BoundIndices> |
| struct MakeVisitationMatrix<ReturnType, FunctionObject, index_sequence<>, |
| index_sequence<BoundIndices...>> { |
| using ResultType = ReturnType (*)(FunctionObject&&); |
| static constexpr ResultType Run() { |
| return &call_with_indices<ReturnType, FunctionObject, |
| (BoundIndices - 1)...>; |
| } |
| }; |
| |
| template <typename Is, std::size_t J> |
| struct AppendToIndexSequence; |
| |
| template <typename Is, std::size_t J> |
| using AppendToIndexSequenceT = typename AppendToIndexSequence<Is, J>::type; |
| |
| template <std::size_t... Is, std::size_t J> |
| struct AppendToIndexSequence<index_sequence<Is...>, J> { |
| using type = index_sequence<Is..., J>; |
| }; |
| |
| template <class ReturnType, class FunctionObject, class EndIndices, |
| class CurrIndices, class BoundIndices> |
| struct MakeVisitationMatrixImpl; |
| |
| template <class ReturnType, class FunctionObject, class EndIndices, |
| std::size_t... CurrIndices, class BoundIndices> |
| struct MakeVisitationMatrixImpl<ReturnType, FunctionObject, EndIndices, |
| index_sequence<CurrIndices...>, BoundIndices> { |
| using ResultType = SimpleArray< |
| typename MakeVisitationMatrix<ReturnType, FunctionObject, EndIndices, |
| index_sequence<>>::ResultType, |
| sizeof...(CurrIndices)>; |
| |
| static constexpr ResultType Run() { |
| return {{MakeVisitationMatrix< |
| ReturnType, FunctionObject, EndIndices, |
| AppendToIndexSequenceT<BoundIndices, CurrIndices>>::Run()...}}; |
| } |
| }; |
| |
| template <class ReturnType, class FunctionObject, std::size_t HeadEndIndex, |
| std::size_t... TailEndIndices, std::size_t... BoundIndices> |
| struct MakeVisitationMatrix<ReturnType, FunctionObject, |
| index_sequence<HeadEndIndex, TailEndIndices...>, |
| index_sequence<BoundIndices...>> |
| : MakeVisitationMatrixImpl<ReturnType, FunctionObject, |
| index_sequence<TailEndIndices...>, |
| absl::make_index_sequence<HeadEndIndex>, |
| index_sequence<BoundIndices...>> {}; |
| |
| struct UnreachableSwitchCase { |
| template <class Op> |
| [[noreturn]] static VisitIndicesResultT<Op, std::size_t> Run( |
| Op&& /*ignored*/) { |
| #if ABSL_HAVE_BUILTIN(__builtin_unreachable) || \ |
| (defined(__GNUC__) && !defined(__clang__)) |
| __builtin_unreachable(); |
| #elif defined(_MSC_VER) |
| __assume(false); |
| #else |
| // Try to use assert of false being identified as an unreachable intrinsic. |
| // NOTE: We use assert directly to increase chances of exploiting an assume |
| // intrinsic. |
| assert(false); // NOLINT |
| |
| // Hack to silence potential no return warning -- cause an infinite loop. |
| return Run(std::forward<Op>(op)); |
| #endif // Checks for __builtin_unreachable |
| } |
| }; |
| |
| template <class Op, std::size_t I> |
| struct ReachableSwitchCase { |
| static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) { |
| return absl::base_internal::invoke(std::forward<Op>(op), SizeT<I>()); |
| } |
| }; |
| |
| // The number 33 is just a guess at a reasonable maximum to our switch. It is |
| // not based on any analysis. The reason it is a power of 2 plus 1 instead of a |
| // power of 2 is because the number was picked to correspond to a power of 2 |
| // amount of "normal" alternatives, plus one for the possibility of the user |
| // providing "monostate" in addition to the more natural alternatives. |
| ABSL_INTERNAL_INLINE_CONSTEXPR(std::size_t, MaxUnrolledVisitCases, 33); |
| |
| // Note: The default-definition is for unreachable cases. |
| template <bool IsReachable> |
| struct PickCaseImpl { |
| template <class Op, std::size_t I> |
| using Apply = UnreachableSwitchCase; |
| }; |
| |
| template <> |
| struct PickCaseImpl</*IsReachable =*/true> { |
| template <class Op, std::size_t I> |
| using Apply = ReachableSwitchCase<Op, I>; |
| }; |
| |
| // Note: This form of dance with template aliases is to make sure that we |
| // instantiate a number of templates proportional to the number of variant |
| // alternatives rather than a number of templates proportional to our |
| // maximum unrolled amount of visitation cases (aliases are effectively |
| // "free" whereas other template instantiations are costly). |
| template <class Op, std::size_t I, std::size_t EndIndex> |
| using PickCase = typename PickCaseImpl<(I < EndIndex)>::template Apply<Op, I>; |
| |
| template <class ReturnType> |
| [[noreturn]] ReturnType TypedThrowBadVariantAccess() { |
| absl::variant_internal::ThrowBadVariantAccess(); |
| } |
| |
| // Given N variant sizes, determine the number of cases there would need to be |
| // in a single switch-statement that would cover every possibility in the |
| // corresponding N-ary visit operation. |
| template <std::size_t... NumAlternatives> |
| struct NumCasesOfSwitch; |
| |
| template <std::size_t HeadNumAlternatives, std::size_t... TailNumAlternatives> |
| struct NumCasesOfSwitch<HeadNumAlternatives, TailNumAlternatives...> { |
| static constexpr std::size_t value = |
| (HeadNumAlternatives + 1) * |
| NumCasesOfSwitch<TailNumAlternatives...>::value; |
| }; |
| |
| template <> |
| struct NumCasesOfSwitch<> { |
| static constexpr std::size_t value = 1; |
| }; |
| |
| // A switch statement optimizes better than the table of function pointers. |
| template <std::size_t EndIndex> |
| struct VisitIndicesSwitch { |
| static_assert(EndIndex <= MaxUnrolledVisitCases, |
| "Maximum unrolled switch size exceeded."); |
| |
| template <class Op> |
| static VisitIndicesResultT<Op, std::size_t> Run(Op&& op, std::size_t i) { |
| switch (i) { |
| case 0: |
| return PickCase<Op, 0, EndIndex>::Run(std::forward<Op>(op)); |
| case 1: |
| return PickCase<Op, 1, EndIndex>::Run(std::forward<Op>(op)); |
| case 2: |
| return PickCase<Op, 2, EndIndex>::Run(std::forward<Op>(op)); |
| case 3: |
| return PickCase<Op, 3, EndIndex>::Run(std::forward<Op>(op)); |
| case 4: |
| return PickCase<Op, 4, EndIndex>::Run(std::forward<Op>(op)); |
| case 5: |
| return PickCase<Op, 5, EndIndex>::Run(std::forward<Op>(op)); |
| case 6: |
| return PickCase<Op, 6, EndIndex>::Run(std::forward<Op>(op)); |
| case 7: |
| return PickCase<Op, 7, EndIndex>::Run(std::forward<Op>(op)); |
| case 8: |
| return PickCase<Op, 8, EndIndex>::Run(std::forward<Op>(op)); |
| case 9: |
| return PickCase<Op, 9, EndIndex>::Run(std::forward<Op>(op)); |
| case 10: |
| return PickCase<Op, 10, EndIndex>::Run(std::forward<Op>(op)); |
| case 11: |
| return PickCase<Op, 11, EndIndex>::Run(std::forward<Op>(op)); |
| case 12: |
| return PickCase<Op, 12, EndIndex>::Run(std::forward<Op>(op)); |
| case 13: |
| return PickCase<Op, 13, EndIndex>::Run(std::forward<Op>(op)); |
| case 14: |
| return PickCase<Op, 14, EndIndex>::Run(std::forward<Op>(op)); |
| case 15: |
| return PickCase<Op, 15, EndIndex>::Run(std::forward<Op>(op)); |
| case 16: |
| return PickCase<Op, 16, EndIndex>::Run(std::forward<Op>(op)); |
| case 17: |
| return PickCase<Op, 17, EndIndex>::Run(std::forward<Op>(op)); |
| case 18: |
| return PickCase<Op, 18, EndIndex>::Run(std::forward<Op>(op)); |
| case 19: |
| return PickCase<Op, 19, EndIndex>::Run(std::forward<Op>(op)); |
| case 20: |
| return PickCase<Op, 20, EndIndex>::Run(std::forward<Op>(op)); |
| case 21: |
| return PickCase<Op, 21, EndIndex>::Run(std::forward<Op>(op)); |
| case 22: |
| return PickCase<Op, 22, EndIndex>::Run(std::forward<Op>(op)); |
| case 23: |
| return PickCase<Op, 23, EndIndex>::Run(std::forward<Op>(op)); |
| case 24: |
| return PickCase<Op, 24, EndIndex>::Run(std::forward<Op>(op)); |
| case 25: |
| return PickCase<Op, 25, EndIndex>::Run(std::forward<Op>(op)); |
| case 26: |
| return PickCase<Op, 26, EndIndex>::Run(std::forward<Op>(op)); |
| case 27: |
| return PickCase<Op, 27, EndIndex>::Run(std::forward<Op>(op)); |
| case 28: |
| return PickCase<Op, 28, EndIndex>::Run(std::forward<Op>(op)); |
| case 29: |
| return PickCase<Op, 29, EndIndex>::Run(std::forward<Op>(op)); |
| case 30: |
| return PickCase<Op, 30, EndIndex>::Run(std::forward<Op>(op)); |
| case 31: |
| return PickCase<Op, 31, EndIndex>::Run(std::forward<Op>(op)); |
| case 32: |
| return PickCase<Op, 32, EndIndex>::Run(std::forward<Op>(op)); |
| default: |
| ABSL_ASSERT(i == variant_npos); |
| return absl::base_internal::invoke(std::forward<Op>(op), NPos()); |
| } |
| } |
| }; |
| |
| template <std::size_t... EndIndices> |
| struct VisitIndicesFallback { |
| template <class Op, class... SizeT> |
| static VisitIndicesResultT<Op, SizeT...> Run(Op&& op, SizeT... indices) { |
| return AccessSimpleArray( |
| MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op, |
| index_sequence<(EndIndices + 1)...>, |
| index_sequence<>>::Run(), |
| (indices + 1)...)(std::forward<Op>(op)); |
| } |
| }; |
| |
| // Take an N-dimensional series of indices and convert them into a single index |
| // without loss of information. The purpose of this is to be able to convert an |
| // N-ary visit operation into a single switch statement. |
| template <std::size_t...> |
| struct FlattenIndices; |
| |
| template <std::size_t HeadSize, std::size_t... TailSize> |
| struct FlattenIndices<HeadSize, TailSize...> { |
| template <class... SizeType> |
| static constexpr std::size_t Run(std::size_t head, SizeType... tail) { |
| return head + HeadSize * FlattenIndices<TailSize...>::Run(tail...); |
| } |
| }; |
| |
| template <> |
| struct FlattenIndices<> { |
| static constexpr std::size_t Run() { return 0; } |
| }; |
| |
| // Take a single "flattened" index (flattened by FlattenIndices) and determine |
| // the value of the index of one of the logically represented dimensions. |
| template <std::size_t I, std::size_t IndexToGet, std::size_t HeadSize, |
| std::size_t... TailSize> |
| struct UnflattenIndex { |
| static constexpr std::size_t value = |
| UnflattenIndex<I / HeadSize, IndexToGet - 1, TailSize...>::value; |
| }; |
| |
| template <std::size_t I, std::size_t HeadSize, std::size_t... TailSize> |
| struct UnflattenIndex<I, 0, HeadSize, TailSize...> { |
| static constexpr std::size_t value = (I % HeadSize); |
| }; |
| |
| // The backend for converting an N-ary visit operation into a unary visit. |
| template <class IndexSequence, std::size_t... EndIndices> |
| struct VisitIndicesVariadicImpl; |
| |
| template <std::size_t... N, std::size_t... EndIndices> |
| struct VisitIndicesVariadicImpl<absl::index_sequence<N...>, EndIndices...> { |
| // A type that can take an N-ary function object and converts it to a unary |
| // function object that takes a single, flattened index, and "unflattens" it |
| // into its individual dimensions when forwarding to the wrapped object. |
| template <class Op> |
| struct FlattenedOp { |
| template <std::size_t I> |
| VisitIndicesResultT<Op, decltype(EndIndices)...> operator()( |
| SizeT<I> /*index*/) && { |
| return base_internal::invoke( |
| std::forward<Op>(op), |
| SizeT<UnflattenIndex<I, N, (EndIndices + 1)...>::value - |
| std::size_t{1}>()...); |
| } |
| |
| Op&& op; |
| }; |
| |
| template <class Op, class... SizeType> |
| static VisitIndicesResultT<Op, decltype(EndIndices)...> Run(Op&& op, |
| SizeType... i) { |
| return VisitIndicesSwitch<NumCasesOfSwitch<EndIndices...>::value>::Run( |
| FlattenedOp<Op>{std::forward<Op>(op)}, |
| FlattenIndices<(EndIndices + std::size_t{1})...>::Run( |
| (i + std::size_t{1})...)); |
| } |
| }; |
| |
| template <std::size_t... EndIndices> |
| struct VisitIndicesVariadic |
| : VisitIndicesVariadicImpl<absl::make_index_sequence<sizeof...(EndIndices)>, |
| EndIndices...> {}; |
| |
| // This implementation will flatten N-ary visit operations into a single switch |
| // statement when the number of cases would be less than our maximum specified |
| // switch-statement size. |
| // TODO(calabrese) |
| // Based on benchmarks, determine whether the function table approach actually |
| // does optimize better than a chain of switch statements and possibly update |
| // the implementation accordingly. Also consider increasing the maximum switch |
| // size. |
| template <std::size_t... EndIndices> |
| struct VisitIndices |
| : absl::conditional_t<(NumCasesOfSwitch<EndIndices...>::value <= |
| MaxUnrolledVisitCases), |
| VisitIndicesVariadic<EndIndices...>, |
| VisitIndicesFallback<EndIndices...>> {}; |
| |
| template <std::size_t EndIndex> |
| struct VisitIndices<EndIndex> |
| : absl::conditional_t<(EndIndex <= MaxUnrolledVisitCases), |
| VisitIndicesSwitch<EndIndex>, |
| VisitIndicesFallback<EndIndex>> {}; |
| |
| // Suppress bogus warning on MSVC: MSVC complains that the `reinterpret_cast` |
| // below is returning the address of a temporary or local object. |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| #pragma warning(disable : 4172) |
| #endif // _MSC_VER |
| |
| // TODO(calabrese) std::launder |
| // TODO(calabrese) constexpr |
| // NOTE: DO NOT REMOVE the `inline` keyword as it is necessary to work around a |
| // MSVC bug. See https://github.com/abseil/abseil-cpp/issues/129 for details. |
| template <class Self, std::size_t I> |
| inline VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) { |
| return reinterpret_cast<VariantAccessResult<I, Self>>(self); |
| } |
| |
| #ifdef _MSC_VER |
| #pragma warning(pop) |
| #endif // _MSC_VER |
| |
| template <class T> |
| void DeducedDestroy(T& self) { // NOLINT |
| self.~T(); |
| } |
| |
| // NOTE: This type exists as a single entity for variant and its bases to |
| // befriend. It contains helper functionality that manipulates the state of the |
| // variant, such as the implementation of things like assignment and emplace |
| // operations. |
| struct VariantCoreAccess { |
| template <class VariantType> |
| static typename VariantType::Variant& Derived(VariantType& self) { // NOLINT |
| return static_cast<typename VariantType::Variant&>(self); |
| } |
| |
| template <class VariantType> |
| static const typename VariantType::Variant& Derived( |
| const VariantType& self) { // NOLINT |
| return static_cast<const typename VariantType::Variant&>(self); |
| } |
| |
| template <class VariantType> |
| static void Destroy(VariantType& self) { // NOLINT |
| Derived(self).destroy(); |
| self.index_ = absl::variant_npos; |
| } |
| |
| template <class Variant> |
| static void SetIndex(Variant& self, std::size_t i) { // NOLINT |
| self.index_ = i; |
| } |
| |
| template <class Variant> |
| static void InitFrom(Variant& self, Variant&& other) { // NOLINT |
| VisitIndices<absl::variant_size<Variant>::value>::Run( |
| InitFromVisitor<Variant, Variant&&>{&self, |
| std::forward<Variant>(other)}, |
| other.index()); |
| self.index_ = other.index(); |
| } |
| |
| // Access a variant alternative, assuming the index is correct. |
| template <std::size_t I, class Variant> |
| static VariantAccessResult<I, Variant> Access(Variant&& self) { |
| // This cast instead of invocation of AccessUnion with an rvalue is a |
| // workaround for msvc. Without this there is a runtime failure when dealing |
| // with rvalues. |
| // TODO(calabrese) Reduce test case and find a simpler workaround. |
| return static_cast<VariantAccessResult<I, Variant>>( |
| variant_internal::AccessUnion(self.state_, SizeT<I>())); |
| } |
| |
| // Access a variant alternative, throwing if the index is incorrect. |
| template <std::size_t I, class Variant> |
| static VariantAccessResult<I, Variant> CheckedAccess(Variant&& self) { |
| if (ABSL_PREDICT_FALSE(self.index_ != I)) { |
| TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>(); |
| } |
| |
| return Access<I>(std::forward<Variant>(self)); |
| } |
| |
| // The implementation of the move-assignment operation for a variant. |
| template <class VType> |
| struct MoveAssignVisitor { |
| using DerivedType = typename VType::Variant; |
| template <std::size_t NewIndex> |
| void operator()(SizeT<NewIndex> /*new_i*/) const { |
| if (left->index_ == NewIndex) { |
| Access<NewIndex>(*left) = std::move(Access<NewIndex>(*right)); |
| } else { |
| Derived(*left).template emplace<NewIndex>( |
| std::move(Access<NewIndex>(*right))); |
| } |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*new_i*/) const { |
| Destroy(*left); |
| } |
| |
| VType* left; |
| VType* right; |
| }; |
| |
| template <class VType> |
| static MoveAssignVisitor<VType> MakeMoveAssignVisitor(VType* left, |
| VType* other) { |
| return {left, other}; |
| } |
| |
| // The implementation of the assignment operation for a variant. |
| template <class VType> |
| struct CopyAssignVisitor { |
| using DerivedType = typename VType::Variant; |
| template <std::size_t NewIndex> |
| void operator()(SizeT<NewIndex> /*new_i*/) const { |
| using New = |
| typename absl::variant_alternative<NewIndex, DerivedType>::type; |
| |
| if (left->index_ == NewIndex) { |
| Access<NewIndex>(*left) = Access<NewIndex>(*right); |
| } else if (std::is_nothrow_copy_constructible<New>::value || |
| !std::is_nothrow_move_constructible<New>::value) { |
| Derived(*left).template emplace<NewIndex>(Access<NewIndex>(*right)); |
| } else { |
| Derived(*left) = DerivedType(Derived(*right)); |
| } |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*new_i*/) const { |
| Destroy(*left); |
| } |
| |
| VType* left; |
| const VType* right; |
| }; |
| |
| template <class VType> |
| static CopyAssignVisitor<VType> MakeCopyAssignVisitor(VType* left, |
| const VType& other) { |
| return {left, &other}; |
| } |
| |
| // The implementation of conversion-assignment operations for variant. |
| template <class Left, class QualifiedNew> |
| struct ConversionAssignVisitor { |
| using NewIndex = |
| variant_internal::IndexOfConstructedType<Left, QualifiedNew>; |
| |
| void operator()(SizeT<NewIndex::value> /*old_i*/ |
| ) const { |
| Access<NewIndex::value>(*left) = std::forward<QualifiedNew>(other); |
| } |
| |
| template <std::size_t OldIndex> |
| void operator()(SizeT<OldIndex> /*old_i*/ |
| ) const { |
| using New = |
| typename absl::variant_alternative<NewIndex::value, Left>::type; |
| if (std::is_nothrow_constructible<New, QualifiedNew>::value || |
| !std::is_nothrow_move_constructible<New>::value) { |
| left->template emplace<NewIndex::value>( |
| std::forward<QualifiedNew>(other)); |
| } else { |
| // the standard says "equivalent to |
| // operator=(variant(std::forward<T>(t)))", but we use `emplace` here |
| // because the variant's move assignment operator could be deleted. |
| left->template emplace<NewIndex::value>( |
| New(std::forward<QualifiedNew>(other))); |
| } |
| } |
| |
| Left* left; |
| QualifiedNew&& other; |
| }; |
| |
| template <class Left, class QualifiedNew> |
| static ConversionAssignVisitor<Left, QualifiedNew> |
| MakeConversionAssignVisitor(Left* left, QualifiedNew&& qual) { |
| return {left, std::forward<QualifiedNew>(qual)}; |
| } |
| |
| // Backend for operations for `emplace()` which destructs `*self` then |
| // construct a new alternative with `Args...`. |
| template <std::size_t NewIndex, class Self, class... Args> |
| static typename absl::variant_alternative<NewIndex, Self>::type& Replace( |
| Self* self, Args&&... args) { |
| Destroy(*self); |
| using New = typename absl::variant_alternative<NewIndex, Self>::type; |
| New* const result = ::new (static_cast<void*>(&self->state_)) |
| New(std::forward<Args>(args)...); |
| self->index_ = NewIndex; |
| return *result; |
| } |
| |
| template <class LeftVariant, class QualifiedRightVariant> |
| struct InitFromVisitor { |
| template <std::size_t NewIndex> |
| void operator()(SizeT<NewIndex> /*new_i*/) const { |
| using Alternative = |
| typename variant_alternative<NewIndex, LeftVariant>::type; |
| ::new (static_cast<void*>(&left->state_)) Alternative( |
| Access<NewIndex>(std::forward<QualifiedRightVariant>(right))); |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*new_i*/) const { |
| // This space intentionally left blank. |
| } |
| LeftVariant* left; |
| QualifiedRightVariant&& right; |
| }; |
| }; |
| |
| template <class Expected, class... T> |
| struct IndexOfImpl; |
| |
| template <class Expected> |
| struct IndexOfImpl<Expected> { |
| using IndexFromEnd = SizeT<0>; |
| using MatchedIndexFromEnd = IndexFromEnd; |
| using MultipleMatches = std::false_type; |
| }; |
| |
| template <class Expected, class Head, class... Tail> |
| struct IndexOfImpl<Expected, Head, Tail...> : IndexOfImpl<Expected, Tail...> { |
| using IndexFromEnd = |
| SizeT<IndexOfImpl<Expected, Tail...>::IndexFromEnd::value + 1>; |
| }; |
| |
| template <class Expected, class... Tail> |
| struct IndexOfImpl<Expected, Expected, Tail...> |
| : IndexOfImpl<Expected, Tail...> { |
| using IndexFromEnd = |
| SizeT<IndexOfImpl<Expected, Tail...>::IndexFromEnd::value + 1>; |
| using MatchedIndexFromEnd = IndexFromEnd; |
| using MultipleMatches = std::integral_constant< |
| bool, IndexOfImpl<Expected, Tail...>::MatchedIndexFromEnd::value != 0>; |
| }; |
| |
| template <class Expected, class... Types> |
| struct IndexOfMeta { |
| using Results = IndexOfImpl<Expected, Types...>; |
| static_assert(!Results::MultipleMatches::value, |
| "Attempted to access a variant by specifying a type that " |
| "matches more than one alternative."); |
| static_assert(Results::MatchedIndexFromEnd::value != 0, |
| "Attempted to access a variant by specifying a type that does " |
| "not match any alternative."); |
| using type = SizeT<sizeof...(Types) - Results::MatchedIndexFromEnd::value>; |
| }; |
| |
| template <class Expected, class... Types> |
| using IndexOf = typename IndexOfMeta<Expected, Types...>::type; |
| |
| template <class Variant, class T, std::size_t CurrIndex> |
| struct UnambiguousIndexOfImpl; |
| |
| // Terminating case encountered once we've checked all of the alternatives |
| template <class T, std::size_t CurrIndex> |
| struct UnambiguousIndexOfImpl<variant<>, T, CurrIndex> : SizeT<CurrIndex> {}; |
| |
| // Case where T is not Head |
| template <class Head, class... Tail, class T, std::size_t CurrIndex> |
| struct UnambiguousIndexOfImpl<variant<Head, Tail...>, T, CurrIndex> |
| : UnambiguousIndexOfImpl<variant<Tail...>, T, CurrIndex + 1>::type {}; |
| |
| // Case where T is Head |
| template <class Head, class... Tail, std::size_t CurrIndex> |
| struct UnambiguousIndexOfImpl<variant<Head, Tail...>, Head, CurrIndex> |
| : SizeT<UnambiguousIndexOfImpl<variant<Tail...>, Head, 0>::value == |
| sizeof...(Tail) |
| ? CurrIndex |
| : CurrIndex + sizeof...(Tail) + 1> {}; |
| |
| template <class Variant, class T> |
| struct UnambiguousIndexOf; |
| |
| struct NoMatch { |
| struct type {}; |
| }; |
| |
| template <class... Alts, class T> |
| struct UnambiguousIndexOf<variant<Alts...>, T> |
| : std::conditional<UnambiguousIndexOfImpl<variant<Alts...>, T, 0>::value != |
| sizeof...(Alts), |
| UnambiguousIndexOfImpl<variant<Alts...>, T, 0>, |
| NoMatch>::type::type {}; |
| |
| template <class T, std::size_t /*Dummy*/> |
| using UnambiguousTypeOfImpl = T; |
| |
| template <class Variant, class T> |
| using UnambiguousTypeOfT = |
| UnambiguousTypeOfImpl<T, UnambiguousIndexOf<Variant, T>::value>; |
| |
| template <class H, class... T> |
| class VariantStateBase; |
| |
| // This is an implementation of the "imaginary function" that is described in |
| // [variant.ctor] |
| // It is used in order to determine which alternative to construct during |
| // initialization from some type T. |
| template <class Variant, std::size_t I = 0> |
| struct ImaginaryFun; |
| |
| template <std::size_t I> |
| struct ImaginaryFun<variant<>, I> { |
| static void Run() = delete; |
| }; |
| |
| template <class H, class... T, std::size_t I> |
| struct ImaginaryFun<variant<H, T...>, I> : ImaginaryFun<variant<T...>, I + 1> { |
| using ImaginaryFun<variant<T...>, I + 1>::Run; |
| |
| // NOTE: const& and && are used instead of by-value due to lack of guaranteed |
| // move elision of C++17. This may have other minor differences, but tests |
| // pass. |
| static SizeT<I> Run(const H&, SizeT<I>); |
| static SizeT<I> Run(H&&, SizeT<I>); |
| }; |
| |
| // The following metafunctions are used in constructor and assignment |
| // constraints. |
| template <class Self, class T> |
| struct IsNeitherSelfNorInPlace : std::true_type {}; |
| |
| template <class Self> |
| struct IsNeitherSelfNorInPlace<Self, Self> : std::false_type {}; |
| |
| template <class Self, class T> |
| struct IsNeitherSelfNorInPlace<Self, in_place_type_t<T>> : std::false_type {}; |
| |
| template <class Self, std::size_t I> |
| struct IsNeitherSelfNorInPlace<Self, in_place_index_t<I>> : std::false_type {}; |
| |
| template <class Variant, class T> |
| struct IndexOfConstructedType< |
| Variant, T, |
| void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>> |
| : decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {})) {}; |
| |
| template <std::size_t... Is> |
| struct ContainsVariantNPos |
| : absl::negation<std::is_same< // NOLINT |
| std::integer_sequence<bool, 0 <= Is...>, |
| std::integer_sequence<bool, Is != absl::variant_npos...>>> {}; |
| |
| template <class Op, class... QualifiedVariants> |
| using RawVisitResult = |
| absl::result_of_t<Op(VariantAccessResult<0, QualifiedVariants>...)>; |
| |
| // NOTE: The spec requires that all return-paths yield the same type and is not |
| // SFINAE-friendly, so we can deduce the return type by examining the first |
| // result. If it's not callable, then we get an error, but are compliant and |
| // fast to compile. |
| // TODO(calabrese) Possibly rewrite in a way that yields better compile errors |
| // at the cost of longer compile-times. |
| template <class Op, class... QualifiedVariants> |
| struct VisitResultImpl { |
| using type = |
| absl::result_of_t<Op(VariantAccessResult<0, QualifiedVariants>...)>; |
| }; |
| |
| // Done in two steps intentionally so that we don't cause substitution to fail. |
| template <class Op, class... QualifiedVariants> |
| using VisitResult = typename VisitResultImpl<Op, QualifiedVariants...>::type; |
| |
| template <class Op, class... QualifiedVariants> |
| struct PerformVisitation { |
| using ReturnType = VisitResult<Op, QualifiedVariants...>; |
| |
| template <std::size_t... Is> |
| constexpr ReturnType operator()(SizeT<Is>... indices) const { |
| return Run(typename ContainsVariantNPos<Is...>::type{}, |
| absl::index_sequence_for<QualifiedVariants...>(), indices...); |
| } |
| |
| template <std::size_t... TupIs, std::size_t... Is> |
| constexpr ReturnType Run(std::false_type /*has_valueless*/, |
| index_sequence<TupIs...>, SizeT<Is>...) const { |
| static_assert( |
| std::is_same<ReturnType, |
| absl::result_of_t<Op(VariantAccessResult< |
| Is, QualifiedVariants>...)>>::value, |
| "All visitation overloads must have the same return type."); |
| return absl::base_internal::invoke( |
| std::forward<Op>(op), |
| VariantCoreAccess::Access<Is>( |
| std::forward<QualifiedVariants>(std::get<TupIs>(variant_tup)))...); |
| } |
| |
| template <std::size_t... TupIs, std::size_t... Is> |
| [[noreturn]] ReturnType Run(std::true_type /*has_valueless*/, |
| index_sequence<TupIs...>, SizeT<Is>...) const { |
| absl::variant_internal::ThrowBadVariantAccess(); |
| } |
| |
| // TODO(calabrese) Avoid using a tuple, which causes lots of instantiations |
| // Attempts using lambda variadic captures fail on current GCC. |
| std::tuple<QualifiedVariants&&...> variant_tup; |
| Op&& op; |
| }; |
| |
| template <class... T> |
| union Union; |
| |
| // We want to allow for variant<> to be trivial. For that, we need the default |
| // constructor to be trivial, which means we can't define it ourselves. |
| // Instead, we use a non-default constructor that takes NoopConstructorTag |
| // that doesn't affect the triviality of the types. |
| struct NoopConstructorTag {}; |
| |
| template <std::size_t I> |
| struct EmplaceTag {}; |
| |
| template <> |
| union Union<> { |
| constexpr explicit Union(NoopConstructorTag) noexcept {} |
| }; |
| |
| // Suppress bogus warning on MSVC: MSVC complains that Union<T...> has a defined |
| // deleted destructor from the `std::is_destructible` check below. |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| #pragma warning(disable : 4624) |
| #endif // _MSC_VER |
| |
| template <class Head, class... Tail> |
| union Union<Head, Tail...> { |
| using TailUnion = Union<Tail...>; |
| |
| explicit constexpr Union(NoopConstructorTag /*tag*/) noexcept |
| : tail(NoopConstructorTag()) {} |
| |
| template <class... P> |
| explicit constexpr Union(EmplaceTag<0>, P&&... args) |
| : head(std::forward<P>(args)...) {} |
| |
| template <std::size_t I, class... P> |
| explicit constexpr Union(EmplaceTag<I>, P&&... args) |
| : tail(EmplaceTag<I - 1>{}, std::forward<P>(args)...) {} |
| |
| Head head; |
| TailUnion tail; |
| }; |
| |
| #ifdef _MSC_VER |
| #pragma warning(pop) |
| #endif // _MSC_VER |
| |
| // TODO(calabrese) Just contain a Union in this union (certain configs fail). |
| template <class... T> |
| union DestructibleUnionImpl; |
| |
| template <> |
| union DestructibleUnionImpl<> { |
| constexpr explicit DestructibleUnionImpl(NoopConstructorTag) noexcept {} |
| }; |
| |
| template <class Head, class... Tail> |
| union DestructibleUnionImpl<Head, Tail...> { |
| using TailUnion = DestructibleUnionImpl<Tail...>; |
| |
| explicit constexpr DestructibleUnionImpl(NoopConstructorTag /*tag*/) noexcept |
| : tail(NoopConstructorTag()) {} |
| |
| template <class... P> |
| explicit constexpr DestructibleUnionImpl(EmplaceTag<0>, P&&... args) |
| : head(std::forward<P>(args)...) {} |
| |
| template <std::size_t I, class... P> |
| explicit constexpr DestructibleUnionImpl(EmplaceTag<I>, P&&... args) |
| : tail(EmplaceTag<I - 1>{}, std::forward<P>(args)...) {} |
| |
| ~DestructibleUnionImpl() {} |
| |
| Head head; |
| TailUnion tail; |
| }; |
| |
| // This union type is destructible even if one or more T are not trivially |
| // destructible. In the case that all T are trivially destructible, then so is |
| // this resultant type. |
| template <class... T> |
| using DestructibleUnion = |
| absl::conditional_t<std::is_destructible<Union<T...>>::value, Union<T...>, |
| DestructibleUnionImpl<T...>>; |
| |
| // Deepest base, containing the actual union and the discriminator |
| template <class H, class... T> |
| class VariantStateBase { |
| protected: |
| using Variant = variant<H, T...>; |
| |
| template <class LazyH = H, |
| class ConstructibleH = absl::enable_if_t< |
| std::is_default_constructible<LazyH>::value, LazyH>> |
| constexpr VariantStateBase() noexcept( |
| std::is_nothrow_default_constructible<ConstructibleH>::value) |
| : state_(EmplaceTag<0>()), index_(0) {} |
| |
| template <std::size_t I, class... P> |
| explicit constexpr VariantStateBase(EmplaceTag<I> tag, P&&... args) |
| : state_(tag, std::forward<P>(args)...), index_(I) {} |
| |
| explicit constexpr VariantStateBase(NoopConstructorTag) |
| : state_(NoopConstructorTag()), index_(variant_npos) {} |
| |
| void destroy() {} // Does nothing (shadowed in child if non-trivial) |
| |
| DestructibleUnion<H, T...> state_; |
| std::size_t index_; |
| }; |
| |
| using absl::internal::type_identity; |
| |
| // OverloadSet::Overload() is a unary function which is overloaded to |
| // take any of the element types of the variant, by reference-to-const. |
| // The return type of the overload on T is type_identity<T>, so that you |
| // can statically determine which overload was called. |
| // |
| // Overload() is not defined, so it can only be called in unevaluated |
| // contexts. |
| template <typename... Ts> |
| struct OverloadSet; |
| |
| template <typename T, typename... Ts> |
| struct OverloadSet<T, Ts...> : OverloadSet<Ts...> { |
| using Base = OverloadSet<Ts...>; |
| static type_identity<T> Overload(const T&); |
| using Base::Overload; |
| }; |
| |
| template <> |
| struct OverloadSet<> { |
| // For any case not handled above. |
| static void Overload(...); |
| }; |
| |
| template <class T> |
| using LessThanResult = decltype(std::declval<T>() < std::declval<T>()); |
| |
| template <class T> |
| using GreaterThanResult = decltype(std::declval<T>() > std::declval<T>()); |
| |
| template <class T> |
| using LessThanOrEqualResult = decltype(std::declval<T>() <= std::declval<T>()); |
| |
| template <class T> |
| using GreaterThanOrEqualResult = |
| decltype(std::declval<T>() >= std::declval<T>()); |
| |
| template <class T> |
| using EqualResult = decltype(std::declval<T>() == std::declval<T>()); |
| |
| template <class T> |
| using NotEqualResult = decltype(std::declval<T>() != std::declval<T>()); |
| |
| using type_traits_internal::is_detected_convertible; |
| |
| template <class... T> |
| using RequireAllHaveEqualT = absl::enable_if_t< |
| absl::conjunction<is_detected_convertible<bool, EqualResult, T>...>::value, |
| bool>; |
| |
| template <class... T> |
| using RequireAllHaveNotEqualT = |
| absl::enable_if_t<absl::conjunction<is_detected_convertible< |
| bool, NotEqualResult, T>...>::value, |
| bool>; |
| |
| template <class... T> |
| using RequireAllHaveLessThanT = |
| absl::enable_if_t<absl::conjunction<is_detected_convertible< |
| bool, LessThanResult, T>...>::value, |
| bool>; |
| |
| template <class... T> |
| using RequireAllHaveLessThanOrEqualT = |
| absl::enable_if_t<absl::conjunction<is_detected_convertible< |
| bool, LessThanOrEqualResult, T>...>::value, |
| bool>; |
| |
| template <class... T> |
| using RequireAllHaveGreaterThanOrEqualT = |
| absl::enable_if_t<absl::conjunction<is_detected_convertible< |
| bool, GreaterThanOrEqualResult, T>...>::value, |
| bool>; |
| |
| template <class... T> |
| using RequireAllHaveGreaterThanT = |
| absl::enable_if_t<absl::conjunction<is_detected_convertible< |
| bool, GreaterThanResult, T>...>::value, |
| bool>; |
| |
| // Helper template containing implementations details of variant that can't go |
| // in the private section. For convenience, this takes the variant type as a |
| // single template parameter. |
| template <typename T> |
| struct VariantHelper; |
| |
| template <typename... Ts> |
| struct VariantHelper<variant<Ts...>> { |
| // Type metafunction which returns the element type selected if |
| // OverloadSet::Overload() is well-formed when called with argument type U. |
| template <typename U> |
| using BestMatch = decltype(variant_internal::OverloadSet<Ts...>::Overload( |
| std::declval<U>())); |
| |
| // Type metafunction which returns true if OverloadSet::Overload() is |
| // well-formed when called with argument type U. |
| // CanAccept can't be just an alias because there is a MSVC bug on parameter |
| // pack expansion involving decltype. |
| template <typename U> |
| struct CanAccept |
| : std::integral_constant<bool, !std::is_void<BestMatch<U>>::value> {}; |
| |
| // Type metafunction which returns true if Other is an instantiation of |
| // variant, and variants's converting constructor from Other will be |
| // well-formed. We will use this to remove constructors that would be |
| // ill-formed from the overload set. |
| template <typename Other> |
| struct CanConvertFrom; |
| |
| template <typename... Us> |
| struct CanConvertFrom<variant<Us...>> |
| : public absl::conjunction<CanAccept<Us>...> {}; |
| }; |
| |
| // A type with nontrivial copy ctor and trivial move ctor. |
| struct TrivialMoveOnly { |
| TrivialMoveOnly(TrivialMoveOnly&&) = default; |
| }; |
| |
| // Trait class to detect whether a type is trivially move constructible. |
| // A union's defaulted copy/move constructor is deleted if any variant member's |
| // copy/move constructor is nontrivial. |
| template <typename T> |
| struct IsTriviallyMoveConstructible |
| : std::is_move_constructible<Union<T, TrivialMoveOnly>> {}; |
| |
| // To guarantee triviality of all special-member functions that can be trivial, |
| // we use a chain of conditional bases for each one. |
| // The order of inheritance of bases from child to base are logically: |
| // |
| // variant |
| // VariantCopyAssignBase |
| // VariantMoveAssignBase |
| // VariantCopyBase |
| // VariantMoveBase |
| // VariantStateBaseDestructor |
| // VariantStateBase |
| // |
| // Note that there is a separate branch at each base that is dependent on |
| // whether or not that corresponding special-member-function can be trivial in |
| // the resultant variant type. |
| |
| template <class... T> |
| class VariantStateBaseDestructorNontrivial; |
| |
| template <class... T> |
| class VariantMoveBaseNontrivial; |
| |
| template <class... T> |
| class VariantCopyBaseNontrivial; |
| |
| template <class... T> |
| class VariantMoveAssignBaseNontrivial; |
| |
| template <class... T> |
| class VariantCopyAssignBaseNontrivial; |
| |
| // Base that is dependent on whether or not the destructor can be trivial. |
| template <class... T> |
| using VariantStateBaseDestructor = |
| absl::conditional_t<std::is_destructible<Union<T...>>::value, |
| VariantStateBase<T...>, |
| VariantStateBaseDestructorNontrivial<T...>>; |
| |
| // Base that is dependent on whether or not the move-constructor can be |
| // implicitly generated by the compiler (trivial or deleted). |
| // Previously we were using `std::is_move_constructible<Union<T...>>` to check |
| // whether all Ts have trivial move constructor, but it ran into a GCC bug: |
| // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84866 |
| // So we have to use a different approach (i.e. `HasTrivialMoveConstructor`) to |
| // work around the bug. |
| template <class... T> |
| using VariantMoveBase = absl::conditional_t< |
| absl::disjunction< |
| absl::negation<absl::conjunction<std::is_move_constructible<T>...>>, |
| absl::conjunction<IsTriviallyMoveConstructible<T>...>>::value, |
| VariantStateBaseDestructor<T...>, VariantMoveBaseNontrivial<T...>>; |
| |
| // Base that is dependent on whether or not the copy-constructor can be trivial. |
| template <class... T> |
| using VariantCopyBase = absl::conditional_t< |
| absl::disjunction< |
| absl::negation<absl::conjunction<std::is_copy_constructible<T>...>>, |
| std::is_copy_constructible<Union<T...>>>::value, |
| VariantMoveBase<T...>, VariantCopyBaseNontrivial<T...>>; |
| |
| // Base that is dependent on whether or not the move-assign can be trivial. |
| template <class... T> |
| using VariantMoveAssignBase = absl::conditional_t< |
| absl::disjunction< |
| absl::conjunction<absl::is_move_assignable<Union<T...>>, |
| std::is_move_constructible<Union<T...>>, |
| std::is_destructible<Union<T...>>>, |
| absl::negation<absl::conjunction<std::is_move_constructible<T>..., |
| // Note: We're not qualifying this with |
| // absl:: because it doesn't compile |
| // under MSVC. |
| is_move_assignable<T>...>>>::value, |
| VariantCopyBase<T...>, VariantMoveAssignBaseNontrivial<T...>>; |
| |
| // Base that is dependent on whether or not the copy-assign can be trivial. |
| template <class... T> |
| using VariantCopyAssignBase = absl::conditional_t< |
| absl::disjunction< |
| absl::conjunction<absl::is_copy_assignable<Union<T...>>, |
| std::is_copy_constructible<Union<T...>>, |
| std::is_destructible<Union<T...>>>, |
| absl::negation<absl::conjunction<std::is_copy_constructible<T>..., |
| // Note: We're not qualifying this with |
| // absl:: because it doesn't compile |
| // under MSVC. |
| is_copy_assignable<T>...>>>::value, |
| VariantMoveAssignBase<T...>, VariantCopyAssignBaseNontrivial<T...>>; |
| |
| template <class... T> |
| using VariantBase = VariantCopyAssignBase<T...>; |
| |
| template <class... T> |
| class VariantStateBaseDestructorNontrivial : protected VariantStateBase<T...> { |
| private: |
| using Base = VariantStateBase<T...>; |
| |
| protected: |
| using Base::Base; |
| |
| VariantStateBaseDestructorNontrivial() = default; |
| VariantStateBaseDestructorNontrivial(VariantStateBaseDestructorNontrivial&&) = |
| default; |
| VariantStateBaseDestructorNontrivial( |
| const VariantStateBaseDestructorNontrivial&) = default; |
| VariantStateBaseDestructorNontrivial& operator=( |
| VariantStateBaseDestructorNontrivial&&) = default; |
| VariantStateBaseDestructorNontrivial& operator=( |
| const VariantStateBaseDestructorNontrivial&) = default; |
| |
| struct Destroyer { |
| template <std::size_t I> |
| void operator()(SizeT<I> i) const { |
| using Alternative = |
| typename absl::variant_alternative<I, variant<T...>>::type; |
| variant_internal::AccessUnion(self->state_, i).~Alternative(); |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*i*/) const { |
| // This space intentionally left blank |
| } |
| |
| VariantStateBaseDestructorNontrivial* self; |
| }; |
| |
| void destroy() { VisitIndices<sizeof...(T)>::Run(Destroyer{this}, index_); } |
| |
| ~VariantStateBaseDestructorNontrivial() { destroy(); } |
| |
| protected: |
| using Base::index_; |
| using Base::state_; |
| }; |
| |
| template <class... T> |
| class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor<T...> { |
| private: |
| using Base = VariantStateBaseDestructor<T...>; |
| |
| protected: |
| using Base::Base; |
| |
| struct Construct { |
| template <std::size_t I> |
| void operator()(SizeT<I> i) const { |
| using Alternative = |
| typename absl::variant_alternative<I, variant<T...>>::type; |
| ::new (static_cast<void*>(&self->state_)) Alternative( |
| variant_internal::AccessUnion(std::move(other->state_), i)); |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*i*/) const {} |
| |
| VariantMoveBaseNontrivial* self; |
| VariantMoveBaseNontrivial* other; |
| }; |
| |
| VariantMoveBaseNontrivial() = default; |
| VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept( |
| absl::conjunction<std::is_nothrow_move_constructible<T>...>::value) |
| : Base(NoopConstructorTag()) { |
| VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_); |
| index_ = other.index_; |
| } |
| |
| VariantMoveBaseNontrivial(VariantMoveBaseNontrivial const&) = default; |
| |
| VariantMoveBaseNontrivial& operator=(VariantMoveBaseNontrivial&&) = default; |
| VariantMoveBaseNontrivial& operator=(VariantMoveBaseNontrivial const&) = |
| default; |
| |
| protected: |
| using Base::index_; |
| using Base::state_; |
| }; |
| |
| template <class... T> |
| class VariantCopyBaseNontrivial : protected VariantMoveBase<T...> { |
| private: |
| using Base = VariantMoveBase<T...>; |
| |
| protected: |
| using Base::Base; |
| |
| VariantCopyBaseNontrivial() = default; |
| VariantCopyBaseNontrivial(VariantCopyBaseNontrivial&&) = default; |
| |
| struct Construct { |
| template <std::size_t I> |
| void operator()(SizeT<I> i) const { |
| using Alternative = |
| typename absl::variant_alternative<I, variant<T...>>::type; |
| ::new (static_cast<void*>(&self->state_)) |
| Alternative(variant_internal::AccessUnion(other->state_, i)); |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*i*/) const {} |
| |
| VariantCopyBaseNontrivial* self; |
| const VariantCopyBaseNontrivial* other; |
| }; |
| |
| VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other) |
| : Base(NoopConstructorTag()) { |
| VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_); |
| index_ = other.index_; |
| } |
| |
| VariantCopyBaseNontrivial& operator=(VariantCopyBaseNontrivial&&) = default; |
| VariantCopyBaseNontrivial& operator=(VariantCopyBaseNontrivial const&) = |
| default; |
| |
| protected: |
| using Base::index_; |
| using Base::state_; |
| }; |
| |
| template <class... T> |
| class VariantMoveAssignBaseNontrivial : protected VariantCopyBase<T...> { |
| friend struct VariantCoreAccess; |
| |
| private: |
| using Base = VariantCopyBase<T...>; |
| |
| protected: |
| using Base::Base; |
| |
| VariantMoveAssignBaseNontrivial() = default; |
| VariantMoveAssignBaseNontrivial(VariantMoveAssignBaseNontrivial&&) = default; |
| VariantMoveAssignBaseNontrivial(const VariantMoveAssignBaseNontrivial&) = |
| default; |
| VariantMoveAssignBaseNontrivial& operator=( |
| VariantMoveAssignBaseNontrivial const&) = default; |
| |
| VariantMoveAssignBaseNontrivial& |
| operator=(VariantMoveAssignBaseNontrivial&& other) noexcept( |
| absl::conjunction<std::is_nothrow_move_constructible<T>..., |
| std::is_nothrow_move_assignable<T>...>::value) { |
| VisitIndices<sizeof...(T)>::Run( |
| VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_); |
| return *this; |
| } |
| |
| protected: |
| using Base::index_; |
| using Base::state_; |
| }; |
| |
| template <class... T> |
| class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase<T...> { |
| friend struct VariantCoreAccess; |
| |
| private: |
| using Base = VariantMoveAssignBase<T...>; |
| |
| protected: |
| using Base::Base; |
| |
| VariantCopyAssignBaseNontrivial() = default; |
| VariantCopyAssignBaseNontrivial(VariantCopyAssignBaseNontrivial&&) = default; |
| VariantCopyAssignBaseNontrivial(const VariantCopyAssignBaseNontrivial&) = |
| default; |
| VariantCopyAssignBaseNontrivial& operator=( |
| VariantCopyAssignBaseNontrivial&&) = default; |
| |
| VariantCopyAssignBaseNontrivial& operator=( |
| const VariantCopyAssignBaseNontrivial& other) { |
| VisitIndices<sizeof...(T)>::Run( |
| VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_); |
| return *this; |
| } |
| |
| protected: |
| using Base::index_; |
| using Base::state_; |
| }; |
| |
| //////////////////////////////////////// |
| // Visitors for Comparison Operations // |
| //////////////////////////////////////// |
| |
| template <class... Types> |
| struct EqualsOp { |
| const variant<Types...>* v; |
| const variant<Types...>* w; |
| |
| constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const { |
| return true; |
| } |
| |
| template <std::size_t I> |
| constexpr bool operator()(SizeT<I> /*v_i*/) const { |
| return VariantCoreAccess::Access<I>(*v) == VariantCoreAccess::Access<I>(*w); |
| } |
| }; |
| |
| template <class... Types> |
| struct NotEqualsOp { |
| const variant<Types...>* v; |
| const variant<Types...>* w; |
| |
| constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const { |
| return false; |
| } |
| |
| template <std::size_t I> |
| constexpr bool operator()(SizeT<I> /*v_i*/) const { |
| return VariantCoreAccess::Access<I>(*v) != VariantCoreAccess::Access<I>(*w); |
| } |
| }; |
| |
| template <class... Types> |
| struct LessThanOp { |
| const variant<Types...>* v; |
| const variant<Types...>* w; |
| |
| constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const { |
| return false; |
| } |
| |
| template <std::size_t I> |
| constexpr bool operator()(SizeT<I> /*v_i*/) const { |
| return VariantCoreAccess::Access<I>(*v) < VariantCoreAccess::Access<I>(*w); |
| } |
| }; |
| |
| template <class... Types> |
| struct GreaterThanOp { |
| const variant<Types...>* v; |
| const variant<Types...>* w; |
| |
| constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const { |
| return false; |
| } |
| |
| template <std::size_t I> |
| constexpr bool operator()(SizeT<I> /*v_i*/) const { |
| return VariantCoreAccess::Access<I>(*v) > VariantCoreAccess::Access<I>(*w); |
| } |
| }; |
| |
| template <class... Types> |
| struct LessThanOrEqualsOp { |
| const variant<Types...>* v; |
| const variant<Types...>* w; |
| |
| constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const { |
| return true; |
| } |
| |
| template <std::size_t I> |
| constexpr bool operator()(SizeT<I> /*v_i*/) const { |
| return VariantCoreAccess::Access<I>(*v) <= VariantCoreAccess::Access<I>(*w); |
| } |
| }; |
| |
| template <class... Types> |
| struct GreaterThanOrEqualsOp { |
| const variant<Types...>* v; |
| const variant<Types...>* w; |
| |
| constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const { |
| return true; |
| } |
| |
| template <std::size_t I> |
| constexpr bool operator()(SizeT<I> /*v_i*/) const { |
| return VariantCoreAccess::Access<I>(*v) >= VariantCoreAccess::Access<I>(*w); |
| } |
| }; |
| |
| // Precondition: v.index() == w.index(); |
| template <class... Types> |
| struct SwapSameIndex { |
| variant<Types...>* v; |
| variant<Types...>* w; |
| template <std::size_t I> |
| void operator()(SizeT<I>) const { |
| type_traits_internal::Swap(VariantCoreAccess::Access<I>(*v), |
| VariantCoreAccess::Access<I>(*w)); |
| } |
| |
| void operator()(SizeT<variant_npos>) const {} |
| }; |
| |
| // TODO(calabrese) do this from a different namespace for proper adl usage |
| template <class... Types> |
| struct Swap { |
| variant<Types...>* v; |
| variant<Types...>* w; |
| |
| void generic_swap() const { |
| variant<Types...> tmp(std::move(*w)); |
| VariantCoreAccess::Destroy(*w); |
| VariantCoreAccess::InitFrom(*w, std::move(*v)); |
| VariantCoreAccess::Destroy(*v); |
| VariantCoreAccess::InitFrom(*v, std::move(tmp)); |
| } |
| |
| void operator()(SizeT<absl::variant_npos> /*w_i*/) const { |
| if (!v->valueless_by_exception()) { |
| generic_swap(); |
| } |
| } |
| |
| template <std::size_t Wi> |
| void operator()(SizeT<Wi> /*w_i*/) { |
| if (v->index() == Wi) { |
| VisitIndices<sizeof...(Types)>::Run(SwapSameIndex<Types...>{v, w}, Wi); |
| } else { |
| generic_swap(); |
| } |
| } |
| }; |
| |
| template <typename Variant, typename = void, typename... Ts> |
| struct VariantHashBase { |
| VariantHashBase() = delete; |
| VariantHashBase(const VariantHashBase&) = delete; |
| VariantHashBase(VariantHashBase&&) = delete; |
| VariantHashBase& operator=(const VariantHashBase&) = delete; |
| VariantHashBase& operator=(VariantHashBase&&) = delete; |
| }; |
| |
| struct VariantHashVisitor { |
| template <typename T> |
| size_t operator()(const T& t) { |
| return std::hash<T>{}(t); |
| } |
| }; |
| |
| template <typename Variant, typename... Ts> |
| struct VariantHashBase<Variant, |
| absl::enable_if_t<absl::conjunction< |
| type_traits_internal::IsHashable<Ts>...>::value>, |
| Ts...> { |
| using argument_type = Variant; |
| using result_type = size_t; |
| size_t operator()(const Variant& var) const { |
| type_traits_internal::AssertHashEnabled<Ts...>(); |
| if (var.valueless_by_exception()) { |
| return 239799884; |
| } |
| size_t result = VisitIndices<variant_size<Variant>::value>::Run( |
| PerformVisitation<VariantHashVisitor, const Variant&>{ |
| std::forward_as_tuple(var), VariantHashVisitor{}}, |
| var.index()); |
| // Combine the index and the hash result in order to distinguish |
| // std::variant<int, int> holding the same value as different alternative. |
| return result ^ var.index(); |
| } |
| }; |
| |
| } // namespace variant_internal |
| ABSL_NAMESPACE_END |
| } // namespace absl |
| |
| #endif // !defined(ABSL_USES_STD_VARIANT) |
| #endif // ABSL_TYPES_INTERNAL_VARIANT_H_ |