Remove the implementation of `absl::variant`, which was only needed prior to C++17. `absl::variant` is now an alias for `std::variant`. It is recommended that clients simply use `std::variant`. PiperOrigin-RevId: 730940936 Change-Id: I7157612a62eec036abf61dd1ad42c5945afeac1d
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 789acf0..67a93da 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -430,10 +430,7 @@ "time/internal/cctz/src/tzfile.h" "time/internal/cctz/src/zone_info_source.cc" "types/any.h" - "types/bad_variant_access.cc" - "types/bad_variant_access.h" "types/compare.h" - "types/internal/variant.h" "types/optional.h" "types/span.h" "types/internal/span.h" @@ -488,7 +485,6 @@ "any" "any_invocable" "atomic_hook" - "bad_variant_access" "base" "base_internal" "bind_front"
diff --git a/absl/base/config.h b/absl/base/config.h index 1bfad2a..6015676 100644 --- a/absl/base/config.h +++ b/absl/base/config.h
@@ -522,19 +522,8 @@ #define ABSL_USES_STD_ANY 1 #define ABSL_HAVE_STD_OPTIONAL 1 #define ABSL_USES_STD_OPTIONAL 1 - -// ABSL_HAVE_STD_VARIANT -// -// Checks whether C++17 std::variant is available. -#ifdef ABSL_HAVE_STD_VARIANT -#error "ABSL_HAVE_STD_VARIANT cannot be directly set." -#elif defined(__cpp_lib_variant) && __cpp_lib_variant >= 201606L #define ABSL_HAVE_STD_VARIANT 1 -#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ - ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ - !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE -#define ABSL_HAVE_STD_VARIANT 1 -#endif +#define ABSL_USES_STD_VARIANT 1 // ABSL_HAVE_STD_STRING_VIEW // @@ -564,21 +553,6 @@ #define ABSL_HAVE_STD_ORDERING 1 #endif -// ABSL_USES_STD_VARIANT -// -// Indicates whether absl::variant is an alias for std::variant. -#if !defined(ABSL_OPTION_USE_STD_VARIANT) -#error options.h is misconfigured. -#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ - (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) -#undef ABSL_USES_STD_VARIANT -#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ - (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) -#define ABSL_USES_STD_VARIANT 1 -#else -#error options.h is misconfigured. -#endif - // ABSL_USES_STD_STRING_VIEW // // Indicates whether absl::string_view is an alias for std::string_view. @@ -612,14 +586,6 @@ #error options.h is misconfigured. #endif -// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION -// SEH exception from emplace for variant<SomeStruct> when constructing the -// struct can throw. This defeats some of variant_test and -// variant_exception_safety_test. -#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG) -#define ABSL_INTERNAL_MSVC_2017_DBG_MODE -#endif - // ABSL_INTERNAL_MANGLED_NS // ABSL_INTERNAL_MANGLED_BACKREFERENCE //
diff --git a/absl/base/options.h b/absl/base/options.h index 593b952..71bafb3 100644 --- a/absl/base/options.h +++ b/absl/base/options.h
@@ -99,32 +99,6 @@ #define ABSL_OPTION_USE_STD_STRING_VIEW 2 -// ABSL_OPTION_USE_STD_VARIANT -// -// This option controls whether absl::variant is implemented as an alias to -// std::variant, or as an independent implementation. -// -// A value of 0 means to use Abseil's implementation. This requires only C++11 -// support, and is expected to work on every toolchain we support. -// -// A value of 1 means to use an alias to std::variant. This requires that all -// code using Abseil is built in C++17 mode or later. -// -// A value of 2 means to detect the C++ version being used to compile Abseil, -// and use an alias only if a working std::variant is available. This option -// is useful when you are building your program from source. It should not be -// used otherwise -- for example, if you are distributing Abseil in a binary -// package manager -- since in mode 2, absl::variant will name a different -// type, with a different mangled name and binary layout, depending on the -// compiler flags passed by the end user. For more info, see -// https://abseil.io/about/design/dropin-types. -// -// User code should not inspect this macro. To check in the preprocessor if -// absl::variant is a typedef of std::variant, use the feature macro -// ABSL_USES_STD_VARIANT. - -#define ABSL_OPTION_USE_STD_VARIANT 2 - // ABSL_OPTION_USE_STD_ORDERING // // This option controls whether absl::{partial,weak,strong}_ordering are
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 6160daa..febc9e5 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -106,33 +106,21 @@ ) cc_library( - name = "bad_variant_access", - srcs = ["bad_variant_access.cc"], - hdrs = ["bad_variant_access.h"], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [ - "//absl/base:config", - "//absl/base:raw_logging_internal", - ], -) - -cc_library( name = "variant", - srcs = ["internal/variant.h"], hdrs = ["variant.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":bad_variant_access", - "//absl/base:base_internal", "//absl/base:config", - "//absl/base:core_headers", - "//absl/meta:type_traits", "//absl/utility", ], ) +cc_library( + name = "bad_variant_access", + deprecation = "bad_variant_access dependency is empty can be removed", +) + cc_test( name = "variant_test", size = "small", @@ -141,45 +129,6 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":variant", - "//absl/base:config", - "//absl/base:core_headers", - "//absl/memory", - "//absl/meta:type_traits", - "//absl/strings", - "@googletest//:gtest", - "@googletest//:gtest_main", - ], -) - -cc_test( - name = "variant_benchmark", - srcs = [ - "variant_benchmark.cc", - ], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["benchmark"], - deps = [ - ":variant", - "//absl/utility", - "@google_benchmark//:benchmark_main", - "@googletest//:gtest", - ], -) - -cc_test( - name = "variant_exception_safety_test", - size = "small", - srcs = [ - "variant_exception_safety_test.cc", - ], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [ - ":variant", - "//absl/base:config", - "//absl/base:exception_safety_testing", - "//absl/memory", "@googletest//:gtest", "@googletest//:gtest_main", ],
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index a18bf3d..817606b 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -102,34 +102,13 @@ absl_cc_library( NAME - bad_variant_access - HDRS - "bad_variant_access.h" - SRCS - "bad_variant_access.cc" - COPTS - ${ABSL_DEFAULT_COPTS} - DEPS - absl::config - absl::raw_logging_internal - PUBLIC -) - -absl_cc_library( - NAME variant HDRS "variant.h" - SRCS - "internal/variant.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS - absl::bad_variant_access - absl::base_internal absl::config - absl::core_headers - absl::type_traits absl::utility PUBLIC ) @@ -143,11 +122,6 @@ ${ABSL_TEST_COPTS} DEPS absl::variant - absl::config - absl::core_headers - absl::memory - absl::type_traits - absl::strings GTest::gmock_main ) @@ -177,18 +151,3 @@ absl::compare GTest::gmock_main ) - -absl_cc_test( - NAME - variant_exception_safety_test - SRCS - "variant_exception_safety_test.cc" - COPTS - ${ABSL_TEST_COPTS} - DEPS - absl::variant - absl::config - absl::exception_safety_testing - absl::memory - GTest::gmock_main -)
diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc deleted file mode 100644 index a76aa80..0000000 --- a/absl/types/bad_variant_access.cc +++ /dev/null
@@ -1,82 +0,0 @@ -// Copyright 2017 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. - -#include "absl/types/bad_variant_access.h" - -#ifndef ABSL_USES_STD_VARIANT - -#include <cstdlib> -#include <stdexcept> - -#include "absl/base/config.h" -#include "absl/base/internal/raw_logging.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -////////////////////////// -// [variant.bad.access] // -////////////////////////// - -bad_variant_access::~bad_variant_access() = default; - -const char* bad_variant_access::what() const noexcept { - return "Bad variant access"; -} - -namespace variant_internal { - -void ThrowBadVariantAccess() { -#ifdef ABSL_HAVE_EXCEPTIONS - throw bad_variant_access(); -#else - ABSL_RAW_LOG(FATAL, "Bad variant access"); - abort(); // TODO(calabrese) Remove once RAW_LOG FATAL is noreturn. -#endif -} - -void Rethrow() { -#ifdef ABSL_HAVE_EXCEPTIONS - throw; -#else - ABSL_RAW_LOG(FATAL, - "Internal error in absl::variant implementation. Attempted to " - "rethrow an exception when building with exceptions disabled."); - abort(); // TODO(calabrese) Remove once RAW_LOG FATAL is noreturn. -#endif -} - -} // namespace variant_internal -ABSL_NAMESPACE_END -} // namespace absl - -#else - -// https://github.com/abseil/abseil-cpp/issues/1465 -// CMake builds on Apple platforms error when libraries are empty. -// Our CMake configuration can avoid this error on header-only libraries, -// but since this library is conditionally empty, including a single -// variable is an easy workaround. -#ifdef __APPLE__ -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace types_internal { -extern const char kAvoidEmptyBadVariantAccessLibraryWarning; -const char kAvoidEmptyBadVariantAccessLibraryWarning = 0; -} // namespace types_internal -ABSL_NAMESPACE_END -} // namespace absl -#endif // __APPLE__ - -#endif // ABSL_USES_STD_VARIANT
diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h deleted file mode 100644 index 8ab215e..0000000 --- a/absl/types/bad_variant_access.h +++ /dev/null
@@ -1,82 +0,0 @@ -// 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. -// -// ----------------------------------------------------------------------------- -// bad_variant_access.h -// ----------------------------------------------------------------------------- -// -// This header file defines the `absl::bad_variant_access` type. - -#ifndef ABSL_TYPES_BAD_VARIANT_ACCESS_H_ -#define ABSL_TYPES_BAD_VARIANT_ACCESS_H_ - -#include <stdexcept> - -#include "absl/base/config.h" - -#ifdef ABSL_USES_STD_VARIANT - -#include <variant> - -namespace absl { -ABSL_NAMESPACE_BEGIN -using std::bad_variant_access; -ABSL_NAMESPACE_END -} // namespace absl - -#else // ABSL_USES_STD_VARIANT - -namespace absl { -ABSL_NAMESPACE_BEGIN - -// ----------------------------------------------------------------------------- -// bad_variant_access -// ----------------------------------------------------------------------------- -// -// An `absl::bad_variant_access` type is an exception type that is thrown in -// the following cases: -// -// * Calling `absl::get(absl::variant) with an index or type that does not -// match the currently selected alternative type -// * Calling `absl::visit on an `absl::variant` that is in the -// `variant::valueless_by_exception` state. -// -// Example: -// -// absl::variant<int, std::string> v; -// v = 1; -// try { -// absl::get<std::string>(v); -// } catch(const absl::bad_variant_access& e) { -// std::cout << "Bad variant access: " << e.what() << '\n'; -// } -class bad_variant_access : public std::exception { - public: - bad_variant_access() noexcept = default; - ~bad_variant_access() override; - const char* what() const noexcept override; -}; - -namespace variant_internal { - -[[noreturn]] ABSL_DLL void ThrowBadVariantAccess(); -[[noreturn]] ABSL_DLL void Rethrow(); - -} // namespace variant_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_USES_STD_VARIANT - -#endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h deleted file mode 100644 index 40574ae..0000000 --- a/absl/types/internal/variant.h +++ /dev/null
@@ -1,1620 +0,0 @@ -// 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/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; - -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*/) { - ABSL_UNREACHABLE(); - } -}; - -template <class Op, std::size_t I> -struct ReachableSwitchCase { - static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) { - return std::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. -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 std::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 std::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 std::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_
diff --git a/absl/types/variant.h b/absl/types/variant.h index 56a7e05..6b36645 100644 --- a/absl/types/variant.h +++ b/absl/types/variant.h
@@ -16,39 +16,18 @@ // variant.h // ----------------------------------------------------------------------------- // -// This header file defines an `absl::variant` type for holding a type-safe -// value of some prescribed set of types (noted as alternative types), and -// associated functions for managing variants. -// -// The `absl::variant` type is a form of type-safe union. An `absl::variant` -// should always hold a value of one of its alternative types (except in the -// "valueless by exception state" -- see below). A default-constructed -// `absl::variant` will hold the value of its first alternative type, provided -// it is default-constructible. -// -// In exceptional cases due to error, an `absl::variant` can hold no -// value (known as a "valueless by exception" state), though this is not the -// norm. -// -// As with `absl::optional`, an `absl::variant` -- when it holds a value -- -// allocates a value of that type directly within the `variant` itself; it -// cannot hold a reference, array, or the type `void`; it can, however, hold a -// pointer to externally managed memory. -// -// `absl::variant` is a C++11 compatible version of the C++17 `std::variant` -// abstraction and is designed to be a drop-in replacement for code compliant -// with C++17. +// Historical note: Abseil once provided an implementation of `absl::variant` +// as a polyfill for `std::variant` prior to C++17. Now that C++17 is required, +// `absl::variant` is an alias for `std::variant`. #ifndef ABSL_TYPES_VARIANT_H_ #define ABSL_TYPES_VARIANT_H_ +#include <variant> + #include "absl/base/config.h" #include "absl/utility/utility.h" -#ifdef ABSL_USES_STD_VARIANT - -#include <variant> // IWYU pragma: export - namespace absl { ABSL_NAMESPACE_BEGIN using std::bad_variant_access; @@ -63,765 +42,8 @@ using std::variant_size; using std::variant_size_v; using std::visit; -ABSL_NAMESPACE_END -} // namespace absl - -#else // ABSL_USES_STD_VARIANT - -#include <functional> -#include <new> -#include <type_traits> -#include <utility> - -#include "absl/base/macros.h" -#include "absl/base/port.h" -#include "absl/meta/type_traits.h" -#include "absl/types/internal/variant.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -// ----------------------------------------------------------------------------- -// absl::variant -// ----------------------------------------------------------------------------- -// -// An `absl::variant` type is a form of type-safe union. An `absl::variant` -- -// except in exceptional cases -- always holds a value of one of its alternative -// types. -// -// Example: -// -// // Construct a variant that holds either an integer or a std::string and -// // assign it to a std::string. -// absl::variant<int, std::string> v = std::string("abc"); -// -// // A default-constructed variant will hold a value-initialized value of -// // the first alternative type. -// auto a = absl::variant<int, std::string>(); // Holds an int of value '0'. -// -// // variants are assignable. -// -// // copy assignment -// auto v1 = absl::variant<int, std::string>("abc"); -// auto v2 = absl::variant<int, std::string>(10); -// v2 = v1; // copy assign -// -// // move assignment -// auto v1 = absl::variant<int, std::string>("abc"); -// v1 = absl::variant<int, std::string>(10); -// -// // assignment through type conversion -// a = 128; // variant contains int -// a = "128"; // variant contains std::string -// -// An `absl::variant` holding a value of one of its alternative types `T` holds -// an allocation of `T` directly within the variant itself. An `absl::variant` -// is not allowed to allocate additional storage, such as dynamic memory, to -// allocate the contained value. The contained value shall be allocated in a -// region of the variant storage suitably aligned for all alternative types. -template <typename... Ts> -class variant; - -// swap() -// -// Swaps two `absl::variant` values. This function is equivalent to `v.swap(w)` -// where `v` and `w` are `absl::variant` types. -// -// Note that this function requires all alternative types to be both swappable -// and move-constructible, because any two variants may refer to either the same -// type (in which case, they will be swapped) or to two different types (in -// which case the values will need to be moved). -// -template < - typename... Ts, - absl::enable_if_t< - absl::conjunction<std::is_move_constructible<Ts>..., - type_traits_internal::IsSwappable<Ts>...>::value, - int> = 0> -void swap(variant<Ts...>& v, variant<Ts...>& w) noexcept(noexcept(v.swap(w))) { - v.swap(w); -} - -// variant_size -// -// Returns the number of alternative types available for a given `absl::variant` -// type as a compile-time constant expression. As this is a class template, it -// is not generally useful for accessing the number of alternative types of -// any given `absl::variant` instance. -// -// Example: -// -// auto a = absl::variant<int, std::string>; -// constexpr int num_types = -// absl::variant_size<absl::variant<int, std::string>>(); -// -// // You can also use the member constant `value`. -// constexpr int num_types = -// absl::variant_size<absl::variant<int, std::string>>::value; -// -// // `absl::variant_size` is more valuable for use in generic code: -// template <typename Variant> -// constexpr bool IsVariantMultivalue() { -// return absl::variant_size<Variant>() > 1; -// } -// -// Note that the set of cv-qualified specializations of `variant_size` are -// provided to ensure that those specializations compile (especially when passed -// within template logic). -template <class T> -struct variant_size; - -template <class... Ts> -struct variant_size<variant<Ts...>> - : std::integral_constant<std::size_t, sizeof...(Ts)> {}; - -// Specialization of `variant_size` for const qualified variants. -template <class T> -struct variant_size<const T> : variant_size<T>::type {}; - -// Specialization of `variant_size` for volatile qualified variants. -template <class T> -struct variant_size<volatile T> : variant_size<T>::type {}; - -// Specialization of `variant_size` for const volatile qualified variants. -template <class T> -struct variant_size<const volatile T> : variant_size<T>::type {}; - -// variant_alternative -// -// Returns the alternative type for a given `absl::variant` at the passed -// index value as a compile-time constant expression. As this is a class -// template resulting in a type, it is not useful for access of the run-time -// value of any given `absl::variant` variable. -// -// Example: -// -// // The type of the 0th alternative is "int". -// using alternative_type_0 -// = absl::variant_alternative<0, absl::variant<int, std::string>>::type; -// -// static_assert(std::is_same<alternative_type_0, int>::value, ""); -// -// // `absl::variant_alternative` is more valuable for use in generic code: -// template <typename Variant> -// constexpr bool IsFirstElementTrivial() { -// return std::is_trivial_v<variant_alternative<0, Variant>::type>; -// } -// -// Note that the set of cv-qualified specializations of `variant_alternative` -// are provided to ensure that those specializations compile (especially when -// passed within template logic). -template <std::size_t I, class T> -struct variant_alternative; - -template <std::size_t I, class... Types> -struct variant_alternative<I, variant<Types...>> { - using type = - variant_internal::VariantAlternativeSfinaeT<I, variant<Types...>>; -}; - -// Specialization of `variant_alternative` for const qualified variants. -template <std::size_t I, class T> -struct variant_alternative<I, const T> { - using type = const typename variant_alternative<I, T>::type; -}; - -// Specialization of `variant_alternative` for volatile qualified variants. -template <std::size_t I, class T> -struct variant_alternative<I, volatile T> { - using type = volatile typename variant_alternative<I, T>::type; -}; - -// Specialization of `variant_alternative` for const volatile qualified -// variants. -template <std::size_t I, class T> -struct variant_alternative<I, const volatile T> { - using type = const volatile typename variant_alternative<I, T>::type; -}; - -// Template type alias for variant_alternative<I, T>::type. -// -// Example: -// -// using alternative_type_0 -// = absl::variant_alternative_t<0, absl::variant<int, std::string>>; -// static_assert(std::is_same<alternative_type_0, int>::value, ""); -template <std::size_t I, class T> -using variant_alternative_t = typename variant_alternative<I, T>::type; - -// holds_alternative() -// -// Checks whether the given variant currently holds a given alternative type, -// returning `true` if so. -// -// Example: -// -// absl::variant<int, std::string> foo = 42; -// if (absl::holds_alternative<int>(foo)) { -// std::cout << "The variant holds an integer"; -// } -template <class T, class... Types> -constexpr bool holds_alternative(const variant<Types...>& v) noexcept { - static_assert( - variant_internal::UnambiguousIndexOfImpl<variant<Types...>, T, - 0>::value != sizeof...(Types), - "The type T must occur exactly once in Types..."); - return v.index() == - variant_internal::UnambiguousIndexOf<variant<Types...>, T>::value; -} - -// get() -// -// Returns a reference to the value currently within a given variant, using -// either a unique alternative type amongst the variant's set of alternative -// types, or the variant's index value. Attempting to get a variant's value -// using a type that is not unique within the variant's set of alternative types -// is a compile-time error. If the index of the alternative being specified is -// different from the index of the alternative that is currently stored, throws -// `absl::bad_variant_access`. -// -// Example: -// -// auto a = absl::variant<int, std::string>; -// -// // Get the value by type (if unique). -// int i = absl::get<int>(a); -// -// auto b = absl::variant<int, int>; -// -// // Getting the value by a type that is not unique is ill-formed. -// int j = absl::get<int>(b); // Compile Error! -// -// // Getting value by index not ambiguous and allowed. -// int k = absl::get<1>(b); - -// Overload for getting a variant's lvalue by type. -template <class T, class... Types> -constexpr T& get(variant<Types...>& v) { // NOLINT - return variant_internal::VariantCoreAccess::CheckedAccess< - variant_internal::IndexOf<T, Types...>::value>(v); -} - -// Overload for getting a variant's rvalue by type. -template <class T, class... Types> -constexpr T&& get(variant<Types...>&& v) { - return variant_internal::VariantCoreAccess::CheckedAccess< - variant_internal::IndexOf<T, Types...>::value>(std::move(v)); -} - -// Overload for getting a variant's const lvalue by type. -template <class T, class... Types> -constexpr const T& get(const variant<Types...>& v) { - return variant_internal::VariantCoreAccess::CheckedAccess< - variant_internal::IndexOf<T, Types...>::value>(v); -} - -// Overload for getting a variant's const rvalue by type. -template <class T, class... Types> -constexpr const T&& get(const variant<Types...>&& v) { - return variant_internal::VariantCoreAccess::CheckedAccess< - variant_internal::IndexOf<T, Types...>::value>(std::move(v)); -} - -// Overload for getting a variant's lvalue by index. -template <std::size_t I, class... Types> -constexpr variant_alternative_t<I, variant<Types...>>& get( - variant<Types...>& v) { // NOLINT - return variant_internal::VariantCoreAccess::CheckedAccess<I>(v); -} - -// Overload for getting a variant's rvalue by index. -template <std::size_t I, class... Types> -constexpr variant_alternative_t<I, variant<Types...>>&& get( - variant<Types...>&& v) { - return variant_internal::VariantCoreAccess::CheckedAccess<I>(std::move(v)); -} - -// Overload for getting a variant's const lvalue by index. -template <std::size_t I, class... Types> -constexpr const variant_alternative_t<I, variant<Types...>>& get( - const variant<Types...>& v) { - return variant_internal::VariantCoreAccess::CheckedAccess<I>(v); -} - -// Overload for getting a variant's const rvalue by index. -template <std::size_t I, class... Types> -constexpr const variant_alternative_t<I, variant<Types...>>&& get( - const variant<Types...>&& v) { - return variant_internal::VariantCoreAccess::CheckedAccess<I>(std::move(v)); -} - -// get_if() -// -// Returns a pointer to the value currently stored within a given variant, if -// present, using either a unique alternative type amongst the variant's set of -// alternative types, or the variant's index value. If such a value does not -// exist, returns `nullptr`. -// -// As with `get`, attempting to get a variant's value using a type that is not -// unique within the variant's set of alternative types is a compile-time error. - -// Overload for getting a pointer to the value stored in the given variant by -// index. -template <std::size_t I, class... Types> -constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>> -get_if(variant<Types...>* v) noexcept { - return (v != nullptr && v->index() == I) - ? std::addressof( - variant_internal::VariantCoreAccess::Access<I>(*v)) - : nullptr; -} - -// Overload for getting a pointer to the const value stored in the given -// variant by index. -template <std::size_t I, class... Types> -constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>> -get_if(const variant<Types...>* v) noexcept { - return (v != nullptr && v->index() == I) - ? std::addressof( - variant_internal::VariantCoreAccess::Access<I>(*v)) - : nullptr; -} - -// Overload for getting a pointer to the value stored in the given variant by -// type. -template <class T, class... Types> -constexpr absl::add_pointer_t<T> get_if(variant<Types...>* v) noexcept { - return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v); -} - -// Overload for getting a pointer to the const value stored in the given variant -// by type. -template <class T, class... Types> -constexpr absl::add_pointer_t<const T> get_if( - const variant<Types...>* v) noexcept { - return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v); -} - -// visit() -// -// Calls a provided functor on a given set of variants. `absl::visit()` is -// commonly used to conditionally inspect the state of a given variant (or set -// of variants). -// -// The functor must return the same type when called with any of the variants' -// alternatives. -// -// Example: -// -// // Define a visitor functor -// struct GetVariant { -// template<typename T> -// void operator()(const T& i) const { -// std::cout << "The variant's value is: " << i; -// } -// }; -// -// // Declare our variant, and call `absl::visit()` on it. -// // Note that `GetVariant()` returns void in either case. -// absl::variant<int, std::string> foo = std::string("foo"); -// GetVariant visitor; -// absl::visit(visitor, foo); // Prints `The variant's value is: foo' -template <typename Visitor, typename... Variants> -variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis, - Variants&&... vars) { - return variant_internal:: - VisitIndices<variant_size<absl::decay_t<Variants> >::value...>::Run( - variant_internal::PerformVisitation<Visitor, Variants...>{ - std::forward_as_tuple(std::forward<Variants>(vars)...), - std::forward<Visitor>(vis)}, - vars.index()...); -} - -// monostate -// -// The monostate class serves as a first alternative type for a variant for -// which the first variant type is otherwise not default-constructible. -struct monostate {}; - -// `absl::monostate` Relational Operators - -constexpr bool operator<(monostate, monostate) noexcept { return false; } -constexpr bool operator>(monostate, monostate) noexcept { return false; } -constexpr bool operator<=(monostate, monostate) noexcept { return true; } -constexpr bool operator>=(monostate, monostate) noexcept { return true; } -constexpr bool operator==(monostate, monostate) noexcept { return true; } -constexpr bool operator!=(monostate, monostate) noexcept { return false; } - - -//------------------------------------------------------------------------------ -// `absl::variant` Template Definition -//------------------------------------------------------------------------------ -template <typename T0, typename... Tn> -class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> { - static_assert(absl::conjunction<std::is_object<T0>, - std::is_object<Tn>...>::value, - "Attempted to instantiate a variant containing a non-object " - "type."); - // Intentionally not qualifying `negation` with `absl::` to work around a bug - // in MSVC 2015 with inline namespace and variadic template. - static_assert(absl::conjunction<negation<std::is_array<T0> >, - negation<std::is_array<Tn> >...>::value, - "Attempted to instantiate a variant containing an array type."); - static_assert(absl::conjunction<std::is_nothrow_destructible<T0>, - std::is_nothrow_destructible<Tn>...>::value, - "Attempted to instantiate a variant containing a non-nothrow " - "destructible type."); - - friend struct variant_internal::VariantCoreAccess; - - private: - using Base = variant_internal::VariantBase<T0, Tn...>; - - public: - // Constructors - - // Constructs a variant holding a default-initialized value of the first - // alternative type. - constexpr variant() /*noexcept(see 111above)*/ = default; - - // Copy constructor, standard semantics - variant(const variant& other) = default; - - // Move constructor, standard semantics - variant(variant&& other) /*noexcept(see above)*/ = default; - - // Constructs a variant of an alternative type specified by overload - // resolution of the provided forwarding arguments through - // direct-initialization. - // - // Note: If the selected constructor is a constexpr constructor, this - // constructor shall be a constexpr constructor. - // - // NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html - // has been voted passed the design phase in the C++ standard meeting in Mar - // 2018. It will be implemented and integrated into `absl::variant`. - template < - class T, - std::size_t I = std::enable_if< - variant_internal::IsNeitherSelfNorInPlace<variant, - absl::decay_t<T> >::value, - variant_internal::IndexOfConstructedType<variant, T> >::type::value, - class Tj = absl::variant_alternative_t<I, variant>, - absl::enable_if_t<std::is_constructible<Tj, T>::value>* = nullptr> - constexpr variant(T&& t) noexcept(std::is_nothrow_constructible<Tj, T>::value) - : Base(variant_internal::EmplaceTag<I>(), std::forward<T>(t)) {} - - // Constructs a variant of an alternative type from the arguments through - // direct-initialization. - // - // Note: If the selected constructor is a constexpr constructor, this - // constructor shall be a constexpr constructor. - template <class T, class... Args, - typename std::enable_if<std::is_constructible< - variant_internal::UnambiguousTypeOfT<variant, T>, - Args...>::value>::type* = nullptr> - constexpr explicit variant(in_place_type_t<T>, Args&&... args) - : Base(variant_internal::EmplaceTag< - variant_internal::UnambiguousIndexOf<variant, T>::value>(), - std::forward<Args>(args)...) {} - - // Constructs a variant of an alternative type from an initializer list - // and other arguments through direct-initialization. - // - // Note: If the selected constructor is a constexpr constructor, this - // constructor shall be a constexpr constructor. - template <class T, class U, class... Args, - typename std::enable_if<std::is_constructible< - variant_internal::UnambiguousTypeOfT<variant, T>, - std::initializer_list<U>&, Args...>::value>::type* = nullptr> - constexpr explicit variant(in_place_type_t<T>, std::initializer_list<U> il, - Args&&... args) - : Base(variant_internal::EmplaceTag< - variant_internal::UnambiguousIndexOf<variant, T>::value>(), - il, std::forward<Args>(args)...) {} - - // Constructs a variant of an alternative type from a provided index, - // through value-initialization using the provided forwarded arguments. - template <std::size_t I, class... Args, - typename std::enable_if<std::is_constructible< - variant_internal::VariantAlternativeSfinaeT<I, variant>, - Args...>::value>::type* = nullptr> - constexpr explicit variant(in_place_index_t<I>, Args&&... args) - : Base(variant_internal::EmplaceTag<I>(), std::forward<Args>(args)...) {} - - // Constructs a variant of an alternative type from a provided index, - // through value-initialization of an initializer list and the provided - // forwarded arguments. - template <std::size_t I, class U, class... Args, - typename std::enable_if<std::is_constructible< - variant_internal::VariantAlternativeSfinaeT<I, variant>, - std::initializer_list<U>&, Args...>::value>::type* = nullptr> - constexpr explicit variant(in_place_index_t<I>, std::initializer_list<U> il, - Args&&... args) - : Base(variant_internal::EmplaceTag<I>(), il, - std::forward<Args>(args)...) {} - - // Destructors - - // Destroys the variant's currently contained value, provided that - // `absl::valueless_by_exception()` is false. - ~variant() = default; - - // Assignment Operators - - // Copy assignment operator - variant& operator=(const variant& other) = default; - - // Move assignment operator - variant& operator=(variant&& other) /*noexcept(see above)*/ = default; - - // Converting assignment operator - // - // NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html - // has been voted passed the design phase in the C++ standard meeting in Mar - // 2018. It will be implemented and integrated into `absl::variant`. - template < - class T, - std::size_t I = std::enable_if< - !std::is_same<absl::decay_t<T>, variant>::value, - variant_internal::IndexOfConstructedType<variant, T>>::type::value, - class Tj = absl::variant_alternative_t<I, variant>, - typename std::enable_if<std::is_assignable<Tj&, T>::value && - std::is_constructible<Tj, T>::value>::type* = - nullptr> - variant& operator=(T&& t) noexcept( - std::is_nothrow_assignable<Tj&, T>::value&& - std::is_nothrow_constructible<Tj, T>::value) { - variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run( - variant_internal::VariantCoreAccess::MakeConversionAssignVisitor( - this, std::forward<T>(t)), - index()); - - return *this; - } - - - // emplace() Functions - - // Constructs a value of the given alternative type T within the variant. The - // existing value of the variant is destroyed first (provided that - // `absl::valueless_by_exception()` is false). Requires that T is unambiguous - // in the variant. - // - // Example: - // - // absl::variant<std::vector<int>, int, std::string> v; - // v.emplace<int>(99); - // v.emplace<std::string>("abc"); - template < - class T, class... Args, - typename std::enable_if<std::is_constructible< - absl::variant_alternative_t< - variant_internal::UnambiguousIndexOf<variant, T>::value, variant>, - Args...>::value>::type* = nullptr> - T& emplace(Args&&... args) { - return variant_internal::VariantCoreAccess::Replace< - variant_internal::UnambiguousIndexOf<variant, T>::value>( - this, std::forward<Args>(args)...); - } - - // Constructs a value of the given alternative type T within the variant using - // an initializer list. The existing value of the variant is destroyed first - // (provided that `absl::valueless_by_exception()` is false). Requires that T - // is unambiguous in the variant. - // - // Example: - // - // absl::variant<std::vector<int>, int, std::string> v; - // v.emplace<std::vector<int>>({0, 1, 2}); - template < - class T, class U, class... Args, - typename std::enable_if<std::is_constructible< - absl::variant_alternative_t< - variant_internal::UnambiguousIndexOf<variant, T>::value, variant>, - std::initializer_list<U>&, Args...>::value>::type* = nullptr> - T& emplace(std::initializer_list<U> il, Args&&... args) { - return variant_internal::VariantCoreAccess::Replace< - variant_internal::UnambiguousIndexOf<variant, T>::value>( - this, il, std::forward<Args>(args)...); - } - - // Destroys the current value of the variant (provided that - // `absl::valueless_by_exception()` is false) and constructs a new value at - // the given index. - // - // Example: - // - // absl::variant<std::vector<int>, int, int> v; - // v.emplace<1>(99); - // v.emplace<2>(98); - // v.emplace<int>(99); // Won't compile. 'int' isn't a unique type. - template <std::size_t I, class... Args, - typename std::enable_if< - std::is_constructible<absl::variant_alternative_t<I, variant>, - Args...>::value>::type* = nullptr> - absl::variant_alternative_t<I, variant>& emplace(Args&&... args) { - return variant_internal::VariantCoreAccess::Replace<I>( - this, std::forward<Args>(args)...); - } - - // Destroys the current value of the variant (provided that - // `absl::valueless_by_exception()` is false) and constructs a new value at - // the given index using an initializer list and the provided arguments. - // - // Example: - // - // absl::variant<std::vector<int>, int, int> v; - // v.emplace<0>({0, 1, 2}); - template <std::size_t I, class U, class... Args, - typename std::enable_if<std::is_constructible< - absl::variant_alternative_t<I, variant>, - std::initializer_list<U>&, Args...>::value>::type* = nullptr> - absl::variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il, - Args&&... args) { - return variant_internal::VariantCoreAccess::Replace<I>( - this, il, std::forward<Args>(args)...); - } - - // variant::valueless_by_exception() - // - // Returns false if and only if the variant currently holds a valid value. - constexpr bool valueless_by_exception() const noexcept { - return this->index_ == absl::variant_npos; - } - - // variant::index() - // - // Returns the index value of the variant's currently selected alternative - // type. - constexpr std::size_t index() const noexcept { return this->index_; } - - // variant::swap() - // - // Swaps the values of two variant objects. - // - void swap(variant& rhs) noexcept( - absl::conjunction< - std::is_nothrow_move_constructible<T0>, - std::is_nothrow_move_constructible<Tn>..., - type_traits_internal::IsNothrowSwappable<T0>, - type_traits_internal::IsNothrowSwappable<Tn>...>::value) { - return variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run( - variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index()); - } -}; - -// We need a valid declaration of variant<> for SFINAE and overload resolution -// to work properly above, but we don't need a full declaration since this type -// will never be constructed. This declaration, though incomplete, suffices. -template <> -class variant<>; - -//------------------------------------------------------------------------------ -// Relational Operators -//------------------------------------------------------------------------------ -// -// If neither operand is in the `variant::valueless_by_exception` state: -// -// * If the index of both variants is the same, the relational operator -// returns the result of the corresponding relational operator for the -// corresponding alternative type. -// * If the index of both variants is not the same, the relational operator -// returns the result of that operation applied to the value of the left -// operand's index and the value of the right operand's index. -// * If at least one operand is in the valueless_by_exception state: -// - A variant in the valueless_by_exception state is only considered equal -// to another variant in the valueless_by_exception state. -// - If exactly one operand is in the valueless_by_exception state, the -// variant in the valueless_by_exception state is less than the variant -// that is not in the valueless_by_exception state. -// -// Note: The value 1 is added to each index in the relational comparisons such -// that the index corresponding to the valueless_by_exception state wraps around -// to 0 (the lowest value for the index type), and the remaining indices stay in -// the same relative order. - -// Equal-to operator -template <typename... Types> -constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==( - const variant<Types...>& a, const variant<Types...>& b) { - return (a.index() == b.index()) && - variant_internal::VisitIndices<sizeof...(Types)>::Run( - variant_internal::EqualsOp<Types...>{&a, &b}, a.index()); -} - -// Not equal operator -template <typename... Types> -constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=( - const variant<Types...>& a, const variant<Types...>& b) { - return (a.index() != b.index()) || - variant_internal::VisitIndices<sizeof...(Types)>::Run( - variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index()); -} - -// Less-than operator -template <typename... Types> -constexpr variant_internal::RequireAllHaveLessThanT<Types...> operator<( - const variant<Types...>& a, const variant<Types...>& b) { - return (a.index() != b.index()) - ? (a.index() + 1) < (b.index() + 1) - : variant_internal::VisitIndices<sizeof...(Types)>::Run( - variant_internal::LessThanOp<Types...>{&a, &b}, a.index()); -} - -// Greater-than operator -template <typename... Types> -constexpr variant_internal::RequireAllHaveGreaterThanT<Types...> operator>( - const variant<Types...>& a, const variant<Types...>& b) { - return (a.index() != b.index()) - ? (a.index() + 1) > (b.index() + 1) - : variant_internal::VisitIndices<sizeof...(Types)>::Run( - variant_internal::GreaterThanOp<Types...>{&a, &b}, - a.index()); -} - -// Less-than or equal-to operator -template <typename... Types> -constexpr variant_internal::RequireAllHaveLessThanOrEqualT<Types...> operator<=( - const variant<Types...>& a, const variant<Types...>& b) { - return (a.index() != b.index()) - ? (a.index() + 1) < (b.index() + 1) - : variant_internal::VisitIndices<sizeof...(Types)>::Run( - variant_internal::LessThanOrEqualsOp<Types...>{&a, &b}, - a.index()); -} - -// Greater-than or equal-to operator -template <typename... Types> -constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT<Types...> -operator>=(const variant<Types...>& a, const variant<Types...>& b) { - return (a.index() != b.index()) - ? (a.index() + 1) > (b.index() + 1) - : variant_internal::VisitIndices<sizeof...(Types)>::Run( - variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b}, - a.index()); -} - -ABSL_NAMESPACE_END -} // namespace absl - -namespace std { - -// hash() -template <> // NOLINT -struct hash<absl::monostate> { - std::size_t operator()(absl::monostate) const { return 0; } -}; - -template <class... T> // NOLINT -struct hash<absl::variant<T...>> - : absl::variant_internal::VariantHashBase<absl::variant<T...>, void, - absl::remove_const_t<T>...> {}; - -} // namespace std - -#endif // ABSL_USES_STD_VARIANT -namespace absl { -ABSL_NAMESPACE_BEGIN namespace variant_internal { - // Helper visitor for converting a variant<Ts...>` into another type (mostly // variant) that can be constructed from any type. template <typename To> @@ -831,7 +53,6 @@ return To(std::forward<T>(v)); } }; - } // namespace variant_internal // ConvertVariantTo()
diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc deleted file mode 100644 index 350b175..0000000 --- a/absl/types/variant_benchmark.cc +++ /dev/null
@@ -1,222 +0,0 @@ -// Copyright 2017 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. - -// Unit tests for the variant template. The 'is' and 'IsEmpty' methods -// of variant are not explicitly tested because they are used repeatedly -// in building other tests. All other public variant methods should have -// explicit tests. - -#include "absl/types/variant.h" - -#include <cstddef> -#include <cstdlib> -#include <string> -#include <tuple> - -#include "benchmark/benchmark.h" -#include "absl/utility/utility.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace { - -template <std::size_t I> -struct VariantAlternative { - char member; -}; - -template <class Indices> -struct VariantOfAlternativesImpl; - -template <std::size_t... Indices> -struct VariantOfAlternativesImpl<absl::index_sequence<Indices...>> { - using type = absl::variant<VariantAlternative<Indices>...>; -}; - -template <std::size_t NumAlternatives> -using VariantOfAlternatives = typename VariantOfAlternativesImpl< - absl::make_index_sequence<NumAlternatives>>::type; - -struct Empty {}; - -template <class... T> -void Ignore(T...) noexcept {} - -template <class T> -Empty DoNotOptimizeAndReturnEmpty(T&& arg) noexcept { - benchmark::DoNotOptimize(arg); - return {}; -} - -struct VisitorApplier { - struct Visitor { - template <class... T> - void operator()(T&&... args) const noexcept { - Ignore(DoNotOptimizeAndReturnEmpty(args)...); - } - }; - - template <class... Vars> - void operator()(const Vars&... vars) const noexcept { - absl::visit(Visitor(), vars...); - } -}; - -template <std::size_t NumIndices, std::size_t CurrIndex = NumIndices - 1> -struct MakeWithIndex { - using Variant = VariantOfAlternatives<NumIndices>; - - static Variant Run(std::size_t index) { - return index == CurrIndex - ? Variant(absl::in_place_index_t<CurrIndex>()) - : MakeWithIndex<NumIndices, CurrIndex - 1>::Run(index); - } -}; - -template <std::size_t NumIndices> -struct MakeWithIndex<NumIndices, 0> { - using Variant = VariantOfAlternatives<NumIndices>; - - static Variant Run(std::size_t /*index*/) { return Variant(); } -}; - -template <std::size_t NumIndices, class Dimensions> -struct MakeVariantTuple; - -template <class T, std::size_t /*I*/> -using always_t = T; - -template <std::size_t NumIndices> -VariantOfAlternatives<NumIndices> MakeVariant(std::size_t dimension, - std::size_t index) { - return dimension == 0 - ? MakeWithIndex<NumIndices>::Run(index % NumIndices) - : MakeVariant<NumIndices>(dimension - 1, index / NumIndices); -} - -template <std::size_t NumIndices, std::size_t... Dimensions> -struct MakeVariantTuple<NumIndices, absl::index_sequence<Dimensions...>> { - using VariantTuple = - std::tuple<always_t<VariantOfAlternatives<NumIndices>, Dimensions>...>; - - static VariantTuple Run(int index) { - return std::make_tuple(MakeVariant<NumIndices>(Dimensions, index)...); - } -}; - -constexpr std::size_t integral_pow(std::size_t base, std::size_t power) { - return power == 0 ? 1 : base * integral_pow(base, power - 1); -} - -template <std::size_t End, std::size_t I = 0> -struct VisitTestBody { - template <class Vars, class State> - static bool Run(Vars& vars, State& state) { - if (state.KeepRunning()) { - absl::apply(VisitorApplier(), vars[I]); - return VisitTestBody<End, I + 1>::Run(vars, state); - } - return false; - } -}; - -template <std::size_t End> -struct VisitTestBody<End, End> { - template <class Vars, class State> - static bool Run(Vars& /*vars*/, State& /*state*/) { - return true; - } -}; - -// Visit operations where branch prediction is likely to give a boost. -template <std::size_t NumIndices, std::size_t NumDimensions = 1> -void BM_RedundantVisit(benchmark::State& state) { - auto vars = - MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>:: - Run(static_cast<std::size_t>(state.range(0))); - - for (auto _ : state) { // NOLINT - benchmark::DoNotOptimize(vars); - absl::apply(VisitorApplier(), vars); - } -} - -// Visit operations where branch prediction is unlikely to give a boost. -template <std::size_t NumIndices, std::size_t NumDimensions = 1> -void BM_Visit(benchmark::State& state) { - constexpr std::size_t num_possibilities = - integral_pow(NumIndices, NumDimensions); - - using VariantTupleMaker = - MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>; - using Tuple = typename VariantTupleMaker::VariantTuple; - - Tuple vars[num_possibilities]; - for (std::size_t i = 0; i < num_possibilities; ++i) - vars[i] = VariantTupleMaker::Run(i); - - while (VisitTestBody<num_possibilities>::Run(vars, state)) { - } -} - -// Visitation -// Each visit is on a different variant with a different active alternative) - -// Unary visit -BENCHMARK_TEMPLATE(BM_Visit, 1); -BENCHMARK_TEMPLATE(BM_Visit, 2); -BENCHMARK_TEMPLATE(BM_Visit, 3); -BENCHMARK_TEMPLATE(BM_Visit, 4); -BENCHMARK_TEMPLATE(BM_Visit, 5); -BENCHMARK_TEMPLATE(BM_Visit, 6); -BENCHMARK_TEMPLATE(BM_Visit, 7); -BENCHMARK_TEMPLATE(BM_Visit, 8); -BENCHMARK_TEMPLATE(BM_Visit, 16); -BENCHMARK_TEMPLATE(BM_Visit, 32); -BENCHMARK_TEMPLATE(BM_Visit, 64); - -// Binary visit -BENCHMARK_TEMPLATE(BM_Visit, 1, 2); -BENCHMARK_TEMPLATE(BM_Visit, 2, 2); -BENCHMARK_TEMPLATE(BM_Visit, 3, 2); -BENCHMARK_TEMPLATE(BM_Visit, 4, 2); -BENCHMARK_TEMPLATE(BM_Visit, 5, 2); - -// Ternary visit -BENCHMARK_TEMPLATE(BM_Visit, 1, 3); -BENCHMARK_TEMPLATE(BM_Visit, 2, 3); -BENCHMARK_TEMPLATE(BM_Visit, 3, 3); - -// Quaternary visit -BENCHMARK_TEMPLATE(BM_Visit, 1, 4); -BENCHMARK_TEMPLATE(BM_Visit, 2, 4); - -// Redundant Visitation -// Each visit consistently has the same alternative active - -// Unary visit -BENCHMARK_TEMPLATE(BM_RedundantVisit, 1)->Arg(0); -BENCHMARK_TEMPLATE(BM_RedundantVisit, 2)->DenseRange(0, 1); -BENCHMARK_TEMPLATE(BM_RedundantVisit, 8)->DenseRange(0, 7); - -// Binary visit -BENCHMARK_TEMPLATE(BM_RedundantVisit, 1, 2)->Arg(0); -BENCHMARK_TEMPLATE(BM_RedundantVisit, 2, 2) - ->DenseRange(0, integral_pow(2, 2) - 1); -BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2) - ->DenseRange(0, integral_pow(4, 2) - 1); - -} // namespace -ABSL_NAMESPACE_END -} // namespace absl
diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc deleted file mode 100644 index 439c6e1..0000000 --- a/absl/types/variant_exception_safety_test.cc +++ /dev/null
@@ -1,532 +0,0 @@ -// Copyright 2017 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. - -#include "absl/types/variant.h" - -#include "absl/base/config.h" - -// This test is a no-op when absl::variant is an alias for std::variant and when -// exceptions are not enabled. -#if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) - -#include <iostream> -#include <memory> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/internal/exception_safety_testing.h" -#include "absl/memory/memory.h" - -// See comment in absl/base/config.h -#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace { - -using ::testing::MakeExceptionSafetyTester; -using ::testing::strong_guarantee; -using ::testing::TestNothrowOp; -using ::testing::TestThrowingCtor; - -using Thrower = testing::ThrowingValue<>; -using CopyNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowCopy>; -using MoveNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; -using ThrowingAlloc = testing::ThrowingAllocator<Thrower>; -using ThrowerVec = std::vector<Thrower, ThrowingAlloc>; -using ThrowingVariant = - absl::variant<Thrower, CopyNothrow, MoveNothrow, ThrowerVec>; - -struct ConversionException {}; - -template <class T> -struct ExceptionOnConversion { - operator T() const { // NOLINT - throw ConversionException(); - } -}; - -// Forces a variant into the valueless by exception state. -void ToValuelessByException(ThrowingVariant& v) { // NOLINT - try { - v.emplace<Thrower>(); - v.emplace<Thrower>(ExceptionOnConversion<Thrower>()); - } catch (const ConversionException&) { - // This space intentionally left blank. - } -} - -// Check that variant is still in a usable state after an exception is thrown. -testing::AssertionResult VariantInvariants(ThrowingVariant* v) { - using testing::AssertionFailure; - using testing::AssertionSuccess; - - // Try using the active alternative - if (absl::holds_alternative<Thrower>(*v)) { - auto& t = absl::get<Thrower>(*v); - t = Thrower{-100}; - if (t.Get() != -100) { - return AssertionFailure() << "Thrower should be assigned -100"; - } - } else if (absl::holds_alternative<ThrowerVec>(*v)) { - auto& tv = absl::get<ThrowerVec>(*v); - tv.clear(); - tv.emplace_back(-100); - if (tv.size() != 1 || tv[0].Get() != -100) { - return AssertionFailure() << "ThrowerVec should be {Thrower{-100}}"; - } - } else if (absl::holds_alternative<CopyNothrow>(*v)) { - auto& t = absl::get<CopyNothrow>(*v); - t = CopyNothrow{-100}; - if (t.Get() != -100) { - return AssertionFailure() << "CopyNothrow should be assigned -100"; - } - } else if (absl::holds_alternative<MoveNothrow>(*v)) { - auto& t = absl::get<MoveNothrow>(*v); - t = MoveNothrow{-100}; - if (t.Get() != -100) { - return AssertionFailure() << "MoveNothrow should be assigned -100"; - } - } - - // Try making variant valueless_by_exception - if (!v->valueless_by_exception()) ToValuelessByException(*v); - if (!v->valueless_by_exception()) { - return AssertionFailure() << "Variant should be valueless_by_exception"; - } - try { - auto unused = absl::get<Thrower>(*v); - static_cast<void>(unused); - return AssertionFailure() << "Variant should not contain Thrower"; - } catch (const absl::bad_variant_access&) { - } catch (...) { - return AssertionFailure() << "Unexpected exception throw from absl::get"; - } - - // Try using the variant - v->emplace<Thrower>(100); - if (!absl::holds_alternative<Thrower>(*v) || - absl::get<Thrower>(*v) != Thrower(100)) { - return AssertionFailure() << "Variant should contain Thrower(100)"; - } - v->emplace<ThrowerVec>({Thrower(100)}); - if (!absl::holds_alternative<ThrowerVec>(*v) || - absl::get<ThrowerVec>(*v)[0] != Thrower(100)) { - return AssertionFailure() - << "Variant should contain ThrowerVec{Thrower(100)}"; - } - return AssertionSuccess(); -} - -template <typename... Args> -Thrower ExpectedThrower(Args&&... args) { - return Thrower(42, args...); -} - -ThrowerVec ExpectedThrowerVec() { return {Thrower(100), Thrower(200)}; } -ThrowingVariant ValuelessByException() { - ThrowingVariant v; - ToValuelessByException(v); - return v; -} -ThrowingVariant WithThrower() { return Thrower(39); } -ThrowingVariant WithThrowerVec() { - return ThrowerVec{Thrower(1), Thrower(2), Thrower(3)}; -} -ThrowingVariant WithCopyNoThrow() { return CopyNothrow(39); } -ThrowingVariant WithMoveNoThrow() { return MoveNothrow(39); } - -TEST(VariantExceptionSafetyTest, DefaultConstructor) { - TestThrowingCtor<ThrowingVariant>(); -} - -TEST(VariantExceptionSafetyTest, CopyConstructor) { - { - ThrowingVariant v(ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(v); - } - { - ThrowingVariant v(ExpectedThrowerVec()); - TestThrowingCtor<ThrowingVariant>(v); - } - { - ThrowingVariant v(ValuelessByException()); - TestThrowingCtor<ThrowingVariant>(v); - } -} - -TEST(VariantExceptionSafetyTest, MoveConstructor) { - { - ThrowingVariant v(ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(std::move(v)); - } - { - ThrowingVariant v(ExpectedThrowerVec()); - TestThrowingCtor<ThrowingVariant>(std::move(v)); - } - { - ThrowingVariant v(ValuelessByException()); - TestThrowingCtor<ThrowingVariant>(std::move(v)); - } -} - -TEST(VariantExceptionSafetyTest, ValueConstructor) { - TestThrowingCtor<ThrowingVariant>(ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(ExpectedThrowerVec()); -} - -TEST(VariantExceptionSafetyTest, InPlaceTypeConstructor) { - TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<Thrower>{}, - ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<ThrowerVec>{}, - ExpectedThrowerVec()); -} - -TEST(VariantExceptionSafetyTest, InPlaceIndexConstructor) { - TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<0>{}, - ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<3>{}, - ExpectedThrowerVec()); -} - -TEST(VariantExceptionSafetyTest, CopyAssign) { - // variant& operator=(const variant& rhs); - // Let j be rhs.index() - { - // - neither *this nor rhs holds a value - const ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = ValuelessByException(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - { - // - *this holds a value but rhs does not - const ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - // - index() == j - { - const ThrowingVariant rhs(ExpectedThrower()); - auto tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } - { - const ThrowingVariant rhs(ExpectedThrowerVec()); - auto tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithThrowerVec()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } - // libstdc++ std::variant has bugs on copy assignment regarding exception - // safety. -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - // index() != j - // if is_nothrow_copy_constructible_v<Tj> or - // !is_nothrow_move_constructible<Tj> is true, equivalent to - // emplace<j>(get<j>(rhs)) - { - // is_nothrow_copy_constructible_v<Tj> == true - // should not throw because emplace() invokes Tj's copy ctor - // which should not throw. - const ThrowingVariant rhs(CopyNothrow{}); - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - { - // is_nothrow_copy_constructible<Tj> == false && - // is_nothrow_move_constructible<Tj> == false - // should provide basic guarantee because emplace() invokes Tj's copy ctor - // which may throw. - const ThrowingVariant rhs(ExpectedThrower()); - auto tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } -#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - { - // is_nothrow_copy_constructible_v<Tj> == false && - // is_nothrow_move_constructible_v<Tj> == true - // should provide strong guarantee because it is equivalent to - // operator=(variant(rhs)) which creates a temporary then invoke the move - // ctor which shouldn't throw. - const ThrowingVariant rhs(MoveNothrow{}); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants, strong_guarantee) - .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); - } -} - -TEST(VariantExceptionSafetyTest, MoveAssign) { - // variant& operator=(variant&& rhs); - // Let j be rhs.index() - { - // - neither *this nor rhs holds a value - ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = ValuelessByException(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); })); - } - { - // - *this holds a value but rhs does not - ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); })); - } - { - // - index() == j - // assign get<j>(std::move(rhs)) to the value contained in *this. - // If an exception is thrown during call to Tj's move assignment, the state - // of the contained value is as defined by the exception safety guarantee of - // Tj's move assignment; index() will be j. - ThrowingVariant rhs(ExpectedThrower()); - size_t j = rhs.index(); - // Since Thrower's move assignment has basic guarantee, so should variant's. - auto tester = MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&](ThrowingVariant* lhs) { - auto copy = rhs; - *lhs = std::move(copy); - }); - EXPECT_TRUE(tester - .WithContracts( - VariantInvariants, - [&](ThrowingVariant* lhs) { return lhs->index() == j; }) - .Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } - { - // libstdc++ introduced a regression between 2018-09-25 and 2019-01-06. - // The fix is targeted for gcc-9. - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7 - // https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614 -#if !(defined(ABSL_USES_STD_VARIANT) && \ - defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) - // - otherwise (index() != j), equivalent to - // emplace<j>(get<j>(std::move(rhs))) - // - If an exception is thrown during the call to Tj's move construction - // (with j being rhs.index()), the variant will hold no value. - ThrowingVariant rhs(CopyNothrow{}); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - *lhs = std::move(copy); - })); -#endif // !(defined(ABSL_USES_STD_VARIANT) && - // defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) - } -} - -TEST(VariantExceptionSafetyTest, ValueAssign) { - // template<class T> variant& operator=(T&& t); - // Let Tj be the type that is selected by overload resolution to be assigned. - { - // If *this holds a Tj, assigns std::forward<T>(t) to the value contained in - // *this. If an exception is thrown during the assignment of - // std::forward<T>(t) to the value contained in *this, the state of the - // contained value and t are as defined by the exception safety guarantee of - // the assignment expression; valueless_by_exception() will be false. - // Since Thrower's copy/move assignment has basic guarantee, so should - // variant's. - Thrower rhs = ExpectedThrower(); - // copy assign - auto copy_tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(copy_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return !lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test()); - // move assign - auto move_tester = MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&](ThrowingVariant* lhs) { - auto copy = rhs; - *lhs = std::move(copy); - }); - EXPECT_TRUE(move_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return !lhs->valueless_by_exception(); - }) - .Test()); - - EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test()); - } - // Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj, - // T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to - // emplace<j>(std::forward<T>(t)). - // We simplify the test by letting T = `const Tj&` or `Tj&&`, so we can reuse - // the CopyNothrow and MoveNothrow types. - - // if is_nothrow_constructible_v<Tj, T> - // (i.e. is_nothrow_copy/move_constructible_v<Tj>) is true, emplace() just - // invokes the copy/move constructor and it should not throw. - { - const CopyNothrow rhs; - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - { - MoveNothrow rhs; - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); })); - } - // if is_nothrow_constructible_v<Tj, T> == false && - // is_nothrow_move_constructible<Tj> == false - // emplace() invokes the copy/move constructor which may throw so it should - // provide basic guarantee and variant object might not hold a value. - { - Thrower rhs = ExpectedThrower(); - // copy - auto copy_tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(copy_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test()); - // move - auto move_tester = MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithOperation([](ThrowingVariant* lhs) { - *lhs = ExpectedThrower(testing::nothrow_ctor); - }); - EXPECT_TRUE(move_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test()); - } - // Otherwise (if is_nothrow_constructible_v<Tj, T> == false && - // is_nothrow_move_constructible<Tj> == true), - // equivalent to operator=(variant(std::forward<T>(t))) - // This should have strong guarantee because it creates a temporary variant - // and operator=(variant&&) invokes Tj's move ctor which doesn't throw. - // libstdc++ std::variant has bugs on conversion assignment regarding - // exception safety. -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - { - MoveNothrow rhs; - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants, strong_guarantee) - .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); - } -#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) -} - -TEST(VariantExceptionSafetyTest, Emplace) { - // If an exception during the initialization of the contained value, the - // variant might not hold a value. The standard requires emplace() to provide - // only basic guarantee. - { - Thrower args = ExpectedThrower(); - auto tester = MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&args](ThrowingVariant* v) { - v->emplace<Thrower>(args); - }); - EXPECT_TRUE(tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* v) { - return v->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } -} - -TEST(VariantExceptionSafetyTest, Swap) { - // if both are valueless_by_exception(), no effect - { - ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = ValuelessByException(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs.swap(rhs); })); - } - // if index() == rhs.index(), calls swap(get<i>(*this), get<i>(rhs)) - // where i is index(). - { - ThrowingVariant rhs = ExpectedThrower(); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - lhs->swap(copy); - })); - } - // Otherwise, exchanges the value of rhs and *this. The exception safety - // involves variant in moved-from state which is not specified in the - // standard, and since swap is 3-step it's impossible for it to provide a - // overall strong guarantee. So, we are only checking basic guarantee here. - { - ThrowingVariant rhs = ExpectedThrower(); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithContracts(VariantInvariants) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - lhs->swap(copy); - })); - } - { - ThrowingVariant rhs = ExpectedThrower(); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithContracts(VariantInvariants) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - copy.swap(*lhs); - })); - } -} - -} // namespace -ABSL_NAMESPACE_END -} // namespace absl - -#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) - -#endif // #if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index 91b142c..4d7b025 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc
@@ -12,2255 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Unit tests for the variant template. The 'is' and 'IsEmpty' methods -// of variant are not explicitly tested because they are used repeatedly -// in building other tests. All other public variant methods should have -// explicit tests. - #include "absl/types/variant.h" -// This test is a no-op when absl::variant is an alias for std::variant. -#if !defined(ABSL_USES_STD_VARIANT) - -#include <algorithm> -#include <cstddef> -#include <functional> -#include <initializer_list> #include <memory> -#include <ostream> -#include <queue> -#include <type_traits> -#include <unordered_set> -#include <utility> -#include <vector> +#include <string> +#include <variant> #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "absl/base/config.h" -#include "absl/base/port.h" -#include "absl/memory/memory.h" -#include "absl/meta/type_traits.h" -#include "absl/strings/string_view.h" - -#ifdef ABSL_HAVE_EXCEPTIONS - -#define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \ - EXPECT_THROW(expr, exception_t) - -#else - -#define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \ - EXPECT_DEATH_IF_SUPPORTED(expr, text) - -#endif // ABSL_HAVE_EXCEPTIONS - -#define ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(...) \ - ABSL_VARIANT_TEST_EXPECT_FAIL((void)(__VA_ARGS__), absl::bad_variant_access, \ - "Bad variant access") - -struct Hashable {}; - -namespace std { -template <> -struct hash<Hashable> { - size_t operator()(const Hashable&); -}; -} // namespace std - -struct NonHashable {}; namespace absl { -ABSL_NAMESPACE_BEGIN namespace { using ::testing::DoubleEq; using ::testing::Pointee; using ::testing::VariantWith; -struct MoveCanThrow { - MoveCanThrow() : v(0) {} - MoveCanThrow(int v) : v(v) {} // NOLINT(runtime/explicit) - MoveCanThrow(const MoveCanThrow& other) : v(other.v) {} - MoveCanThrow& operator=(const MoveCanThrow& /*other*/) { return *this; } - int v; -}; - -bool operator==(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v == rhs.v; } -bool operator!=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v != rhs.v; } -bool operator<(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v < rhs.v; } -bool operator<=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v <= rhs.v; } -bool operator>=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v >= rhs.v; } -bool operator>(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v > rhs.v; } - -// This helper class allows us to determine if it was swapped with std::swap() -// or with its friend swap() function. -struct SpecialSwap { - explicit SpecialSwap(int i) : i(i) {} - friend void swap(SpecialSwap& a, SpecialSwap& b) { - a.special_swap = b.special_swap = true; - std::swap(a.i, b.i); - } - bool operator==(SpecialSwap other) const { return i == other.i; } - int i; - bool special_swap = false; -}; - -struct MoveOnlyWithListConstructor { - MoveOnlyWithListConstructor() = default; - explicit MoveOnlyWithListConstructor(std::initializer_list<int> /*ilist*/, - int value) - : value(value) {} - MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default; - MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) = - default; - - int value = 0; -}; - -#ifdef ABSL_HAVE_EXCEPTIONS - -struct ConversionException {}; - -template <class T> -struct ExceptionOnConversion { - operator T() const { // NOLINT(runtime/explicit) - throw ConversionException(); - } -}; - -// Forces a variant into the valueless by exception state. -template <class H, class... T> -void ToValuelessByException(absl::variant<H, T...>& v) { // NOLINT - try { - v.template emplace<0>(ExceptionOnConversion<H>()); - } catch (ConversionException& /*e*/) { - // This space intentionally left blank. - } -} - -#endif // ABSL_HAVE_EXCEPTIONS - -// An indexed sequence of distinct structures holding a single -// value of type T -template<typename T, size_t N> -struct ValueHolder { - explicit ValueHolder(const T& x) : value(x) {} - typedef T value_type; - value_type value; - static const size_t kIndex = N; -}; -template<typename T, size_t N> -const size_t ValueHolder<T, N>::kIndex; - -// The following three functions make ValueHolder compatible with -// EXPECT_EQ and EXPECT_NE -template<typename T, size_t N> -inline bool operator==(const ValueHolder<T, N>& left, - const ValueHolder<T, N>& right) { - return left.value == right.value; -} - -template<typename T, size_t N> -inline bool operator!=(const ValueHolder<T, N>& left, - const ValueHolder<T, N>& right) { - return left.value != right.value; -} - -template<typename T, size_t N> -inline std::ostream& operator<<( - std::ostream& stream, const ValueHolder<T, N>& object) { - return stream << object.value; -} - -// Makes a variant holding twelve uniquely typed T wrappers. -template<typename T> -struct VariantFactory { - typedef variant<ValueHolder<T, 1>, ValueHolder<T, 2>, ValueHolder<T, 3>, - ValueHolder<T, 4>> - Type; -}; - -// A typelist in 1:1 with VariantFactory, to use type driven unit tests. -typedef ::testing::Types<ValueHolder<size_t, 1>, ValueHolder<size_t, 2>, - ValueHolder<size_t, 3>, - ValueHolder<size_t, 4>> VariantTypes; - -// Increments the provided counter pointer in the destructor -struct IncrementInDtor { - explicit IncrementInDtor(int* counter) : counter(counter) {} - ~IncrementInDtor() { *counter += 1; } - int* counter; -}; - -struct IncrementInDtorCopyCanThrow { - explicit IncrementInDtorCopyCanThrow(int* counter) : counter(counter) {} - IncrementInDtorCopyCanThrow(IncrementInDtorCopyCanThrow&& other) noexcept = - default; - IncrementInDtorCopyCanThrow(const IncrementInDtorCopyCanThrow& other) - : counter(other.counter) {} - IncrementInDtorCopyCanThrow& operator=( - IncrementInDtorCopyCanThrow&&) noexcept = default; - IncrementInDtorCopyCanThrow& operator=( - IncrementInDtorCopyCanThrow const& other) { - counter = other.counter; - return *this; - } - ~IncrementInDtorCopyCanThrow() { *counter += 1; } - int* counter; -}; - -// This is defined so operator== for ValueHolder<IncrementInDtor> will -// return true if two IncrementInDtor objects increment the same -// counter -inline bool operator==(const IncrementInDtor& left, - const IncrementInDtor& right) { - return left.counter == right.counter; -} - -// This is defined so EXPECT_EQ can work with IncrementInDtor -inline std::ostream& operator<<( - std::ostream& stream, const IncrementInDtor& object) { - return stream << object.counter; -} - -// A class that can be copied, but not assigned. -class CopyNoAssign { - public: - explicit CopyNoAssign(int value) : foo(value) {} - CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} - int foo; - private: - const CopyNoAssign& operator=(const CopyNoAssign&); -}; - -// A class that can neither be copied nor assigned. We provide -// overloads for the constructor with up to four parameters so we can -// test the overloads of variant::emplace. -class NonCopyable { - public: - NonCopyable() - : value(0) {} - explicit NonCopyable(int value1) - : value(value1) {} - - NonCopyable(int value1, int value2) - : value(value1 + value2) {} - - NonCopyable(int value1, int value2, int value3) - : value(value1 + value2 + value3) {} - - NonCopyable(int value1, int value2, int value3, int value4) - : value(value1 + value2 + value3 + value4) {} - NonCopyable(const NonCopyable&) = delete; - NonCopyable& operator=(const NonCopyable&) = delete; - int value; -}; - -// A typed test and typed test case over the VariantTypes typelist, -// from which we derive a number of tests that will execute for one of -// each type. -template <typename T> -class VariantTypesTest : public ::testing::Test {}; -TYPED_TEST_SUITE(VariantTypesTest, VariantTypes); - -//////////////////// -// [variant.ctor] // -//////////////////// - -struct NonNoexceptDefaultConstructible { - NonNoexceptDefaultConstructible() {} - int value = 5; -}; - -struct NonDefaultConstructible { - NonDefaultConstructible() = delete; -}; - -TEST(VariantTest, TestDefaultConstructor) { - { - using X = variant<int>; - constexpr variant<int> x{}; - ASSERT_FALSE(x.valueless_by_exception()); - ASSERT_EQ(0u, x.index()); - EXPECT_EQ(0, absl::get<0>(x)); - EXPECT_TRUE(std::is_nothrow_default_constructible<X>::value); - } - - { - using X = variant<NonNoexceptDefaultConstructible>; - X x{}; - ASSERT_FALSE(x.valueless_by_exception()); - ASSERT_EQ(0u, x.index()); - EXPECT_EQ(5, absl::get<0>(x).value); - EXPECT_FALSE(std::is_nothrow_default_constructible<X>::value); - } - - { - using X = variant<int, NonNoexceptDefaultConstructible>; - X x{}; - ASSERT_FALSE(x.valueless_by_exception()); - ASSERT_EQ(0u, x.index()); - EXPECT_EQ(0, absl::get<0>(x)); - EXPECT_TRUE(std::is_nothrow_default_constructible<X>::value); - } - - { - using X = variant<NonNoexceptDefaultConstructible, int>; - X x{}; - ASSERT_FALSE(x.valueless_by_exception()); - ASSERT_EQ(0u, x.index()); - EXPECT_EQ(5, absl::get<0>(x).value); - EXPECT_FALSE(std::is_nothrow_default_constructible<X>::value); - } - EXPECT_FALSE( - std::is_default_constructible<variant<NonDefaultConstructible>>::value); - EXPECT_FALSE((std::is_default_constructible< - variant<NonDefaultConstructible, int>>::value)); - EXPECT_TRUE((std::is_default_constructible< - variant<int, NonDefaultConstructible>>::value)); -} - -// Test that for each slot, copy constructing a variant with that type -// produces a sensible object that correctly reports its type, and -// that copies the provided value. -TYPED_TEST(VariantTypesTest, TestCopyCtor) { - typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - using value_type2 = absl::variant_alternative_t<1, Variant>; - using value_type3 = absl::variant_alternative_t<2, Variant>; - using value_type4 = absl::variant_alternative_t<3, Variant>; - const TypeParam value(TypeParam::kIndex); - Variant original(value); - Variant copied(original); - EXPECT_TRUE(absl::holds_alternative<value_type1>(copied) || - TypeParam::kIndex != 1); - EXPECT_TRUE(absl::holds_alternative<value_type2>(copied) || - TypeParam::kIndex != 2); - EXPECT_TRUE(absl::holds_alternative<value_type3>(copied) || - TypeParam::kIndex != 3); - EXPECT_TRUE(absl::holds_alternative<value_type4>(copied) || - TypeParam::kIndex != 4); - EXPECT_TRUE((absl::get_if<value_type1>(&original) == - absl::get_if<value_type1>(&copied)) || - TypeParam::kIndex == 1); - EXPECT_TRUE((absl::get_if<value_type2>(&original) == - absl::get_if<value_type2>(&copied)) || - TypeParam::kIndex == 2); - EXPECT_TRUE((absl::get_if<value_type3>(&original) == - absl::get_if<value_type3>(&copied)) || - TypeParam::kIndex == 3); - EXPECT_TRUE((absl::get_if<value_type4>(&original) == - absl::get_if<value_type4>(&copied)) || - TypeParam::kIndex == 4); - EXPECT_TRUE((absl::get_if<value_type1>(&original) == - absl::get_if<value_type1>(&copied)) || - TypeParam::kIndex == 1); - EXPECT_TRUE((absl::get_if<value_type2>(&original) == - absl::get_if<value_type2>(&copied)) || - TypeParam::kIndex == 2); - EXPECT_TRUE((absl::get_if<value_type3>(&original) == - absl::get_if<value_type3>(&copied)) || - TypeParam::kIndex == 3); - EXPECT_TRUE((absl::get_if<value_type4>(&original) == - absl::get_if<value_type4>(&copied)) || - TypeParam::kIndex == 4); - const TypeParam* ovalptr = absl::get_if<TypeParam>(&original); - const TypeParam* cvalptr = absl::get_if<TypeParam>(&copied); - ASSERT_TRUE(ovalptr != nullptr); - ASSERT_TRUE(cvalptr != nullptr); - EXPECT_EQ(*ovalptr, *cvalptr); - TypeParam* mutable_ovalptr = absl::get_if<TypeParam>(&original); - TypeParam* mutable_cvalptr = absl::get_if<TypeParam>(&copied); - ASSERT_TRUE(mutable_ovalptr != nullptr); - ASSERT_TRUE(mutable_cvalptr != nullptr); - EXPECT_EQ(*mutable_ovalptr, *mutable_cvalptr); -} - -template <class> -struct MoveOnly { - MoveOnly() = default; - explicit MoveOnly(int value) : value(value) {} - MoveOnly(MoveOnly&&) = default; - MoveOnly& operator=(MoveOnly&&) = default; - int value = 5; -}; - -TEST(VariantTest, TestMoveConstruct) { - using V = variant<MoveOnly<class A>, MoveOnly<class B>, MoveOnly<class C>>; - - V v(in_place_index<1>, 10); - V v2 = std::move(v); - EXPECT_EQ(10, absl::get<1>(v2).value); -} - -// Used internally to emulate missing triviality traits for tests. -template <class T> -union SingleUnion { - T member; -}; - -// NOTE: These don't work with types that can't be union members. -// They are just for testing. -template <class T> -struct is_trivially_move_constructible - : std::is_move_constructible<SingleUnion<T>>::type {}; - -template <class T> -struct is_trivially_move_assignable - : absl::is_move_assignable<SingleUnion<T>>::type {}; - -TEST(VariantTest, NothrowMoveConstructible) { - // Verify that variant is nothrow move constructible iff its template - // arguments are. - using U = std::unique_ptr<int>; - struct E { - E(E&&) {} - }; - static_assert(std::is_nothrow_move_constructible<variant<U>>::value, ""); - static_assert(std::is_nothrow_move_constructible<variant<U, int>>::value, ""); - static_assert(!std::is_nothrow_move_constructible<variant<U, E>>::value, ""); -} - -// Test that for each slot, constructing a variant with that type -// produces a sensible object that correctly reports its type, and -// that copies the provided value. -TYPED_TEST(VariantTypesTest, TestValueCtor) { - typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - using value_type2 = absl::variant_alternative_t<1, Variant>; - using value_type3 = absl::variant_alternative_t<2, Variant>; - using value_type4 = absl::variant_alternative_t<3, Variant>; - const TypeParam value(TypeParam::kIndex); - Variant v(value); - EXPECT_TRUE(absl::holds_alternative<value_type1>(v) || - TypeParam::kIndex != 1); - EXPECT_TRUE(absl::holds_alternative<value_type2>(v) || - TypeParam::kIndex != 2); - EXPECT_TRUE(absl::holds_alternative<value_type3>(v) || - TypeParam::kIndex != 3); - EXPECT_TRUE(absl::holds_alternative<value_type4>(v) || - TypeParam::kIndex != 4); - EXPECT_TRUE(nullptr != absl::get_if<value_type1>(&v) || - TypeParam::kIndex != 1); - EXPECT_TRUE(nullptr != absl::get_if<value_type2>(&v) || - TypeParam::kIndex != 2); - EXPECT_TRUE(nullptr != absl::get_if<value_type3>(&v) || - TypeParam::kIndex != 3); - EXPECT_TRUE(nullptr != absl::get_if<value_type4>(&v) || - TypeParam::kIndex != 4); - EXPECT_TRUE(nullptr != absl::get_if<value_type1>(&v) || - TypeParam::kIndex != 1); - EXPECT_TRUE(nullptr != absl::get_if<value_type2>(&v) || - TypeParam::kIndex != 2); - EXPECT_TRUE(nullptr != absl::get_if<value_type3>(&v) || - TypeParam::kIndex != 3); - EXPECT_TRUE(nullptr != absl::get_if<value_type4>(&v) || - TypeParam::kIndex != 4); - const TypeParam* valptr = absl::get_if<TypeParam>(&v); - ASSERT_TRUE(nullptr != valptr); - EXPECT_EQ(value.value, valptr->value); - const TypeParam* mutable_valptr = absl::get_if<TypeParam>(&v); - ASSERT_TRUE(nullptr != mutable_valptr); - EXPECT_EQ(value.value, mutable_valptr->value); -} - -TEST(VariantTest, AmbiguousValueConstructor) { - EXPECT_FALSE((std::is_convertible<int, absl::variant<int, int>>::value)); - EXPECT_FALSE((std::is_constructible<absl::variant<int, int>, int>::value)); -} - -TEST(VariantTest, InPlaceType) { - using Var = variant<int, std::string, NonCopyable, std::vector<int>>; - - Var v1(in_place_type_t<int>(), 7); - ASSERT_TRUE(absl::holds_alternative<int>(v1)); - EXPECT_EQ(7, absl::get<int>(v1)); - - Var v2(in_place_type_t<std::string>(), "ABC"); - ASSERT_TRUE(absl::holds_alternative<std::string>(v2)); - EXPECT_EQ("ABC", absl::get<std::string>(v2)); - - Var v3(in_place_type_t<std::string>(), "ABC", 2u); - ASSERT_TRUE(absl::holds_alternative<std::string>(v3)); - EXPECT_EQ("AB", absl::get<std::string>(v3)); - - Var v4(in_place_type_t<NonCopyable>{}); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v4)); - - Var v5(in_place_type_t<std::vector<int>>(), {1, 2, 3}); - ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5)); - EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); -} - -TEST(VariantTest, InPlaceTypeVariableTemplate) { - using Var = variant<int, std::string, NonCopyable, std::vector<int>>; - - Var v1(in_place_type<int>, 7); - ASSERT_TRUE(absl::holds_alternative<int>(v1)); - EXPECT_EQ(7, absl::get<int>(v1)); - - Var v2(in_place_type<std::string>, "ABC"); - ASSERT_TRUE(absl::holds_alternative<std::string>(v2)); - EXPECT_EQ("ABC", absl::get<std::string>(v2)); - - Var v3(in_place_type<std::string>, "ABC", 2u); - ASSERT_TRUE(absl::holds_alternative<std::string>(v3)); - EXPECT_EQ("AB", absl::get<std::string>(v3)); - - Var v4(in_place_type<NonCopyable>); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v4)); - - Var v5(in_place_type<std::vector<int>>, {1, 2, 3}); - ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5)); - EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); -} - -TEST(VariantTest, InPlaceTypeInitializerList) { - using Var = - variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - - Var v1(in_place_type_t<MoveOnlyWithListConstructor>(), {1, 2, 3, 4, 5}, 6); - ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); - EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); -} - -TEST(VariantTest, InPlaceTypeInitializerListVariabletemplate) { - using Var = - variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - - Var v1(in_place_type<MoveOnlyWithListConstructor>, {1, 2, 3, 4, 5}, 6); - ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); - EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); -} - -TEST(VariantTest, InPlaceIndex) { - using Var = variant<int, std::string, NonCopyable, std::vector<int>>; - - Var v1(in_place_index_t<0>(), 7); - ASSERT_TRUE(absl::holds_alternative<int>(v1)); - EXPECT_EQ(7, absl::get<int>(v1)); - - Var v2(in_place_index_t<1>(), "ABC"); - ASSERT_TRUE(absl::holds_alternative<std::string>(v2)); - EXPECT_EQ("ABC", absl::get<std::string>(v2)); - - Var v3(in_place_index_t<1>(), "ABC", 2u); - ASSERT_TRUE(absl::holds_alternative<std::string>(v3)); - EXPECT_EQ("AB", absl::get<std::string>(v3)); - - Var v4(in_place_index_t<2>{}); - EXPECT_TRUE(absl::holds_alternative<NonCopyable>(v4)); - - // Verify that a variant with only non-copyables can still be constructed. - EXPECT_TRUE(absl::holds_alternative<NonCopyable>( - variant<NonCopyable>(in_place_index_t<0>{}))); - - Var v5(in_place_index_t<3>(), {1, 2, 3}); - ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5)); - EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); -} - -TEST(VariantTest, InPlaceIndexVariableTemplate) { - using Var = variant<int, std::string, NonCopyable, std::vector<int>>; - - Var v1(in_place_index<0>, 7); - ASSERT_TRUE(absl::holds_alternative<int>(v1)); - EXPECT_EQ(7, absl::get<int>(v1)); - - Var v2(in_place_index<1>, "ABC"); - ASSERT_TRUE(absl::holds_alternative<std::string>(v2)); - EXPECT_EQ("ABC", absl::get<std::string>(v2)); - - Var v3(in_place_index<1>, "ABC", 2u); - ASSERT_TRUE(absl::holds_alternative<std::string>(v3)); - EXPECT_EQ("AB", absl::get<std::string>(v3)); - - Var v4(in_place_index<2>); - EXPECT_TRUE(absl::holds_alternative<NonCopyable>(v4)); - - // Verify that a variant with only non-copyables can still be constructed. - EXPECT_TRUE(absl::holds_alternative<NonCopyable>( - variant<NonCopyable>(in_place_index<0>))); - - Var v5(in_place_index<3>, {1, 2, 3}); - ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5)); - EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); -} - -TEST(VariantTest, InPlaceIndexInitializerList) { - using Var = - variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - - Var v1(in_place_index_t<3>(), {1, 2, 3, 4, 5}, 6); - ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); - EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); -} - -TEST(VariantTest, InPlaceIndexInitializerListVariableTemplate) { - using Var = - variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - - Var v1(in_place_index<3>, {1, 2, 3, 4, 5}, 6); - ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); - EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); -} - -//////////////////// -// [variant.dtor] // -//////////////////// - -// Make sure that the destructor destroys the contained value -TEST(VariantTest, TestDtor) { - typedef VariantFactory<IncrementInDtor>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - using value_type2 = absl::variant_alternative_t<1, Variant>; - using value_type3 = absl::variant_alternative_t<2, Variant>; - using value_type4 = absl::variant_alternative_t<3, Variant>; - int counter = 0; - IncrementInDtor counter_adjuster(&counter); - EXPECT_EQ(0, counter); - - value_type1 value1(counter_adjuster); - { Variant object(value1); } - EXPECT_EQ(1, counter); - - value_type2 value2(counter_adjuster); - { Variant object(value2); } - EXPECT_EQ(2, counter); - - value_type3 value3(counter_adjuster); - { Variant object(value3); } - EXPECT_EQ(3, counter); - - value_type4 value4(counter_adjuster); - { Variant object(value4); } - EXPECT_EQ(4, counter); -} - -#ifdef ABSL_HAVE_EXCEPTIONS - -// See comment in absl/base/config.h -#if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) -TEST(VariantTest, DISABLED_TestDtorValuelessByException) -#else -// Test destruction when in the valueless_by_exception state. -TEST(VariantTest, TestDtorValuelessByException) -#endif -{ - int counter = 0; - IncrementInDtor counter_adjuster(&counter); - - { - using Variant = VariantFactory<IncrementInDtor>::Type; - - Variant v(in_place_index<0>, counter_adjuster); - EXPECT_EQ(0, counter); - - ToValuelessByException(v); - ASSERT_TRUE(v.valueless_by_exception()); - EXPECT_EQ(1, counter); - } - EXPECT_EQ(1, counter); -} - -#endif // ABSL_HAVE_EXCEPTIONS - -////////////////////// -// [variant.assign] // -////////////////////// - -// Test that self-assignment doesn't destroy the current value -TEST(VariantTest, TestSelfAssignment) { - typedef VariantFactory<IncrementInDtor>::Type Variant; - int counter = 0; - IncrementInDtor counter_adjuster(&counter); - absl::variant_alternative_t<0, Variant> value(counter_adjuster); - Variant object(value); - object.operator=(object); - EXPECT_EQ(0, counter); - - // A string long enough that it's likely to defeat any inline representation - // optimization. - const std::string long_str(128, 'a'); - - std::string foo = long_str; - foo = *&foo; - EXPECT_EQ(long_str, foo); - - variant<int, std::string> so = long_str; - ASSERT_EQ(1u, so.index()); - EXPECT_EQ(long_str, absl::get<1>(so)); - so = *&so; - - ASSERT_EQ(1u, so.index()); - EXPECT_EQ(long_str, absl::get<1>(so)); -} - -// Test that assigning a variant<..., T, ...> to a variant<..., T, ...> produces -// a variant<..., T, ...> with the correct value. -TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValueSameTypes) { - typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant; - const TypeParam value(TypeParam::kIndex); - const Variant source(value); - Variant target(TypeParam(value.value + 1)); - ASSERT_TRUE(absl::holds_alternative<TypeParam>(source)); - ASSERT_TRUE(absl::holds_alternative<TypeParam>(target)); - ASSERT_NE(absl::get<TypeParam>(source), absl::get<TypeParam>(target)); - target = source; - ASSERT_TRUE(absl::holds_alternative<TypeParam>(source)); - ASSERT_TRUE(absl::holds_alternative<TypeParam>(target)); - EXPECT_EQ(absl::get<TypeParam>(source), absl::get<TypeParam>(target)); -} - -// Test that assisnging a variant<..., T, ...> to a variant<1, ...> -// produces a variant<..., T, ...> with the correct value. -TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingSourceType) { - typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - const TypeParam value(TypeParam::kIndex); - const Variant source(value); - ASSERT_TRUE(absl::holds_alternative<TypeParam>(source)); - Variant target(value_type1(1)); - ASSERT_TRUE(absl::holds_alternative<value_type1>(target)); - target = source; - EXPECT_TRUE(absl::holds_alternative<TypeParam>(source)); - EXPECT_TRUE(absl::holds_alternative<TypeParam>(target)); - EXPECT_EQ(absl::get<TypeParam>(source), absl::get<TypeParam>(target)); -} - -// Test that assigning a variant<1, ...> to a variant<..., T, ...> -// produces a variant<1, ...> with the correct value. -TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingTargetType) { - typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - const Variant source(value_type1(1)); - ASSERT_TRUE(absl::holds_alternative<value_type1>(source)); - const TypeParam value(TypeParam::kIndex); - Variant target(value); - ASSERT_TRUE(absl::holds_alternative<TypeParam>(target)); - target = source; - EXPECT_TRUE(absl::holds_alternative<value_type1>(target)); - EXPECT_TRUE(absl::holds_alternative<value_type1>(source)); - EXPECT_EQ(absl::get<value_type1>(source), absl::get<value_type1>(target)); -} - -// Test that operator=<T> works, that assigning a new value destroys -// the old and that assigning the new value again does not redestroy -// the old -TEST(VariantTest, TestAssign) { - typedef VariantFactory<IncrementInDtor>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - using value_type2 = absl::variant_alternative_t<1, Variant>; - using value_type3 = absl::variant_alternative_t<2, Variant>; - using value_type4 = absl::variant_alternative_t<3, Variant>; - - const int kSize = 4; - int counter[kSize]; - std::unique_ptr<IncrementInDtor> counter_adjustor[kSize]; - for (int i = 0; i != kSize; i++) { - counter[i] = 0; - counter_adjustor[i] = absl::make_unique<IncrementInDtor>(&counter[i]); - } - - value_type1 v1(*counter_adjustor[0]); - value_type2 v2(*counter_adjustor[1]); - value_type3 v3(*counter_adjustor[2]); - value_type4 v4(*counter_adjustor[3]); - - // Test that reassignment causes destruction of old value - { - Variant object(v1); - object = v2; - object = v3; - object = v4; - object = v1; - } - - EXPECT_EQ(2, counter[0]); - EXPECT_EQ(1, counter[1]); - EXPECT_EQ(1, counter[2]); - EXPECT_EQ(1, counter[3]); - - std::fill(std::begin(counter), std::end(counter), 0); - - // Test that self-assignment does not cause destruction of old value - { - Variant object(v1); - object.operator=(object); - EXPECT_EQ(0, counter[0]); - } - { - Variant object(v2); - object.operator=(object); - EXPECT_EQ(0, counter[1]); - } - { - Variant object(v3); - object.operator=(object); - EXPECT_EQ(0, counter[2]); - } - { - Variant object(v4); - object.operator=(object); - EXPECT_EQ(0, counter[3]); - } - - EXPECT_EQ(1, counter[0]); - EXPECT_EQ(1, counter[1]); - EXPECT_EQ(1, counter[2]); - EXPECT_EQ(1, counter[3]); -} - -// This tests that we perform a backup if the copy-assign can throw but the move -// cannot throw. -TEST(VariantTest, TestBackupAssign) { - typedef VariantFactory<IncrementInDtorCopyCanThrow>::Type Variant; - using value_type1 = absl::variant_alternative_t<0, Variant>; - using value_type2 = absl::variant_alternative_t<1, Variant>; - using value_type3 = absl::variant_alternative_t<2, Variant>; - using value_type4 = absl::variant_alternative_t<3, Variant>; - - const int kSize = 4; - int counter[kSize]; - std::unique_ptr<IncrementInDtorCopyCanThrow> counter_adjustor[kSize]; - for (int i = 0; i != kSize; i++) { - counter[i] = 0; - counter_adjustor[i].reset(new IncrementInDtorCopyCanThrow(&counter[i])); - } - - value_type1 v1(*counter_adjustor[0]); - value_type2 v2(*counter_adjustor[1]); - value_type3 v3(*counter_adjustor[2]); - value_type4 v4(*counter_adjustor[3]); - - // Test that reassignment causes destruction of old value - { - Variant object(v1); - object = v2; - object = v3; - object = v4; - object = v1; - } - - // libstdc++ doesn't pass this test -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - EXPECT_EQ(3, counter[0]); - EXPECT_EQ(2, counter[1]); - EXPECT_EQ(2, counter[2]); - EXPECT_EQ(2, counter[3]); -#endif - - std::fill(std::begin(counter), std::end(counter), 0); - - // Test that self-assignment does not cause destruction of old value - { - Variant object(v1); - object.operator=(object); - EXPECT_EQ(0, counter[0]); - } - { - Variant object(v2); - object.operator=(object); - EXPECT_EQ(0, counter[1]); - } - { - Variant object(v3); - object.operator=(object); - EXPECT_EQ(0, counter[2]); - } - { - Variant object(v4); - object.operator=(object); - EXPECT_EQ(0, counter[3]); - } - - EXPECT_EQ(1, counter[0]); - EXPECT_EQ(1, counter[1]); - EXPECT_EQ(1, counter[2]); - EXPECT_EQ(1, counter[3]); -} - -/////////////////// -// [variant.mod] // -/////////////////// - -TEST(VariantTest, TestEmplaceBasic) { - using Variant = variant<int, char>; - - Variant v(absl::in_place_index<0>, 0); - - { - char& emplace_result = v.emplace<char>(); - ASSERT_TRUE(absl::holds_alternative<char>(v)); - EXPECT_EQ(absl::get<char>(v), 0); - EXPECT_EQ(&emplace_result, &absl::get<char>(v)); - } - - // Make sure that another emplace does zero-initialization - absl::get<char>(v) = 'a'; - v.emplace<char>('b'); - ASSERT_TRUE(absl::holds_alternative<char>(v)); - EXPECT_EQ(absl::get<char>(v), 'b'); - - { - int& emplace_result = v.emplace<int>(); - EXPECT_TRUE(absl::holds_alternative<int>(v)); - EXPECT_EQ(absl::get<int>(v), 0); - EXPECT_EQ(&emplace_result, &absl::get<int>(v)); - } -} - -TEST(VariantTest, TestEmplaceInitializerList) { - using Var = - variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - - Var v1(absl::in_place_index<0>, 555); - MoveOnlyWithListConstructor& emplace_result = - v1.emplace<MoveOnlyWithListConstructor>({1, 2, 3, 4, 5}, 6); - ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); - EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); - EXPECT_EQ(&emplace_result, &absl::get<MoveOnlyWithListConstructor>(v1)); -} - -TEST(VariantTest, TestEmplaceIndex) { - using Variant = variant<int, char>; - - Variant v(absl::in_place_index<0>, 555); - - { - char& emplace_result = v.emplace<1>(); - ASSERT_TRUE(absl::holds_alternative<char>(v)); - EXPECT_EQ(absl::get<char>(v), 0); - EXPECT_EQ(&emplace_result, &absl::get<char>(v)); - } - - // Make sure that another emplace does zero-initialization - absl::get<char>(v) = 'a'; - v.emplace<1>('b'); - ASSERT_TRUE(absl::holds_alternative<char>(v)); - EXPECT_EQ(absl::get<char>(v), 'b'); - - { - int& emplace_result = v.emplace<0>(); - EXPECT_TRUE(absl::holds_alternative<int>(v)); - EXPECT_EQ(absl::get<int>(v), 0); - EXPECT_EQ(&emplace_result, &absl::get<int>(v)); - } -} - -TEST(VariantTest, TestEmplaceIndexInitializerList) { - using Var = - variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - - Var v1(absl::in_place_index<0>, 555); - MoveOnlyWithListConstructor& emplace_result = - v1.emplace<3>({1, 2, 3, 4, 5}, 6); - ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); - EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); - EXPECT_EQ(&emplace_result, &absl::get<MoveOnlyWithListConstructor>(v1)); -} - -////////////////////// -// [variant.status] // -////////////////////// - -TEST(VariantTest, Index) { - using Var = variant<int, std::string, double>; - - Var v = 1; - EXPECT_EQ(0u, v.index()); - v = "str"; - EXPECT_EQ(1u, v.index()); - v = 0.; - EXPECT_EQ(2u, v.index()); - - Var v2 = v; - EXPECT_EQ(2u, v2.index()); - v2.emplace<int>(3); - EXPECT_EQ(0u, v2.index()); -} - -TEST(VariantTest, NotValuelessByException) { - using Var = variant<int, std::string, double>; - - Var v = 1; - EXPECT_FALSE(v.valueless_by_exception()); - v = "str"; - EXPECT_FALSE(v.valueless_by_exception()); - v = 0.; - EXPECT_FALSE(v.valueless_by_exception()); - - Var v2 = v; - EXPECT_FALSE(v.valueless_by_exception()); - v2.emplace<int>(3); - EXPECT_FALSE(v.valueless_by_exception()); -} - -#ifdef ABSL_HAVE_EXCEPTIONS - -TEST(VariantTest, IndexValuelessByException) { - using Var = variant<MoveCanThrow, std::string, double>; - - Var v(absl::in_place_index<0>); - EXPECT_EQ(0u, v.index()); - ToValuelessByException(v); - EXPECT_EQ(absl::variant_npos, v.index()); - v = "str"; - EXPECT_EQ(1u, v.index()); -} - -TEST(VariantTest, ValuelessByException) { - using Var = variant<MoveCanThrow, std::string, double>; - - Var v(absl::in_place_index<0>); - EXPECT_FALSE(v.valueless_by_exception()); - ToValuelessByException(v); - EXPECT_TRUE(v.valueless_by_exception()); - v = "str"; - EXPECT_FALSE(v.valueless_by_exception()); -} - -#endif // ABSL_HAVE_EXCEPTIONS - -//////////////////// -// [variant.swap] // -//////////////////// - -TEST(VariantTest, MemberSwap) { - SpecialSwap v1(3); - SpecialSwap v2(7); - - variant<SpecialSwap> a = v1, b = v2; - - EXPECT_THAT(a, VariantWith<SpecialSwap>(v1)); - EXPECT_THAT(b, VariantWith<SpecialSwap>(v2)); - - a.swap(b); - EXPECT_THAT(a, VariantWith<SpecialSwap>(v2)); - EXPECT_THAT(b, VariantWith<SpecialSwap>(v1)); - EXPECT_TRUE(absl::get<SpecialSwap>(a).special_swap); - - using V = variant<MoveCanThrow, std::string, int>; - int i = 33; - std::string s = "abc"; - { - // lhs and rhs holds different alternative - V lhs(i), rhs(s); - lhs.swap(rhs); - EXPECT_THAT(lhs, VariantWith<std::string>(s)); - EXPECT_THAT(rhs, VariantWith<int>(i)); - } -#ifdef ABSL_HAVE_EXCEPTIONS - V valueless(in_place_index<0>); - ToValuelessByException(valueless); - { - // lhs is valueless - V lhs(valueless), rhs(i); - lhs.swap(rhs); - EXPECT_THAT(lhs, VariantWith<int>(i)); - EXPECT_TRUE(rhs.valueless_by_exception()); - } - { - // rhs is valueless - V lhs(s), rhs(valueless); - lhs.swap(rhs); - EXPECT_THAT(rhs, VariantWith<std::string>(s)); - EXPECT_TRUE(lhs.valueless_by_exception()); - } - { - // both are valueless - V lhs(valueless), rhs(valueless); - lhs.swap(rhs); - EXPECT_TRUE(lhs.valueless_by_exception()); - EXPECT_TRUE(rhs.valueless_by_exception()); - } -#endif // ABSL_HAVE_EXCEPTIONS -} - -////////////////////// -// [variant.helper] // -////////////////////// - -TEST(VariantTest, VariantSize) { - { - using Size1Variant = absl::variant<int>; - EXPECT_EQ(1u, absl::variant_size<Size1Variant>::value); - EXPECT_EQ(1u, absl::variant_size<const Size1Variant>::value); - EXPECT_EQ(1u, absl::variant_size<volatile Size1Variant>::value); - EXPECT_EQ(1u, absl::variant_size<const volatile Size1Variant>::value); - } - - { - using Size3Variant = absl::variant<int, float, int>; - EXPECT_EQ(3u, absl::variant_size<Size3Variant>::value); - EXPECT_EQ(3u, absl::variant_size<const Size3Variant>::value); - EXPECT_EQ(3u, absl::variant_size<volatile Size3Variant>::value); - EXPECT_EQ(3u, absl::variant_size<const volatile Size3Variant>::value); - } -} - -TEST(VariantTest, VariantAlternative) { - { - using V = absl::variant<float, int, const char*>; - EXPECT_TRUE( - (std::is_same<float, absl::variant_alternative_t<0, V>>::value)); - EXPECT_TRUE((std::is_same<const float, - absl::variant_alternative_t<0, const V>>::value)); - EXPECT_TRUE( - (std::is_same<volatile float, - absl::variant_alternative_t<0, volatile V>>::value)); - EXPECT_TRUE(( - std::is_same<const volatile float, - absl::variant_alternative_t<0, const volatile V>>::value)); - - EXPECT_TRUE((std::is_same<int, absl::variant_alternative_t<1, V>>::value)); - EXPECT_TRUE((std::is_same<const int, - absl::variant_alternative_t<1, const V>>::value)); - EXPECT_TRUE( - (std::is_same<volatile int, - absl::variant_alternative_t<1, volatile V>>::value)); - EXPECT_TRUE(( - std::is_same<const volatile int, - absl::variant_alternative_t<1, const volatile V>>::value)); - - EXPECT_TRUE( - (std::is_same<const char*, absl::variant_alternative_t<2, V>>::value)); - EXPECT_TRUE((std::is_same<const char* const, - absl::variant_alternative_t<2, const V>>::value)); - EXPECT_TRUE( - (std::is_same<const char* volatile, - absl::variant_alternative_t<2, volatile V>>::value)); - EXPECT_TRUE(( - std::is_same<const char* const volatile, - absl::variant_alternative_t<2, const volatile V>>::value)); - } - - { - using V = absl::variant<float, volatile int, const char*>; - EXPECT_TRUE( - (std::is_same<float, absl::variant_alternative_t<0, V>>::value)); - EXPECT_TRUE((std::is_same<const float, - absl::variant_alternative_t<0, const V>>::value)); - EXPECT_TRUE( - (std::is_same<volatile float, - absl::variant_alternative_t<0, volatile V>>::value)); - EXPECT_TRUE(( - std::is_same<const volatile float, - absl::variant_alternative_t<0, const volatile V>>::value)); - - EXPECT_TRUE( - (std::is_same<volatile int, absl::variant_alternative_t<1, V>>::value)); - EXPECT_TRUE((std::is_same<const volatile int, - absl::variant_alternative_t<1, const V>>::value)); - EXPECT_TRUE( - (std::is_same<volatile int, - absl::variant_alternative_t<1, volatile V>>::value)); - EXPECT_TRUE(( - std::is_same<const volatile int, - absl::variant_alternative_t<1, const volatile V>>::value)); - - EXPECT_TRUE( - (std::is_same<const char*, absl::variant_alternative_t<2, V>>::value)); - EXPECT_TRUE((std::is_same<const char* const, - absl::variant_alternative_t<2, const V>>::value)); - EXPECT_TRUE( - (std::is_same<const char* volatile, - absl::variant_alternative_t<2, volatile V>>::value)); - EXPECT_TRUE(( - std::is_same<const char* const volatile, - absl::variant_alternative_t<2, const volatile V>>::value)); - } -} - -/////////////////// -// [variant.get] // -/////////////////// - -TEST(VariantTest, HoldsAlternative) { - using Var = variant<int, std::string, double>; - - Var v = 1; - EXPECT_TRUE(absl::holds_alternative<int>(v)); - EXPECT_FALSE(absl::holds_alternative<std::string>(v)); - EXPECT_FALSE(absl::holds_alternative<double>(v)); - v = "str"; - EXPECT_FALSE(absl::holds_alternative<int>(v)); - EXPECT_TRUE(absl::holds_alternative<std::string>(v)); - EXPECT_FALSE(absl::holds_alternative<double>(v)); - v = 0.; - EXPECT_FALSE(absl::holds_alternative<int>(v)); - EXPECT_FALSE(absl::holds_alternative<std::string>(v)); - EXPECT_TRUE(absl::holds_alternative<double>(v)); - - Var v2 = v; - EXPECT_FALSE(absl::holds_alternative<int>(v2)); - EXPECT_FALSE(absl::holds_alternative<std::string>(v2)); - EXPECT_TRUE(absl::holds_alternative<double>(v2)); - v2.emplace<int>(3); - EXPECT_TRUE(absl::holds_alternative<int>(v2)); - EXPECT_FALSE(absl::holds_alternative<std::string>(v2)); - EXPECT_FALSE(absl::holds_alternative<double>(v2)); -} - -TEST(VariantTest, GetIndex) { - using Var = variant<int, std::string, double, int>; - - { - Var v(absl::in_place_index<0>, 0); - - using LValueGetType = decltype(absl::get<0>(v)); - using RValueGetType = decltype(absl::get<0>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, int&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value)); - EXPECT_EQ(absl::get<0>(v), 0); - EXPECT_EQ(absl::get<0>(std::move(v)), 0); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<0>(const_v)); - using ConstRValueGetType = decltype(absl::get<0>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value)); - EXPECT_EQ(absl::get<0>(const_v), 0); - EXPECT_EQ(absl::get<0>(std::move(const_v)), 0); - } - - { - Var v = std::string("Hello"); - - using LValueGetType = decltype(absl::get<1>(v)); - using RValueGetType = decltype(absl::get<1>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, std::string&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, std::string&&>::value)); - EXPECT_EQ(absl::get<1>(v), "Hello"); - EXPECT_EQ(absl::get<1>(std::move(v)), "Hello"); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<1>(const_v)); - using ConstRValueGetType = decltype(absl::get<1>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const std::string&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const std::string&&>::value)); - EXPECT_EQ(absl::get<1>(const_v), "Hello"); - EXPECT_EQ(absl::get<1>(std::move(const_v)), "Hello"); - } - - { - Var v = 2.0; - - using LValueGetType = decltype(absl::get<2>(v)); - using RValueGetType = decltype(absl::get<2>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, double&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, double&&>::value)); - EXPECT_EQ(absl::get<2>(v), 2.); - EXPECT_EQ(absl::get<2>(std::move(v)), 2.); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<2>(const_v)); - using ConstRValueGetType = decltype(absl::get<2>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const double&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const double&&>::value)); - EXPECT_EQ(absl::get<2>(const_v), 2.); - EXPECT_EQ(absl::get<2>(std::move(const_v)), 2.); - } - - { - Var v(absl::in_place_index<0>, 0); - v.emplace<3>(1); - - using LValueGetType = decltype(absl::get<3>(v)); - using RValueGetType = decltype(absl::get<3>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, int&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value)); - EXPECT_EQ(absl::get<3>(v), 1); - EXPECT_EQ(absl::get<3>(std::move(v)), 1); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<3>(const_v)); - using ConstRValueGetType = decltype(absl::get<3>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value)); - EXPECT_EQ(absl::get<3>(const_v), 1); - EXPECT_EQ(absl::get<3>(std::move(const_v)), 1); // NOLINT - } -} - -TEST(VariantTest, BadGetIndex) { - using Var = variant<int, std::string, double>; - - { - Var v = 1; - - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(std::move(v))); - - const Var& const_v = v; - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(const_v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( - absl::get<1>(std::move(const_v))); // NOLINT - } - - { - Var v = std::string("Hello"); - - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(std::move(v))); - - const Var& const_v = v; - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(const_v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( - absl::get<0>(std::move(const_v))); // NOLINT - } -} - -TEST(VariantTest, GetType) { - using Var = variant<int, std::string, double>; - - { - Var v = 1; - - using LValueGetType = decltype(absl::get<int>(v)); - using RValueGetType = decltype(absl::get<int>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, int&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value)); - EXPECT_EQ(absl::get<int>(v), 1); - EXPECT_EQ(absl::get<int>(std::move(v)), 1); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<int>(const_v)); - using ConstRValueGetType = decltype(absl::get<int>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value)); - EXPECT_EQ(absl::get<int>(const_v), 1); - EXPECT_EQ(absl::get<int>(std::move(const_v)), 1); - } - - { - Var v = std::string("Hello"); - - using LValueGetType = decltype(absl::get<1>(v)); - using RValueGetType = decltype(absl::get<1>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, std::string&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, std::string&&>::value)); - EXPECT_EQ(absl::get<std::string>(v), "Hello"); - EXPECT_EQ(absl::get<std::string>(std::move(v)), "Hello"); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<1>(const_v)); - using ConstRValueGetType = decltype(absl::get<1>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const std::string&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const std::string&&>::value)); - EXPECT_EQ(absl::get<std::string>(const_v), "Hello"); - EXPECT_EQ(absl::get<std::string>(std::move(const_v)), "Hello"); - } - - { - Var v = 2.0; - - using LValueGetType = decltype(absl::get<2>(v)); - using RValueGetType = decltype(absl::get<2>(std::move(v))); - - EXPECT_TRUE((std::is_same<LValueGetType, double&>::value)); - EXPECT_TRUE((std::is_same<RValueGetType, double&&>::value)); - EXPECT_EQ(absl::get<double>(v), 2.); - EXPECT_EQ(absl::get<double>(std::move(v)), 2.); - - const Var& const_v = v; - using ConstLValueGetType = decltype(absl::get<2>(const_v)); - using ConstRValueGetType = decltype(absl::get<2>(std::move(const_v))); - EXPECT_TRUE((std::is_same<ConstLValueGetType, const double&>::value)); - EXPECT_TRUE((std::is_same<ConstRValueGetType, const double&&>::value)); - EXPECT_EQ(absl::get<double>(const_v), 2.); - EXPECT_EQ(absl::get<double>(std::move(const_v)), 2.); - } -} - -TEST(VariantTest, BadGetType) { - using Var = variant<int, std::string, double>; - - { - Var v = 1; - - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<std::string>(v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( - absl::get<std::string>(std::move(v))); - - const Var& const_v = v; - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( - absl::get<std::string>(const_v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( - absl::get<std::string>(std::move(const_v))); // NOLINT - } - - { - Var v = std::string("Hello"); - - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(std::move(v))); - - const Var& const_v = v; - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(const_v)); - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( - absl::get<int>(std::move(const_v))); // NOLINT - } -} - -TEST(VariantTest, GetIfIndex) { - using Var = variant<int, std::string, double, int>; - - { - Var v(absl::in_place_index<0>, 0); - EXPECT_TRUE(noexcept(absl::get_if<0>(&v))); - - { - auto* elem = absl::get_if<0>(&v); - EXPECT_TRUE((std::is_same<decltype(elem), int*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, 0); - { - auto* bad_elem = absl::get_if<1>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<2>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<3>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - } - - const Var& const_v = v; - EXPECT_TRUE(noexcept(absl::get_if<0>(&const_v))); - - { - auto* elem = absl::get_if<0>(&const_v); - EXPECT_TRUE((std::is_same<decltype(elem), const int*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, 0); - { - auto* bad_elem = absl::get_if<1>(&const_v); - EXPECT_TRUE( - (std::is_same<decltype(bad_elem), const std::string*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<2>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<3>(&const_v); - EXPECT_EQ(bad_elem, nullptr); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value)); - } - } - } - - { - Var v = std::string("Hello"); - EXPECT_TRUE(noexcept(absl::get_if<1>(&v))); - - { - auto* elem = absl::get_if<1>(&v); - EXPECT_TRUE((std::is_same<decltype(elem), std::string*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, "Hello"); - { - auto* bad_elem = absl::get_if<0>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<2>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<3>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - } - - const Var& const_v = v; - EXPECT_TRUE(noexcept(absl::get_if<1>(&const_v))); - - { - auto* elem = absl::get_if<1>(&const_v); - EXPECT_TRUE((std::is_same<decltype(elem), const std::string*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, "Hello"); - { - auto* bad_elem = absl::get_if<0>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<2>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<3>(&const_v); - EXPECT_EQ(bad_elem, nullptr); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value)); - } - } - } - - { - Var v = 2.0; - EXPECT_TRUE(noexcept(absl::get_if<2>(&v))); - - { - auto* elem = absl::get_if<2>(&v); - EXPECT_TRUE((std::is_same<decltype(elem), double*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, 2.0); - { - auto* bad_elem = absl::get_if<0>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<1>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<3>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - } - - const Var& const_v = v; - EXPECT_TRUE(noexcept(absl::get_if<2>(&const_v))); - - { - auto* elem = absl::get_if<2>(&const_v); - EXPECT_TRUE((std::is_same<decltype(elem), const double*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, 2.0); - { - auto* bad_elem = absl::get_if<0>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<1>(&const_v); - EXPECT_TRUE( - (std::is_same<decltype(bad_elem), const std::string*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<3>(&const_v); - EXPECT_EQ(bad_elem, nullptr); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value)); - } - } - } - - { - Var v(absl::in_place_index<0>, 0); - v.emplace<3>(1); - EXPECT_TRUE(noexcept(absl::get_if<3>(&v))); - - { - auto* elem = absl::get_if<3>(&v); - EXPECT_TRUE((std::is_same<decltype(elem), int*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, 1); - { - auto* bad_elem = absl::get_if<0>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<1>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<2>(&v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - } - - const Var& const_v = v; - EXPECT_TRUE(noexcept(absl::get_if<3>(&const_v))); - - { - auto* elem = absl::get_if<3>(&const_v); - EXPECT_TRUE((std::is_same<decltype(elem), const int*>::value)); - ASSERT_NE(elem, nullptr); - EXPECT_EQ(*elem, 1); - { - auto* bad_elem = absl::get_if<0>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<1>(&const_v); - EXPECT_TRUE( - (std::is_same<decltype(bad_elem), const std::string*>::value)); - EXPECT_EQ(bad_elem, nullptr); - } - { - auto* bad_elem = absl::get_if<2>(&const_v); - EXPECT_EQ(bad_elem, nullptr); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value)); - } - } - } -} - -////////////////////// -// [variant.relops] // -////////////////////// - -TEST(VariantTest, OperatorEquals) { - variant<int, std::string> a(1), b(1); - EXPECT_TRUE(a == b); - EXPECT_TRUE(b == a); - EXPECT_FALSE(a != b); - EXPECT_FALSE(b != a); - - b = "str"; - EXPECT_FALSE(a == b); - EXPECT_FALSE(b == a); - EXPECT_TRUE(a != b); - EXPECT_TRUE(b != a); - - b = 0; - EXPECT_FALSE(a == b); - EXPECT_FALSE(b == a); - EXPECT_TRUE(a != b); - EXPECT_TRUE(b != a); - - a = b = "foo"; - EXPECT_TRUE(a == b); - EXPECT_TRUE(b == a); - EXPECT_FALSE(a != b); - EXPECT_FALSE(b != a); - - a = "bar"; - EXPECT_FALSE(a == b); - EXPECT_FALSE(b == a); - EXPECT_TRUE(a != b); - EXPECT_TRUE(b != a); -} - -TEST(VariantTest, OperatorRelational) { - variant<int, std::string> a(1), b(1); - EXPECT_FALSE(a < b); - EXPECT_FALSE(b < a); - EXPECT_FALSE(a > b); - EXPECT_FALSE(b > a); - EXPECT_TRUE(a <= b); - EXPECT_TRUE(b <= a); - EXPECT_TRUE(a >= b); - EXPECT_TRUE(b >= a); - - b = "str"; - EXPECT_TRUE(a < b); - EXPECT_FALSE(b < a); - EXPECT_FALSE(a > b); - EXPECT_TRUE(b > a); - EXPECT_TRUE(a <= b); - EXPECT_FALSE(b <= a); - EXPECT_FALSE(a >= b); - EXPECT_TRUE(b >= a); - - b = 0; - EXPECT_FALSE(a < b); - EXPECT_TRUE(b < a); - EXPECT_TRUE(a > b); - EXPECT_FALSE(b > a); - EXPECT_FALSE(a <= b); - EXPECT_TRUE(b <= a); - EXPECT_TRUE(a >= b); - EXPECT_FALSE(b >= a); - - a = b = "foo"; - EXPECT_FALSE(a < b); - EXPECT_FALSE(b < a); - EXPECT_FALSE(a > b); - EXPECT_FALSE(b > a); - EXPECT_TRUE(a <= b); - EXPECT_TRUE(b <= a); - EXPECT_TRUE(a >= b); - EXPECT_TRUE(b >= a); - - a = "bar"; - EXPECT_TRUE(a < b); - EXPECT_FALSE(b < a); - EXPECT_FALSE(a > b); - EXPECT_TRUE(b > a); - EXPECT_TRUE(a <= b); - EXPECT_FALSE(b <= a); - EXPECT_FALSE(a >= b); - EXPECT_TRUE(b >= a); -} - -#ifdef ABSL_HAVE_EXCEPTIONS - -TEST(VariantTest, ValuelessOperatorEquals) { - variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"), - valueless(absl::in_place_index<0>), - other_valueless(absl::in_place_index<0>); - ToValuelessByException(valueless); - ToValuelessByException(other_valueless); - - EXPECT_TRUE(valueless == other_valueless); - EXPECT_TRUE(other_valueless == valueless); - EXPECT_FALSE(valueless == int_v); - EXPECT_FALSE(valueless == string_v); - EXPECT_FALSE(int_v == valueless); - EXPECT_FALSE(string_v == valueless); - - EXPECT_FALSE(valueless != other_valueless); - EXPECT_FALSE(other_valueless != valueless); - EXPECT_TRUE(valueless != int_v); - EXPECT_TRUE(valueless != string_v); - EXPECT_TRUE(int_v != valueless); - EXPECT_TRUE(string_v != valueless); -} - -TEST(VariantTest, ValuelessOperatorRelational) { - variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"), - valueless(absl::in_place_index<0>), - other_valueless(absl::in_place_index<0>); - ToValuelessByException(valueless); - ToValuelessByException(other_valueless); - - EXPECT_FALSE(valueless < other_valueless); - EXPECT_FALSE(other_valueless < valueless); - EXPECT_TRUE(valueless < int_v); - EXPECT_TRUE(valueless < string_v); - EXPECT_FALSE(int_v < valueless); - EXPECT_FALSE(string_v < valueless); - - EXPECT_TRUE(valueless <= other_valueless); - EXPECT_TRUE(other_valueless <= valueless); - EXPECT_TRUE(valueless <= int_v); - EXPECT_TRUE(valueless <= string_v); - EXPECT_FALSE(int_v <= valueless); - EXPECT_FALSE(string_v <= valueless); - - EXPECT_TRUE(valueless >= other_valueless); - EXPECT_TRUE(other_valueless >= valueless); - EXPECT_FALSE(valueless >= int_v); - EXPECT_FALSE(valueless >= string_v); - EXPECT_TRUE(int_v >= valueless); - EXPECT_TRUE(string_v >= valueless); - - EXPECT_FALSE(valueless > other_valueless); - EXPECT_FALSE(other_valueless > valueless); - EXPECT_FALSE(valueless > int_v); - EXPECT_FALSE(valueless > string_v); - EXPECT_TRUE(int_v > valueless); - EXPECT_TRUE(string_v > valueless); -} - -#endif - -///////////////////// -// [variant.visit] // -///////////////////// - -template <typename T> -struct ConvertTo { - template <typename U> - T operator()(const U& u) const { - return u; - } -}; - -TEST(VariantTest, VisitSimple) { - variant<std::string, const char*> v = "A"; - - std::string str = absl::visit(ConvertTo<std::string>{}, v); - EXPECT_EQ("A", str); - - v = std::string("B"); - - absl::string_view piece = absl::visit(ConvertTo<absl::string_view>{}, v); - EXPECT_EQ("B", piece); - - struct StrLen { - size_t operator()(const char* s) const { return strlen(s); } - size_t operator()(const std::string& s) const { return s.size(); } - }; - - v = "SomeStr"; - EXPECT_EQ(7u, absl::visit(StrLen{}, v)); - v = std::string("VeryLargeThisTime"); - EXPECT_EQ(17u, absl::visit(StrLen{}, v)); -} - -TEST(VariantTest, VisitRValue) { - variant<std::string> v = std::string("X"); - struct Visitor { - bool operator()(const std::string&) const { return false; } - bool operator()(std::string&&) const { return true; } // NOLINT - - int operator()(const std::string&, const std::string&) const { return 0; } - int operator()(const std::string&, std::string&&) const { - return 1; - } // NOLINT - int operator()(std::string&&, const std::string&) const { - return 2; - } // NOLINT - int operator()(std::string&&, std::string&&) const { return 3; } // NOLINT - }; - EXPECT_FALSE(absl::visit(Visitor{}, v)); - EXPECT_TRUE(absl::visit(Visitor{}, std::move(v))); - - // Also test the variadic overload. - EXPECT_EQ(0, absl::visit(Visitor{}, v, v)); - EXPECT_EQ(1, absl::visit(Visitor{}, v, std::move(v))); - EXPECT_EQ(2, absl::visit(Visitor{}, std::move(v), v)); - EXPECT_EQ(3, absl::visit(Visitor{}, std::move(v), std::move(v))); -} - -TEST(VariantTest, VisitRValueVisitor) { - variant<std::string> v = std::string("X"); - struct Visitor { - bool operator()(const std::string&) const& { return false; } - bool operator()(const std::string&) && { return true; } - }; - Visitor visitor; - EXPECT_FALSE(absl::visit(visitor, v)); - EXPECT_TRUE(absl::visit(Visitor{}, v)); -} - -TEST(VariantTest, VisitResultTypeDifferent) { - variant<std::string> v = std::string("X"); - struct LValue_LValue {}; - struct RValue_LValue {}; - struct LValue_RValue {}; - struct RValue_RValue {}; - struct Visitor { - LValue_LValue operator()(const std::string&) const& { return {}; } - RValue_LValue operator()(std::string&&) const& { return {}; } // NOLINT - LValue_RValue operator()(const std::string&) && { return {}; } - RValue_RValue operator()(std::string&&) && { return {}; } // NOLINT - } visitor; - - EXPECT_TRUE( - (std::is_same<LValue_LValue, decltype(absl::visit(visitor, v))>::value)); - EXPECT_TRUE( - (std::is_same<RValue_LValue, - decltype(absl::visit(visitor, std::move(v)))>::value)); - EXPECT_TRUE(( - std::is_same<LValue_RValue, decltype(absl::visit(Visitor{}, v))>::value)); - EXPECT_TRUE( - (std::is_same<RValue_RValue, - decltype(absl::visit(Visitor{}, std::move(v)))>::value)); -} - -TEST(VariantTest, VisitVariadic) { - using A = variant<int, std::string>; - using B = variant<std::unique_ptr<int>, absl::string_view>; - - struct Visitor { - std::pair<int, int> operator()(int a, std::unique_ptr<int> b) const { - return {a, *b}; - } - std::pair<int, int> operator()(absl::string_view a, - std::unique_ptr<int> b) const { - return {static_cast<int>(a.size()), static_cast<int>(*b)}; - } - std::pair<int, int> operator()(int a, absl::string_view b) const { - return {a, static_cast<int>(b.size())}; - } - std::pair<int, int> operator()(absl::string_view a, - absl::string_view b) const { - return {static_cast<int>(a.size()), static_cast<int>(b.size())}; - } - }; - - EXPECT_THAT(absl::visit(Visitor(), A(1), B(std::unique_ptr<int>(new int(7)))), - ::testing::Pair(1, 7)); - EXPECT_THAT(absl::visit(Visitor(), A(1), B(absl::string_view("ABC"))), - ::testing::Pair(1, 3)); - EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")), - B(std::unique_ptr<int>(new int(7)))), - ::testing::Pair(5, 7)); - EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")), - B(absl::string_view("ABC"))), - ::testing::Pair(5, 3)); -} - -TEST(VariantTest, VisitNoArgs) { - EXPECT_EQ(5, absl::visit([] { return 5; })); -} - -struct ConstFunctor { - int operator()(int a, int b) const { return a - b; } -}; - -struct MutableFunctor { - int operator()(int a, int b) { return a - b; } -}; - -struct Class { - int Method(int a, int b) { return a - b; } - int ConstMethod(int a, int b) const { return a - b; } - - int member; -}; - -TEST(VariantTest, VisitReferenceWrapper) { - ConstFunctor cf; - MutableFunctor mf; - absl::variant<int> three = 3; - absl::variant<int> two = 2; - - EXPECT_EQ(1, absl::visit(std::cref(cf), three, two)); - EXPECT_EQ(1, absl::visit(std::ref(cf), three, two)); - EXPECT_EQ(1, absl::visit(std::ref(mf), three, two)); -} - -// libstdc++ std::variant doesn't support the INVOKE semantics. -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) -TEST(VariantTest, VisitMemberFunction) { - absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>()); - absl::variant<std::unique_ptr<const Class>> cp( - absl::make_unique<const Class>()); - absl::variant<int> three = 3; - absl::variant<int> two = 2; - - EXPECT_EQ(1, absl::visit(&Class::Method, p, three, two)); - EXPECT_EQ(1, absl::visit(&Class::ConstMethod, p, three, two)); - EXPECT_EQ(1, absl::visit(&Class::ConstMethod, cp, three, two)); -} - -TEST(VariantTest, VisitDataMember) { - absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>(Class{42})); - absl::variant<std::unique_ptr<const Class>> cp( - absl::make_unique<const Class>(Class{42})); - EXPECT_EQ(42, absl::visit(&Class::member, p)); - - absl::visit(&Class::member, p) = 5; - EXPECT_EQ(5, absl::visit(&Class::member, p)); - - EXPECT_EQ(42, absl::visit(&Class::member, cp)); -} -#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - -///////////////////////// -// [variant.monostate] // -///////////////////////// - -TEST(VariantTest, MonostateBasic) { - absl::monostate mono; - (void)mono; - - // TODO(mattcalabrese) Expose move triviality metafunctions in absl. - EXPECT_TRUE(absl::is_trivially_default_constructible<absl::monostate>::value); - EXPECT_TRUE(is_trivially_move_constructible<absl::monostate>::value); - EXPECT_TRUE(absl::is_trivially_copy_constructible<absl::monostate>::value); - EXPECT_TRUE(is_trivially_move_assignable<absl::monostate>::value); - EXPECT_TRUE(absl::is_trivially_copy_assignable<absl::monostate>::value); - EXPECT_TRUE(absl::is_trivially_destructible<absl::monostate>::value); -} - -TEST(VariantTest, VariantMonostateDefaultConstruction) { - absl::variant<absl::monostate, NonDefaultConstructible> var; - EXPECT_EQ(var.index(), 0u); -} - -//////////////////////////////// -// [variant.monostate.relops] // -//////////////////////////////// - -TEST(VariantTest, MonostateComparisons) { - absl::monostate lhs, rhs; - - EXPECT_EQ(lhs, lhs); - EXPECT_EQ(lhs, rhs); - - EXPECT_FALSE(lhs != lhs); - EXPECT_FALSE(lhs != rhs); - EXPECT_FALSE(lhs < lhs); - EXPECT_FALSE(lhs < rhs); - EXPECT_FALSE(lhs > lhs); - EXPECT_FALSE(lhs > rhs); - - EXPECT_LE(lhs, lhs); - EXPECT_LE(lhs, rhs); - EXPECT_GE(lhs, lhs); - EXPECT_GE(lhs, rhs); - - EXPECT_TRUE(noexcept(std::declval<absl::monostate>() == - std::declval<absl::monostate>())); - EXPECT_TRUE(noexcept(std::declval<absl::monostate>() != - std::declval<absl::monostate>())); - EXPECT_TRUE(noexcept(std::declval<absl::monostate>() < - std::declval<absl::monostate>())); - EXPECT_TRUE(noexcept(std::declval<absl::monostate>() > - std::declval<absl::monostate>())); - EXPECT_TRUE(noexcept(std::declval<absl::monostate>() <= - std::declval<absl::monostate>())); - EXPECT_TRUE(noexcept(std::declval<absl::monostate>() >= - std::declval<absl::monostate>())); -} - -/////////////////////// -// [variant.specalg] // -/////////////////////// - -TEST(VariantTest, NonmemberSwap) { - using std::swap; - - SpecialSwap v1(3); - SpecialSwap v2(7); - - variant<SpecialSwap> a = v1, b = v2; - - EXPECT_THAT(a, VariantWith<SpecialSwap>(v1)); - EXPECT_THAT(b, VariantWith<SpecialSwap>(v2)); - - std::swap(a, b); - EXPECT_THAT(a, VariantWith<SpecialSwap>(v2)); - EXPECT_THAT(b, VariantWith<SpecialSwap>(v1)); -#ifndef ABSL_USES_STD_VARIANT - EXPECT_FALSE(absl::get<SpecialSwap>(a).special_swap); -#endif - - swap(a, b); - EXPECT_THAT(a, VariantWith<SpecialSwap>(v1)); - EXPECT_THAT(b, VariantWith<SpecialSwap>(v2)); - EXPECT_TRUE(absl::get<SpecialSwap>(b).special_swap); -} - -////////////////////////// -// [variant.bad.access] // -////////////////////////// - -TEST(VariantTest, BadAccess) { - EXPECT_TRUE(noexcept(absl::bad_variant_access())); - absl::bad_variant_access exception_obj; - std::exception* base = &exception_obj; - (void)base; -} - -//////////////////// -// [variant.hash] // -//////////////////// - -TEST(VariantTest, MonostateHash) { - absl::monostate mono, other_mono; - std::hash<absl::monostate> const hasher{}; - static_assert(std::is_same<decltype(hasher(mono)), std::size_t>::value, ""); - EXPECT_EQ(hasher(mono), hasher(other_mono)); -} - -TEST(VariantTest, Hash) { - static_assert(type_traits_internal::IsHashable<variant<int>>::value, ""); - static_assert(type_traits_internal::IsHashable<variant<Hashable>>::value, ""); - static_assert(type_traits_internal::IsHashable<variant<int, Hashable>>::value, - ""); - -#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ - static_assert(!type_traits_internal::IsHashable<variant<NonHashable>>::value, - ""); - static_assert( - !type_traits_internal::IsHashable<variant<Hashable, NonHashable>>::value, - ""); -#endif - -// MSVC std::hash<std::variant> does not use the index, thus produce the same -// result on the same value as different alternative. -#if !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) - { - // same value as different alternative - variant<int, int> v0(in_place_index<0>, 42); - variant<int, int> v1(in_place_index<1>, 42); - std::hash<variant<int, int>> hash; - EXPECT_NE(hash(v0), hash(v1)); - } -#endif // !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) - - { - std::hash<variant<int>> hash; - std::set<size_t> hashcodes; - for (int i = 0; i < 100; ++i) { - hashcodes.insert(hash(i)); - } - EXPECT_GT(hashcodes.size(), 90u); - - // test const-qualified - static_assert(type_traits_internal::IsHashable<variant<const int>>::value, - ""); - static_assert( - type_traits_internal::IsHashable<variant<const Hashable>>::value, ""); - std::hash<absl::variant<const int>> c_hash; - for (int i = 0; i < 100; ++i) { - EXPECT_EQ(hash(i), c_hash(i)); - } - } -} - -//////////////////////////////////////// -// Miscellaneous and deprecated tests // -//////////////////////////////////////// - -// Test that a set requiring a basic type conversion works correctly -#if !defined(ABSL_USES_STD_VARIANT) -TEST(VariantTest, TestConvertingSet) { - typedef variant<double> Variant; - Variant v(1.0); - const int two = 2; - v = two; - EXPECT_TRUE(absl::holds_alternative<double>(v)); - ASSERT_TRUE(nullptr != absl::get_if<double>(&v)); - EXPECT_DOUBLE_EQ(2, absl::get<double>(v)); -} -#endif // ABSL_USES_STD_VARIANT - -// Test that a vector of variants behaves reasonably. -TEST(VariantTest, Container) { - typedef variant<int, float> Variant; - - // Creation of vector should work - std::vector<Variant> vec; - vec.push_back(Variant(10)); - vec.push_back(Variant(20.0f)); - - // Vector resizing should work if we supply a value for new slots - vec.resize(10, Variant(0)); -} - -// Test that a variant with a non-copyable type can be constructed and -// manipulated to some degree. -TEST(VariantTest, TestVariantWithNonCopyableType) { - typedef variant<int, NonCopyable> Variant; - const int kValue = 1; - Variant v(kValue); - ASSERT_TRUE(absl::holds_alternative<int>(v)); - EXPECT_EQ(kValue, absl::get<int>(v)); -} - -// Test that a variant with a non-copyable type can be transformed to -// the non-copyable type with a call to `emplace` for different numbers -// of arguments. We do not need to test this for each of T1 ... T8 -// because `emplace` does not overload on T1 ... to T8, so if this -// works for any one of T1 ... T8, then it works for all of them. We -// do need to test that it works with varying numbers of parameters -// though. -TEST(VariantTest, TestEmplace) { - typedef variant<int, NonCopyable> Variant; - const int kValue = 1; - Variant v(kValue); - ASSERT_TRUE(absl::holds_alternative<int>(v)); - EXPECT_EQ(kValue, absl::get<int>(v)); - - // emplace with zero arguments, then back to 'int' - v.emplace<NonCopyable>(); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v)); - EXPECT_EQ(0, absl::get<NonCopyable>(v).value); - v = kValue; - ASSERT_TRUE(absl::holds_alternative<int>(v)); - - // emplace with one argument: - v.emplace<NonCopyable>(1); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v)); - EXPECT_EQ(1, absl::get<NonCopyable>(v).value); - v = kValue; - ASSERT_TRUE(absl::holds_alternative<int>(v)); - - // emplace with two arguments: - v.emplace<NonCopyable>(1, 2); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v)); - EXPECT_EQ(3, absl::get<NonCopyable>(v).value); - v = kValue; - ASSERT_TRUE(absl::holds_alternative<int>(v)); - - // emplace with three arguments - v.emplace<NonCopyable>(1, 2, 3); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v)); - EXPECT_EQ(6, absl::get<NonCopyable>(v).value); - v = kValue; - ASSERT_TRUE(absl::holds_alternative<int>(v)); - - // emplace with four arguments - v.emplace<NonCopyable>(1, 2, 3, 4); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v)); - EXPECT_EQ(10, absl::get<NonCopyable>(v).value); - v = kValue; - ASSERT_TRUE(absl::holds_alternative<int>(v)); -} - -TEST(VariantTest, TestEmplaceDestroysCurrentValue) { - typedef variant<int, IncrementInDtor, NonCopyable> Variant; - int counter = 0; - Variant v(0); - ASSERT_TRUE(absl::holds_alternative<int>(v)); - v.emplace<IncrementInDtor>(&counter); - ASSERT_TRUE(absl::holds_alternative<IncrementInDtor>(v)); - ASSERT_EQ(0, counter); - v.emplace<NonCopyable>(); - ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v)); - EXPECT_EQ(1, counter); -} - -TEST(VariantTest, TestMoveSemantics) { - typedef variant<std::unique_ptr<int>, std::unique_ptr<std::string>> Variant; - - // Construct a variant by moving from an element value. - Variant v(absl::WrapUnique(new int(10))); - EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v)); - - // Construct a variant by moving from another variant. - Variant v2(std::move(v)); - ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v2)); - ASSERT_NE(nullptr, absl::get<std::unique_ptr<int>>(v2)); - EXPECT_EQ(10, *absl::get<std::unique_ptr<int>>(v2)); - - // Moving from a variant object leaves it holding moved-from value of the - // same element type. - EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v)); - ASSERT_NE(nullptr, absl::get_if<std::unique_ptr<int>>(&v)); - EXPECT_EQ(nullptr, absl::get<std::unique_ptr<int>>(v)); - - // Assign a variant from an element value by move. - v = absl::make_unique<std::string>("foo"); - ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v)); - EXPECT_EQ("foo", *absl::get<std::unique_ptr<std::string>>(v)); - - // Move-assign a variant. - v2 = std::move(v); - ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v2)); - EXPECT_EQ("foo", *absl::get<std::unique_ptr<std::string>>(v2)); - EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v)); -} - -variant<int, std::string> PassThrough(const variant<int, std::string>& arg) { - return arg; -} - -TEST(VariantTest, TestImplicitConversion) { - EXPECT_TRUE(absl::holds_alternative<int>(PassThrough(0))); - - // We still need the explicit cast for std::string, because C++ won't apply - // two user-defined implicit conversions in a row. - EXPECT_TRUE( - absl::holds_alternative<std::string>(PassThrough(std::string("foo")))); -} - struct Convertible2; struct Convertible1 { Convertible1() {} @@ -2281,116 +48,41 @@ }; TEST(VariantTest, TestRvalueConversion) { -#if !defined(ABSL_USES_STD_VARIANT) - variant<double, std::string> var( - ConvertVariantTo<variant<double, std::string>>( - variant<std::string, int>(0))); - ASSERT_TRUE(absl::holds_alternative<double>(var)); - EXPECT_EQ(0.0, absl::get<double>(var)); + std::variant<Convertible1, Convertible2> v( + ConvertVariantTo<std::variant<Convertible1, Convertible2>>( + (std::variant<Convertible2, Convertible1>(Convertible1())))); + ASSERT_TRUE(absl::holds_alternative<Convertible1>(v)); - var = ConvertVariantTo<variant<double, std::string>>( - variant<const char*, float>("foo")); - ASSERT_TRUE(absl::holds_alternative<std::string>(var)); - EXPECT_EQ("foo", absl::get<std::string>(var)); - - variant<double> singleton( - ConvertVariantTo<variant<double>>(variant<int, float>(42))); - ASSERT_TRUE(absl::holds_alternative<double>(singleton)); - EXPECT_EQ(42.0, absl::get<double>(singleton)); - - singleton = ConvertVariantTo<variant<double>>(variant<int, float>(3.14f)); - ASSERT_TRUE(absl::holds_alternative<double>(singleton)); - EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton))); - - singleton = ConvertVariantTo<variant<double>>(variant<int>(0)); - ASSERT_TRUE(absl::holds_alternative<double>(singleton)); - EXPECT_EQ(0.0, absl::get<double>(singleton)); - - variant<int32_t, uint32_t> variant2( - ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42))); - ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2)); - EXPECT_EQ(42, absl::get<int32_t>(variant2)); - - variant2 = - ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42u)); - ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2)); - EXPECT_EQ(42u, absl::get<uint32_t>(variant2)); -#endif // !ABSL_USES_STD_VARIANT - - variant<Convertible1, Convertible2> variant3( - ConvertVariantTo<variant<Convertible1, Convertible2>>( - (variant<Convertible2, Convertible1>(Convertible1())))); - ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3)); - - variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>( - variant<Convertible2, Convertible1>(Convertible2())); - ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3)); + v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>( + std::variant<Convertible2, Convertible1>(Convertible2())); + ASSERT_TRUE(absl::holds_alternative<Convertible2>(v)); } TEST(VariantTest, TestLvalueConversion) { -#if !defined(ABSL_USES_STD_VARIANT) - variant<std::string, int> source1 = 0; - variant<double, std::string> destination( - ConvertVariantTo<variant<double, std::string>>(source1)); - ASSERT_TRUE(absl::holds_alternative<double>(destination)); - EXPECT_EQ(0.0, absl::get<double>(destination)); + std::variant<Convertible2, Convertible1> source((Convertible1())); + std::variant<Convertible1, Convertible2> v( + ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source)); + ASSERT_TRUE(absl::holds_alternative<Convertible1>(v)); - variant<const char*, float> source2 = "foo"; - destination = ConvertVariantTo<variant<double, std::string>>(source2); - ASSERT_TRUE(absl::holds_alternative<std::string>(destination)); - EXPECT_EQ("foo", absl::get<std::string>(destination)); - - variant<int, float> source3(42); - variant<double> singleton(ConvertVariantTo<variant<double>>(source3)); - ASSERT_TRUE(absl::holds_alternative<double>(singleton)); - EXPECT_EQ(42.0, absl::get<double>(singleton)); - - source3 = 3.14f; - singleton = ConvertVariantTo<variant<double>>(source3); - ASSERT_TRUE(absl::holds_alternative<double>(singleton)); - EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton))); - - variant<int> source4(0); - singleton = ConvertVariantTo<variant<double>>(source4); - ASSERT_TRUE(absl::holds_alternative<double>(singleton)); - EXPECT_EQ(0.0, absl::get<double>(singleton)); - - variant<int32_t> source5(42); - variant<int32_t, uint32_t> variant2( - ConvertVariantTo<variant<int32_t, uint32_t>>(source5)); - ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2)); - EXPECT_EQ(42, absl::get<int32_t>(variant2)); - - variant<uint32_t> source6(42u); - variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6); - ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2)); - EXPECT_EQ(42u, absl::get<uint32_t>(variant2)); -#endif - - variant<Convertible2, Convertible1> source7((Convertible1())); - variant<Convertible1, Convertible2> variant3( - ConvertVariantTo<variant<Convertible1, Convertible2>>(source7)); - ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3)); - - source7 = Convertible2(); - variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(source7); - ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3)); + source = Convertible2(); + v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source); + ASSERT_TRUE(absl::holds_alternative<Convertible2>(v)); } TEST(VariantTest, TestMoveConversion) { - using Variant = - variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>; + using Variant = std::variant<std::unique_ptr<const int>, + std::unique_ptr<const std::string>>; using OtherVariant = - variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; + std::variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; Variant var( - ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(0)})); + ConvertVariantTo<Variant>(OtherVariant{std::make_unique<int>(0)})); ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const int>>(var)); ASSERT_NE(absl::get<std::unique_ptr<const int>>(var), nullptr); EXPECT_EQ(0, *absl::get<std::unique_ptr<const int>>(var)); var = ConvertVariantTo<Variant>( - OtherVariant(absl::make_unique<std::string>("foo"))); + OtherVariant(std::make_unique<std::string>("foo"))); ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const std::string>>(var)); EXPECT_EQ("foo", *absl::get<std::unique_ptr<const std::string>>(var)); } @@ -2399,10 +91,10 @@ // We use shared_ptr here because it's both copyable and movable, and // a moved-from shared_ptr is guaranteed to be null, so we can detect // whether moving or copying has occurred. - using Variant = - variant<std::shared_ptr<const int>, std::shared_ptr<const std::string>>; + using Variant = std::variant<std::shared_ptr<const int>, + std::shared_ptr<const std::string>>; using OtherVariant = - variant<std::shared_ptr<int>, std::shared_ptr<std::string>>; + std::variant<std::shared_ptr<int>, std::shared_ptr<std::string>>; Variant v1(std::make_shared<const int>(0)); @@ -2430,289 +122,43 @@ } TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { -#if !defined(ABSL_USES_STD_VARIANT) - variant<double, std::string> var( - ConvertVariantTo<variant<double, std::string>>( - variant<std::string, int>(3))); - EXPECT_THAT(absl::get_if<double>(&var), Pointee(3.0)); + variant<Convertible1, Convertible2> v( + ConvertVariantTo<std::variant<Convertible1, Convertible2>>( + (std::variant<Convertible2, Convertible1>(Convertible1())))); + ASSERT_TRUE(absl::holds_alternative<Convertible1>(v)); - var = ConvertVariantTo<variant<double, std::string>>( - variant<const char*, float>("foo")); - EXPECT_THAT(absl::get_if<std::string>(&var), Pointee(std::string("foo"))); - - variant<double> singleton( - ConvertVariantTo<variant<double>>(variant<int, float>(42))); - EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(42.0)); - - singleton = ConvertVariantTo<variant<double>>(variant<int, float>(3.14f)); - EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(DoubleEq(3.14f))); - - singleton = ConvertVariantTo<variant<double>>(variant<int>(3)); - EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(3.0)); - - variant<int32_t, uint32_t> variant2( - ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42))); - EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42)); - - variant2 = - ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42u)); - EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42u)); -#endif - - variant<Convertible1, Convertible2> variant3( - ConvertVariantTo<variant<Convertible1, Convertible2>>( - (variant<Convertible2, Convertible1>(Convertible1())))); - ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3)); - - variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>( - variant<Convertible2, Convertible1>(Convertible2())); - ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3)); + v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>( + std::variant<Convertible2, Convertible1>(Convertible2())); + ASSERT_TRUE(absl::holds_alternative<Convertible2>(v)); } TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { -#if !defined(ABSL_USES_STD_VARIANT) - variant<std::string, int> source1 = 3; - variant<double, std::string> destination( - ConvertVariantTo<variant<double, std::string>>(source1)); - EXPECT_THAT(absl::get_if<double>(&destination), Pointee(3.0)); + variant<Convertible2, Convertible1> source((Convertible1())); + variant<Convertible1, Convertible2> v( + ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source)); + ASSERT_TRUE(absl::holds_alternative<Convertible1>(v)); - variant<const char*, float> source2 = "foo"; - destination = ConvertVariantTo<variant<double, std::string>>(source2); - EXPECT_THAT(absl::get_if<std::string>(&destination), - Pointee(std::string("foo"))); - - variant<int, float> source3(42); - variant<double> singleton(ConvertVariantTo<variant<double>>(source3)); - EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(42.0)); - - source3 = 3.14f; - singleton = ConvertVariantTo<variant<double>>(source3); - EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton))); - EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(DoubleEq(3.14f))); - - variant<int> source4(3); - singleton = ConvertVariantTo<variant<double>>(source4); - EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(3.0)); - - variant<int32_t> source5(42); - variant<int32_t, uint32_t> variant2( - ConvertVariantTo<variant<int32_t, uint32_t>>(source5)); - EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42)); - - variant<uint32_t> source6(42u); - variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6); - EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42u)); -#endif // !ABSL_USES_STD_VARIANT - - variant<Convertible2, Convertible1> source7((Convertible1())); - variant<Convertible1, Convertible2> variant3( - ConvertVariantTo<variant<Convertible1, Convertible2>>(source7)); - ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3)); - - source7 = Convertible2(); - variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(source7); - ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3)); + source = Convertible2(); + v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source); + ASSERT_TRUE(absl::holds_alternative<Convertible2>(v)); } TEST(VariantTest, TestMoveConversionViaConvertVariantTo) { - using Variant = - variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>; + using Variant = std::variant<std::unique_ptr<const int>, + std::unique_ptr<const std::string>>; using OtherVariant = - variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; + std::variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; Variant var( - ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(3)})); + ConvertVariantTo<Variant>(OtherVariant{std::make_unique<int>(3)})); EXPECT_THAT(absl::get_if<std::unique_ptr<const int>>(&var), Pointee(Pointee(3))); var = ConvertVariantTo<Variant>( - OtherVariant(absl::make_unique<std::string>("foo"))); + OtherVariant(std::make_unique<std::string>("foo"))); EXPECT_THAT(absl::get_if<std::unique_ptr<const std::string>>(&var), Pointee(Pointee(std::string("foo")))); } -// If all alternatives are trivially copy/move constructible, variant should -// also be trivially copy/move constructible. This is not required by the -// standard and we know that libstdc++ variant doesn't have this feature. -// For more details see the paper: -// http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) -#define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1 -#endif - -TEST(VariantTest, TestCopyAndMoveTypeTraits) { - EXPECT_TRUE(std::is_copy_constructible<variant<std::string>>::value); - EXPECT_TRUE(absl::is_copy_assignable<variant<std::string>>::value); - EXPECT_TRUE(std::is_move_constructible<variant<std::string>>::value); - EXPECT_TRUE(absl::is_move_assignable<variant<std::string>>::value); - EXPECT_TRUE(std::is_move_constructible<variant<std::unique_ptr<int>>>::value); - EXPECT_TRUE(absl::is_move_assignable<variant<std::unique_ptr<int>>>::value); - EXPECT_FALSE( - std::is_copy_constructible<variant<std::unique_ptr<int>>>::value); - EXPECT_FALSE(absl::is_copy_assignable<variant<std::unique_ptr<int>>>::value); - - EXPECT_FALSE( - absl::is_trivially_copy_constructible<variant<std::string>>::value); - EXPECT_FALSE(absl::is_trivially_copy_assignable<variant<std::string>>::value); -#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY - EXPECT_TRUE(absl::is_trivially_copy_constructible<variant<int>>::value); - EXPECT_TRUE(absl::is_trivially_copy_assignable<variant<int>>::value); - EXPECT_TRUE(is_trivially_move_constructible<variant<int>>::value); - EXPECT_TRUE(is_trivially_move_assignable<variant<int>>::value); -#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY -} - -TEST(VariantTest, TestVectorOfMoveonlyVariant) { - // Verify that variant<MoveonlyType> works correctly as a std::vector element. - std::vector<variant<std::unique_ptr<int>, std::string>> vec; - vec.push_back(absl::make_unique<int>(42)); - vec.emplace_back("Hello"); - vec.reserve(3); - auto another_vec = std::move(vec); - // As a sanity check, verify vector contents. - ASSERT_EQ(2u, another_vec.size()); - EXPECT_EQ(42, *absl::get<std::unique_ptr<int>>(another_vec[0])); - EXPECT_EQ("Hello", absl::get<std::string>(another_vec[1])); -} - -TEST(VariantTest, NestedVariant) { -#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY - static_assert(absl::is_trivially_copy_constructible<variant<int>>(), ""); - static_assert(absl::is_trivially_copy_assignable<variant<int>>(), ""); - static_assert(is_trivially_move_constructible<variant<int>>(), ""); - static_assert(is_trivially_move_assignable<variant<int>>(), ""); - - static_assert(absl::is_trivially_copy_constructible<variant<variant<int>>>(), - ""); - static_assert(absl::is_trivially_copy_assignable<variant<variant<int>>>(), - ""); - static_assert(is_trivially_move_constructible<variant<variant<int>>>(), ""); - static_assert(is_trivially_move_assignable<variant<variant<int>>>(), ""); -#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY - - variant<int> x(42); - variant<variant<int>> y(x); - variant<variant<int>> z(y); - EXPECT_TRUE(absl::holds_alternative<variant<int>>(z)); - EXPECT_EQ(x, absl::get<variant<int>>(z)); -} - -struct TriviallyDestructible { - TriviallyDestructible(TriviallyDestructible&&) {} - TriviallyDestructible(const TriviallyDestructible&) {} - TriviallyDestructible& operator=(TriviallyDestructible&&) { return *this; } - TriviallyDestructible& operator=(const TriviallyDestructible&) { - return *this; - } -}; - -struct TriviallyMovable { - TriviallyMovable(TriviallyMovable&&) = default; - TriviallyMovable(TriviallyMovable const&) {} - TriviallyMovable& operator=(const TriviallyMovable&) { return *this; } -}; - -struct TriviallyCopyable { - TriviallyCopyable(const TriviallyCopyable&) = default; - TriviallyCopyable& operator=(const TriviallyCopyable&) { return *this; } -}; - -struct TriviallyMoveAssignable { - TriviallyMoveAssignable(TriviallyMoveAssignable&&) = default; - TriviallyMoveAssignable(const TriviallyMoveAssignable&) {} - TriviallyMoveAssignable& operator=(TriviallyMoveAssignable&&) = default; - TriviallyMoveAssignable& operator=(const TriviallyMoveAssignable&) { - return *this; - } -}; - -struct TriviallyCopyAssignable {}; - -#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY -TEST(VariantTest, TestTriviality) { - { - using TrivDestVar = absl::variant<TriviallyDestructible>; - - EXPECT_FALSE(is_trivially_move_constructible<TrivDestVar>::value); - EXPECT_FALSE(absl::is_trivially_copy_constructible<TrivDestVar>::value); - EXPECT_FALSE(is_trivially_move_assignable<TrivDestVar>::value); - EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivDestVar>::value); - EXPECT_TRUE(absl::is_trivially_destructible<TrivDestVar>::value); - } - - { - using TrivMoveVar = absl::variant<TriviallyMovable>; - - EXPECT_TRUE(is_trivially_move_constructible<TrivMoveVar>::value); - EXPECT_FALSE(absl::is_trivially_copy_constructible<TrivMoveVar>::value); - EXPECT_FALSE(is_trivially_move_assignable<TrivMoveVar>::value); - EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivMoveVar>::value); - EXPECT_TRUE(absl::is_trivially_destructible<TrivMoveVar>::value); - } - - { - using TrivCopyVar = absl::variant<TriviallyCopyable>; - - EXPECT_TRUE(is_trivially_move_constructible<TrivCopyVar>::value); - EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivCopyVar>::value); - EXPECT_FALSE(is_trivially_move_assignable<TrivCopyVar>::value); - EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivCopyVar>::value); - EXPECT_TRUE(absl::is_trivially_destructible<TrivCopyVar>::value); - } - - { - using TrivMoveAssignVar = absl::variant<TriviallyMoveAssignable>; - - EXPECT_TRUE(is_trivially_move_constructible<TrivMoveAssignVar>::value); - EXPECT_FALSE( - absl::is_trivially_copy_constructible<TrivMoveAssignVar>::value); - EXPECT_TRUE(is_trivially_move_assignable<TrivMoveAssignVar>::value); - EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivMoveAssignVar>::value); - EXPECT_TRUE(absl::is_trivially_destructible<TrivMoveAssignVar>::value); - } - - { - using TrivCopyAssignVar = absl::variant<TriviallyCopyAssignable>; - - EXPECT_TRUE(is_trivially_move_constructible<TrivCopyAssignVar>::value); - EXPECT_TRUE( - absl::is_trivially_copy_constructible<TrivCopyAssignVar>::value); - EXPECT_TRUE(is_trivially_move_assignable<TrivCopyAssignVar>::value); - EXPECT_TRUE(absl::is_trivially_copy_assignable<TrivCopyAssignVar>::value); - EXPECT_TRUE(absl::is_trivially_destructible<TrivCopyAssignVar>::value); - } -} -#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY - -// To verify that absl::variant correctly use the nontrivial move ctor of its -// member rather than use the trivial copy constructor. -TEST(VariantTest, MoveCtorBug) { - // To simulate std::tuple in libstdc++. - struct TrivialCopyNontrivialMove { - TrivialCopyNontrivialMove() = default; - TrivialCopyNontrivialMove(const TrivialCopyNontrivialMove&) = default; - TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) { called = true; } - bool called = false; - }; - { - using V = absl::variant<TrivialCopyNontrivialMove, int>; - V v1(absl::in_place_index<0>); - // this should invoke the move ctor, rather than the trivial copy ctor. - V v2(std::move(v1)); - EXPECT_TRUE(absl::get<0>(v2).called); - } - { - // this case failed to compile before our fix due to a GCC bug. - using V = absl::variant<int, TrivialCopyNontrivialMove>; - V v1(absl::in_place_index<1>); - // this should invoke the move ctor, rather than the trivial copy ctor. - V v2(std::move(v1)); - EXPECT_TRUE(absl::get<1>(v2).called); - } -} - } // namespace -ABSL_NAMESPACE_END } // namespace absl - -#endif // #if !defined(ABSL_USES_STD_VARIANT)
diff --git a/absl/utility/utility.h b/absl/utility/utility.h index c357e79..a49030e 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h
@@ -22,10 +22,6 @@ // * exchange<T> == std::exchange<T> // * make_from_tuple<T> == std::make_from_tuple<T> // -// This header file also provides the tag types `in_place_t`, `in_place_type_t`, -// and `in_place_index_t`, as well as the constant `in_place`, and -// `constexpr` `std::move()` and `std::forward()` implementations in C++11. -// // References: // // https://en.cppreference.com/w/cpp/utility/apply @@ -51,55 +47,20 @@ // the ones from std directly. using std::exchange; using std::forward; -using std::index_sequence; -using std::index_sequence_for; using std::in_place; +using std::in_place_index; +using std::in_place_index_t; using std::in_place_t; using std::in_place_type; using std::in_place_type_t; +using std::index_sequence; +using std::index_sequence_for; using std::integer_sequence; using std::make_index_sequence; using std::make_integer_sequence; using std::move; namespace utility_internal { - -template <typename T> -struct InPlaceTypeTag { - explicit InPlaceTypeTag() = delete; - InPlaceTypeTag(const InPlaceTypeTag&) = delete; - InPlaceTypeTag& operator=(const InPlaceTypeTag&) = delete; -}; - -template <size_t I> -struct InPlaceIndexTag { - explicit InPlaceIndexTag() = delete; - InPlaceIndexTag(const InPlaceIndexTag&) = delete; - InPlaceIndexTag& operator=(const InPlaceIndexTag&) = delete; -}; - -} // namespace utility_internal - -// Tag types - -#ifdef ABSL_USES_STD_VARIANT -using std::in_place_index; -using std::in_place_index_t; -#else - -// in_place_index_t -// -// Tag type used for in-place construction when the type to construct needs to -// be specified, such as with `absl::any`, designed to be a drop-in replacement -// for C++17's `std::in_place_index_t`. -template <size_t I> -using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag<I>); - -template <size_t I> -void in_place_index(utility_internal::InPlaceIndexTag<I>) {} -#endif // ABSL_USES_STD_VARIANT - -namespace utility_internal { // Helper method for expanding tuple into a called method. template <typename Functor, typename Tuple, std::size_t... Indexes> auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>) @@ -108,7 +69,6 @@ return std::invoke(absl::forward<Functor>(functor), std::get<Indexes>(absl::forward<Tuple>(t))...); } - } // namespace utility_internal // apply