|
@@ -0,0 +1,1649 @@
|
|
|
+// 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 OTABSL_TYPES_variant_internal_H_
|
|
|
+#define OTABSL_TYPES_variant_internal_H_
|
|
|
+
|
|
|
+#include <cassert>
|
|
|
+#include <cstddef>
|
|
|
+#include <cstdlib>
|
|
|
+#include <memory>
|
|
|
+#include <stdexcept>
|
|
|
+#include <tuple>
|
|
|
+#include <type_traits>
|
|
|
+
|
|
|
+#include "../../base/config.h"
|
|
|
+#include "../../base/internal/identity.h"
|
|
|
+#include "../../base/internal/inline_variable.h"
|
|
|
+#include "../../base/internal/invoke.h"
|
|
|
+#include "../../base/macros.h"
|
|
|
+#include "../../base/optimization.h"
|
|
|
+#include "../../meta/type_traits.h"
|
|
|
+#include "../../types/bad_variant_access.h"
|
|
|
+#include "../../utility/utility.h"
|
|
|
+
|
|
|
+#if !defined(OTABSL_USES_STD_VARIANT)
|
|
|
+
|
|
|
+namespace absl {
|
|
|
+OTABSL_NAMESPACE_BEGIN
|
|
|
+
|
|
|
+template <class... Types>
|
|
|
+class variant;
|
|
|
+
|
|
|
+OTABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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 absl::OTABSL_OPTION_NAMESPACE_NAME::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&&);
|
|
|
+ // cppcheck-suppress [duplInheritedMember]
|
|
|
+ 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::OTABSL_OPTION_NAMESPACE_NAME::make_index_sequence<HeadEndIndex>,
|
|
|
+ index_sequence<BoundIndices...>> {};
|
|
|
+
|
|
|
+struct UnreachableSwitchCase {
|
|
|
+ template <class Op>
|
|
|
+ [[noreturn]] static VisitIndicesResultT<Op, std::size_t> Run(
|
|
|
+ Op&& /*ignored*/) {
|
|
|
+#if OTABSL_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(absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::base_internal::Invoke(absl::OTABSL_OPTION_NAMESPACE_NAME::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.
|
|
|
+OTABSL_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::OTABSL_OPTION_NAMESPACE_NAME::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(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 1:
|
|
|
+ return PickCase<Op, 1, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 2:
|
|
|
+ return PickCase<Op, 2, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 3:
|
|
|
+ return PickCase<Op, 3, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 4:
|
|
|
+ return PickCase<Op, 4, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 5:
|
|
|
+ return PickCase<Op, 5, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 6:
|
|
|
+ return PickCase<Op, 6, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 7:
|
|
|
+ return PickCase<Op, 7, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 8:
|
|
|
+ return PickCase<Op, 8, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 9:
|
|
|
+ return PickCase<Op, 9, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 10:
|
|
|
+ return PickCase<Op, 10, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 11:
|
|
|
+ return PickCase<Op, 11, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 12:
|
|
|
+ return PickCase<Op, 12, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 13:
|
|
|
+ return PickCase<Op, 13, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 14:
|
|
|
+ return PickCase<Op, 14, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 15:
|
|
|
+ return PickCase<Op, 15, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 16:
|
|
|
+ return PickCase<Op, 16, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 17:
|
|
|
+ return PickCase<Op, 17, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 18:
|
|
|
+ return PickCase<Op, 18, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 19:
|
|
|
+ return PickCase<Op, 19, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 20:
|
|
|
+ return PickCase<Op, 20, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 21:
|
|
|
+ return PickCase<Op, 21, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 22:
|
|
|
+ return PickCase<Op, 22, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 23:
|
|
|
+ return PickCase<Op, 23, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 24:
|
|
|
+ return PickCase<Op, 24, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 25:
|
|
|
+ return PickCase<Op, 25, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 26:
|
|
|
+ return PickCase<Op, 26, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 27:
|
|
|
+ return PickCase<Op, 27, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 28:
|
|
|
+ return PickCase<Op, 28, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 29:
|
|
|
+ return PickCase<Op, 29, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 30:
|
|
|
+ return PickCase<Op, 30, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 31:
|
|
|
+ return PickCase<Op, 31, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ case 32:
|
|
|
+ return PickCase<Op, 32, EndIndex>::Run(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op));
|
|
|
+ default:
|
|
|
+ OTABSL_ASSERT(i == variant_npos);
|
|
|
+ return absl::OTABSL_OPTION_NAMESPACE_NAME::base_internal::Invoke(absl::OTABSL_OPTION_NAMESPACE_NAME::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)...)(absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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 absl::OTABSL_OPTION_NAMESPACE_NAME::base_internal::Invoke(
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::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>{absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op)},
|
|
|
+ FlattenIndices<(EndIndices + std::size_t{1})...>::Run(
|
|
|
+ (i + std::size_t{1})...));
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+template <std::size_t... EndIndices>
|
|
|
+struct VisitIndicesVariadic
|
|
|
+ : VisitIndicesVariadicImpl<absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::conditional_t<(NumCasesOfSwitch<EndIndices...>::value <=
|
|
|
+ MaxUnrolledVisitCases),
|
|
|
+ VisitIndicesVariadic<EndIndices...>,
|
|
|
+ VisitIndicesFallback<EndIndices...>> {};
|
|
|
+
|
|
|
+template <std::size_t EndIndex>
|
|
|
+struct VisitIndices<EndIndex>
|
|
|
+ : absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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 (OTABSL_PREDICT_FALSE(self.index_ != I)) {
|
|
|
+ TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
|
|
|
+ }
|
|
|
+
|
|
|
+ return Access<I>(absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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) = absl::OTABSL_OPTION_NAMESPACE_NAME::forward<QualifiedNew>(other);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <std::size_t OldIndex>
|
|
|
+ void operator()(SizeT<OldIndex> /*old_i*/
|
|
|
+ ) const {
|
|
|
+ using New =
|
|
|
+ typename absl::OTABSL_OPTION_NAMESPACE_NAME::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>(
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::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(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<QualifiedNew>(other)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Left* left;
|
|
|
+ QualifiedNew&& other;
|
|
|
+ };
|
|
|
+
|
|
|
+ template <class Left, class QualifiedNew>
|
|
|
+ static ConversionAssignVisitor<Left, QualifiedNew>
|
|
|
+ MakeConversionAssignVisitor(Left* left, QualifiedNew&& qual) {
|
|
|
+ return {left, absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::variant_alternative<NewIndex, Self>::type& Replace(
|
|
|
+ Self* self, Args&&... args) {
|
|
|
+ Destroy(*self);
|
|
|
+ using New = typename absl::OTABSL_OPTION_NAMESPACE_NAME::variant_alternative<NewIndex, Self>::type;
|
|
|
+ // cppcheck-suppress [legacyUninitvar]
|
|
|
+ New* const result = ::new (static_cast<void*>(&self->state_))
|
|
|
+ New(absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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, class = void>
|
|
|
+struct ConversionIsPossibleImpl : std::false_type {};
|
|
|
+
|
|
|
+template <class Variant, class T>
|
|
|
+struct ConversionIsPossibleImpl<
|
|
|
+ Variant, T,
|
|
|
+ void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>>
|
|
|
+ : std::true_type {};
|
|
|
+
|
|
|
+template <class Variant, class T>
|
|
|
+struct ConversionIsPossible : ConversionIsPossibleImpl<Variant, T>::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::OTABSL_OPTION_NAMESPACE_NAME::negation<std::is_same< // NOLINT
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::integer_sequence<bool, 0 <= Is...>,
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::integer_sequence<bool, Is != absl::OTABSL_OPTION_NAMESPACE_NAME::variant_npos...>>> {};
|
|
|
+
|
|
|
+template <class Op, class... QualifiedVariants>
|
|
|
+using RawVisitResult =
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::result_of_t<Op(VariantAccessResult<
|
|
|
+ Is, QualifiedVariants>...)>>::value,
|
|
|
+ "All visitation overloads must have the same return type.");
|
|
|
+ return absl::OTABSL_OPTION_NAMESPACE_NAME::base_internal::Invoke(
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::forward<Op>(op),
|
|
|
+ VariantCoreAccess::Access<Is>(
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<P>(args)...) {}
|
|
|
+
|
|
|
+ template <std::size_t I, class... P>
|
|
|
+ explicit constexpr Union(EmplaceTag<I>, P&&... args)
|
|
|
+ : tail(EmplaceTag<I - 1>{}, absl::OTABSL_OPTION_NAMESPACE_NAME::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(absl::OTABSL_OPTION_NAMESPACE_NAME::forward<P>(args)...) {}
|
|
|
+
|
|
|
+ template <std::size_t I, class... P>
|
|
|
+ explicit constexpr DestructibleUnionImpl(EmplaceTag<I>, P&&... args)
|
|
|
+ : tail(EmplaceTag<I - 1>{}, absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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, absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::internal::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 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 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::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<is_detected_convertible<bool, EqualResult, T>...>::value,
|
|
|
+ bool>;
|
|
|
+
|
|
|
+template <class... T>
|
|
|
+using RequireAllHaveNotEqualT =
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<is_detected_convertible<
|
|
|
+ bool, NotEqualResult, T>...>::value,
|
|
|
+ bool>;
|
|
|
+
|
|
|
+template <class... T>
|
|
|
+using RequireAllHaveLessThanT =
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<is_detected_convertible<
|
|
|
+ bool, LessThanResult, T>...>::value,
|
|
|
+ bool>;
|
|
|
+
|
|
|
+template <class... T>
|
|
|
+using RequireAllHaveLessThanOrEqualT =
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<is_detected_convertible<
|
|
|
+ bool, LessThanOrEqualResult, T>...>::value,
|
|
|
+ bool>;
|
|
|
+
|
|
|
+template <class... T>
|
|
|
+using RequireAllHaveGreaterThanOrEqualT =
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<is_detected_convertible<
|
|
|
+ bool, GreaterThanOrEqualResult, T>...>::value,
|
|
|
+ bool>;
|
|
|
+
|
|
|
+template <class... T>
|
|
|
+using RequireAllHaveGreaterThanT =
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::conditional_t<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::disjunction<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::negation<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<std::is_move_constructible<T>...>>,
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::conditional_t<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::disjunction<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::negation<absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::conditional_t<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::disjunction<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<absl::OTABSL_OPTION_NAMESPACE_NAME::is_move_assignable<Union<T...>>,
|
|
|
+ std::is_move_constructible<Union<T...>>,
|
|
|
+ std::is_destructible<Union<T...>>>,
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::negation<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<std::is_move_constructible<T>...,
|
|
|
+ // Note: We're not qualifying this with
|
|
|
+ // absl::OTABSL_OPTION_NAMESPACE_NAME:: 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::OTABSL_OPTION_NAMESPACE_NAME::conditional_t<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::disjunction<
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<absl::OTABSL_OPTION_NAMESPACE_NAME::is_copy_assignable<Union<T...>>,
|
|
|
+ std::is_copy_constructible<Union<T...>>,
|
|
|
+ std::is_destructible<Union<T...>>>,
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::negation<absl::OTABSL_OPTION_NAMESPACE_NAME::conjunction<std::is_copy_constructible<T>...,
|
|
|
+ // Note: We're not qualifying this with
|
|
|
+ // absl::OTABSL_OPTION_NAMESPACE_NAME:: 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::OTABSL_OPTION_NAMESPACE_NAME::variant_alternative<I, variant<T...>>::type;
|
|
|
+ variant_internal::AccessUnion(self->state_, i).~Alternative();
|
|
|
+ }
|
|
|
+
|
|
|
+ void operator()(SizeT<absl::OTABSL_OPTION_NAMESPACE_NAME::variant_npos> /*i*/) const {
|
|
|
+ // This space intentionally left blank
|
|
|
+ }
|
|
|
+
|
|
|
+ VariantStateBaseDestructorNontrivial* self;
|
|
|
+ };
|
|
|
+
|
|
|
+ // cppcheck-suppress [duplInheritedMember]
|
|
|
+ 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::OTABSL_OPTION_NAMESPACE_NAME::variant_alternative<I, variant<T...>>::type;
|
|
|
+ ::new (static_cast<void*>(&self->state_)) Alternative(
|
|
|
+ variant_internal::AccessUnion(absl::OTABSL_OPTION_NAMESPACE_NAME::move(other->state_), i));
|
|
|
+ }
|
|
|
+
|
|
|
+ void operator()(SizeT<absl::OTABSL_OPTION_NAMESPACE_NAME::variant_npos> /*i*/) const {}
|
|
|
+
|
|
|
+ VariantMoveBaseNontrivial* self;
|
|
|
+ VariantMoveBaseNontrivial* other;
|
|
|
+ };
|
|
|
+
|
|
|
+ VariantMoveBaseNontrivial() = default;
|
|
|
+ VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept(
|
|
|
+ absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::variant_alternative<I, variant<T...>>::type;
|
|
|
+ ::new (static_cast<void*>(&self->state_))
|
|
|
+ Alternative(variant_internal::AccessUnion(other->state_, i));
|
|
|
+ }
|
|
|
+
|
|
|
+ void operator()(SizeT<absl::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::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::OTABSL_OPTION_NAMESPACE_NAME::enable_if_t<absl::OTABSL_OPTION_NAMESPACE_NAME::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
|
|
|
+OTABSL_NAMESPACE_END
|
|
|
+} // namespace absl
|
|
|
+
|
|
|
+#endif // !defined(OTABSL_USES_STD_VARIANT)
|
|
|
+#endif // OTABSL_TYPES_variant_internal_H_
|