From faf0a1b90374eab44e8956973b0e13febdcf3377 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 20 Apr 2018 09:16:52 -0700 Subject: - 551e205ef49682a1cb7e6e0cda46957fbcf88edd Release absl::variant. by Xiaoyi Zhang - 01c52f640594d073c6e54c47e7853b25522cf085 Update comments in absl::variant (and minor changes to ab... by Tom Manshreck - 064465e1e6b158abd8c38fd1942b4fc464b57d6a Documentation change. by Abseil Team - 58df2c8a27e80c9ea21d87c1acee8019246377c9 Relocates SetCountdown and UnsetCountdown to the exceptio... by Abseil Team - fd9d248d0948d472f2543f7fd9c0ae4a1cd60d01 Clarify thread_annotation.h documentation around local va... by Abseil Team - 0d0abaf7f0945ac5f2c5f49e78afe1b326d5aca0 Typo fix in comments. by Abseil Team - 67286d587cbd07508a81e5b8147c245a5b5538b4 Internal change. by Xiaoyi Zhang GitOrigin-RevId: 551e205ef49682a1cb7e6e0cda46957fbcf88edd Change-Id: I1a343b080187293cb5f892a309618b5712e7aa14 --- absl/base/config.h | 24 +- absl/base/exception_safety_testing_test.cc | 4 +- absl/base/internal/exception_safety_testing.h | 19 +- absl/base/thread_annotations.h | 31 +- absl/meta/type_traits.h | 19 + absl/strings/str_join.h | 2 +- absl/time/time.h | 23 +- absl/types/BUILD.bazel | 42 + absl/types/CMakeLists.txt | 15 +- absl/types/any.h | 4 +- absl/types/bad_any_cast.h | 33 +- absl/types/bad_optional_access.h | 25 +- absl/types/bad_variant_access.cc | 58 + absl/types/bad_variant_access.h | 64 + absl/types/internal/variant.h | 1387 +++++++++++++ absl/types/optional.h | 5 +- absl/types/variant.h | 838 ++++++++ absl/types/variant_test.cc | 2622 +++++++++++++++++++++++++ absl/utility/utility.h | 9 +- 19 files changed, 5172 insertions(+), 52 deletions(-) create mode 100644 absl/types/bad_variant_access.cc create mode 100644 absl/types/bad_variant_access.h create mode 100644 absl/types/internal/variant.h create mode 100644 absl/types/variant.h create mode 100644 absl/types/variant_test.cc diff --git a/absl/base/config.h b/absl/base/config.h index 500bc8c..3eb24ab 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -382,6 +382,19 @@ #endif #endif +// ABSL_HAVE_STD_VARIANT +// +// Checks whether C++17 std::optional is available. +#ifdef ABSL_HAVE_STD_VARIANT +#error "ABSL_HAVE_STD_VARIANT cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#define ABSL_HAVE_STD_VARIANT 1 +#endif +#endif + // ABSL_HAVE_STD_STRING_VIEW // // Checks whether C++17 std::string_view is available. @@ -396,17 +409,18 @@ #endif // For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than -// the support for , , . So we use _MSC_VER to check -// whether we have VS 2017 RTM (when , , is -// implemented) or higher. -// Also, `__cplusplus` is not correctly set by MSVC, so we use `_MSVC_LANG` to -// check the language version. +// the support for , , , . So we use +// _MSC_VER to check whether we have VS 2017 RTM (when , , +// , is implemented) or higher. Also, `__cplusplus` is +// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language +// version. // TODO(zhangxy): fix tests before enabling aliasing for `std::any`, // `std::string_view`. #if defined(_MSC_VER) && _MSC_VER >= 1910 && \ ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402) // #define ABSL_HAVE_STD_ANY 1 #define ABSL_HAVE_STD_OPTIONAL 1 +#define ABSL_HAVE_STD_VARIANT 1 // #define ABSL_HAVE_STD_STRING_VIEW 1 #endif diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index 041d780..94a7e4f 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -27,7 +27,9 @@ namespace absl { namespace { +using ::absl::exceptions_internal::SetCountdown; using ::absl::exceptions_internal::TestException; +using ::absl::exceptions_internal::UnsetCountdown; // EXPECT_NO_THROW can't inspect the thrown inspection in general. template @@ -54,7 +56,7 @@ TEST_F(ThrowingValueTest, Throws) { // It's not guaranteed that every operator only throws *once*. The default // ctor only throws once, though, so use it to make sure we only throw when // the countdown hits 0 - exceptions_internal::countdown = 2; + SetCountdown(2); ExpectNoThrow([]() { ThrowingValue<> bomb; }); ExpectNoThrow([]() { ThrowingValue<> bomb; }); EXPECT_THROW(ThrowingValue<> bomb, TestException); diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index 3493b5e..48a292b 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -94,6 +94,12 @@ class TestBadAllocException : public std::bad_alloc, public TestException { extern int countdown; +// Allows the countdown variable to be set manually (defaulting to the initial +// value of 0) +inline void SetCountdown(int i = 0) { countdown = i; } +// Sets the countdown to the terminal value -1 +inline void UnsetCountdown() { SetCountdown(-1); } + void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false); testing::AssertionResult FailureMessage(const TestException& e, @@ -134,7 +140,7 @@ absl::optional TestSingleInvariantAtCountdownImpl( const Invariant& invariant) { auto t_ptr = factory(); absl::optional current_res; - exceptions_internal::countdown = count; + SetCountdown(count); try { operation(t_ptr.get()); } catch (const exceptions_internal::TestException& e) { @@ -143,7 +149,7 @@ absl::optional TestSingleInvariantAtCountdownImpl( *current_res << e.what() << " failed invariant check"; } } - exceptions_internal::countdown = -1; + UnsetCountdown(); return current_res; } @@ -196,11 +202,6 @@ inline absl::optional TestAllInvariantsAtCountdown( extern exceptions_internal::NoThrowTag no_throw_ctor; extern exceptions_internal::StrongGuaranteeTagType strong_guarantee; -// These are useful for tests which just construct objects and make sure there -// are no leaks. -inline void SetCountdown() { exceptions_internal::countdown = 0; } -inline void UnsetCountdown() { exceptions_internal::countdown = -1; } - // A test class which is convertible to bool. The conversion can be // instrumented to throw at a controlled time. class ThrowingBool { @@ -731,10 +732,10 @@ struct ConstructorTracker { template T TestThrowingCtor(Args&&... args) { struct Cleanup { - ~Cleanup() { UnsetCountdown(); } + ~Cleanup() { exceptions_internal::UnsetCountdown(); } } c; for (int count = 0;; ++count) { - exceptions_internal::countdown = count; + exceptions_internal::SetCountdown(count); try { return T(std::forward(args)...); } catch (const exceptions_internal::TestException&) { diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h index 0da3bad..8d30b93 100644 --- a/absl/base/thread_annotations.h +++ b/absl/base/thread_annotations.h @@ -47,10 +47,17 @@ // mutex. GUARDED_BY() allows the user to specify a particular mutex that // should be held when accessing the annotated variable. // +// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// // Example: // -// Mutex mu; -// int p1 GUARDED_BY(mu); +// class Foo { +// Mutex mu_; +// int p1_ GUARDED_BY(mu_); +// ... +// }; #define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) // PT_GUARDED_BY() @@ -59,17 +66,20 @@ // by a mutex when dereferencing the pointer. // // Example: -// Mutex mu; -// int *p1 PT_GUARDED_BY(mu); +// class Foo { +// Mutex mu_; +// int *p1_ PT_GUARDED_BY(mu_); +// ... +// }; // // Note that a pointer variable to a shared memory location could itself be a // shared variable. // // Example: // -// // `q`, guarded by `mu1`, points to a shared memory location that is -// // guarded by `mu2`: -// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); #define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) // ACQUIRED_AFTER() / ACQUIRED_BEFORE() @@ -80,10 +90,13 @@ // (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER // and ACQUIRED_BEFORE.) // +// As with GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// // Example: // -// Mutex m1; -// Mutex m2 ACQUIRED_AFTER(m1); +// Mutex m1_; +// Mutex m2_ ACQUIRED_AFTER(m1_); #define ACQUIRED_AFTER(...) \ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index ac5d8e1..bb1b0ba 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -36,6 +36,7 @@ #define ABSL_META_TYPE_TRAITS_H_ #include +#include #include #include "absl/base/config.h" @@ -349,5 +350,23 @@ using underlying_type_t = typename std::underlying_type::type; template using result_of_t = typename std::result_of::type; +namespace type_traits_internal { +template +struct IsHashable : std::false_type {}; + +template +struct IsHashable>()(std::declval()))> + : std::true_type {}; + +template +struct IsHashEnabled + : absl::conjunction>, + std::is_copy_constructible>, + std::is_destructible>, + std::is_copy_assignable>, + IsHashable> {}; +} // namespace type_traits_internal + } // namespace absl #endif // ABSL_META_TYPE_TRAITS_H_ diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h index 4733749..bd4d0e1 100644 --- a/absl/strings/str_join.h +++ b/absl/strings/str_join.h @@ -165,7 +165,7 @@ DereferenceFormatter() { // // Example 1: // // Joins a collection of strings. This pattern also works with a collection -// // of `asbl::string_view` or even `const char*`. +// // of `absl::string_view` or even `const char*`. // std::vector v = {"foo", "bar", "baz"}; // std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("foo-bar-baz", s); diff --git a/absl/time/time.h b/absl/time/time.h index daf0b15..30c49d4 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -880,7 +880,8 @@ extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z // provided format std::string. Uses strftime()-like formatting options, with // the following extensions: // -// - %Ez - RFC3339-compatible numeric time zone (+hh:mm or -hh:mm) +// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm) +// - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss) // - %E#S - Seconds with # digits of fractional precision // - %E*S - Seconds with full fractional precision (a literal '*') // - %E#f - Fractional seconds with # digits of precision @@ -894,8 +895,8 @@ extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z // year. A year outside of [-999:9999] when formatted with %E4Y will produce // more than four characters, just like %Y. // -// We recommend that format strings include %Ez so that the result uniquely -// identifies a time instant. +// We recommend that format strings include the UTC offset (%z, %Ez, or %E*z) +// so that the result uniquely identifies a time instant. // // Example: // @@ -929,7 +930,8 @@ inline std::ostream& operator<<(std::ostream& os, Time t) { // Parses an input std::string according to the provided format std::string and // returns the corresponding `absl::Time`. Uses strftime()-like formatting // options, with the same extensions as FormatTime(), but with the -// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. +// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez +// and %E*z also accept the same inputs. // // %Y consumes as many numeric characters as it can, so the matching data // should always be terminated with a non-numeric. %E4Y always consumes @@ -940,10 +942,11 @@ inline std::ostream& operator<<(std::ostream& os, Time t) { // "1970-01-01 00:00:00.0 +0000" // // For example, parsing a std::string of "15:45" (%H:%M) will return an absl::Time -// that represents "1970-01-01 15:45:00.0 +0000". Note: Since ParseTime() -// returns time instants, it makes the most sense to parse fully-specified -// date/time strings that include a UTC offset (%z/%Ez), such as those -// matching RFC3339_full above. +// that represents "1970-01-01 15:45:00.0 +0000". +// +// Note that since ParseTime() returns time instants, it makes the most sense +// to parse fully-specified date/time strings that include a UTC offset (%z, +// %Ez, or %E*z). // // Note also that `absl::ParseTime()` only heeds the fields year, month, day, // hour, minute, (fractional) second, and UTC offset. Other fields, like @@ -974,8 +977,8 @@ bool ParseTime(const std::string& format, const std::string& input, Time* time, std::string* err); // Like ParseTime() above, but if the format std::string does not contain a UTC -// offset specification (%z/%Ez) then the input is interpreted in the given -// TimeZone. This means that the input, by itself, does not identify a +// offset specification (%z/%Ez/%E*z) then the input is interpreted in the +// given TimeZone. This means that the input, by itself, does not identify a // unique instant. Being time-zone dependent, it also admits the possibility // of ambiguity or non-existence, in which case the "pre" time (as defined // for ConvertDateTime()) is returned. For these reasons we recommend that diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index c2f0338..0bdb2f7 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -165,6 +165,17 @@ cc_library( ], ) +cc_library( + name = "bad_variant_access", + srcs = ["bad_variant_access.cc"], + hdrs = ["bad_variant_access.h"], + copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base", + "//absl/base:config", + ], +) + cc_test( name = "optional_test", size = "small", @@ -181,3 +192,34 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "variant", + srcs = ["internal/variant.h"], + hdrs = ["variant.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":bad_variant_access", + "//absl/base:base_internal", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/utility", + ], +) + +cc_test( + name = "variant_test", + size = "small", + srcs = ["variant_test.cc"], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":variant", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index fd71f38..f51d126 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt @@ -20,6 +20,7 @@ list(APPEND TYPES_PUBLIC_HEADERS "bad_optional_access.h" "optional.h" "span.h" + "variant.h" ) @@ -95,7 +96,19 @@ absl_library( bad_optional_access ) - +# variant library +absl_library( + TARGET + absl_variant + SOURCES + "bad_variant_access.h" "bad_variant_access.cc" "variant.h" "internal/variant.h" + PUBLIC_LIBRARIES + absl::base absl::meta absl::utility + PRIVATE_COMPILE_FLAGS + ${ABSL_EXCEPTIONS_FLAG} + EXPORT_NAME + variant +) # ## TESTS diff --git a/absl/types/any.h b/absl/types/any.h index 760a160..68bc288 100644 --- a/absl/types/any.h +++ b/absl/types/any.h @@ -172,7 +172,9 @@ const ValueType* any_cast(const any* operand) noexcept; template ValueType* any_cast(any* operand) noexcept; -// any +// ----------------------------------------------------------------------------- +// absl::any +// ----------------------------------------------------------------------------- // // An `absl::any` object provides the facility to either store an instance of a // type, known as the "contained object", or no value. An `absl::any` is used to diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h index 8ffbe4b..3b96307 100644 --- a/absl/types/bad_any_cast.h +++ b/absl/types/bad_any_cast.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Abseil Authors. +// 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. @@ -11,6 +11,12 @@ // 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_any_cast.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::bad_any_cast` type. #ifndef ABSL_TYPES_BAD_ANY_CAST_H_ #define ABSL_TYPES_BAD_ANY_CAST_H_ @@ -19,21 +25,28 @@ namespace absl { -//////////////////////// -// [any.bad_any_cast] // -//////////////////////// - -// Objects of type bad_any_cast are thrown by a failed any_cast. +// ----------------------------------------------------------------------------- +// bad_any_cast +// ----------------------------------------------------------------------------- +// +// An `absl::bad_any_cast` type is an exception type that is thrown when +// failing to successfully cast the return value of an `absl::any` object. +// +// Example: +// +// auto a = absl::any(65); +// absl::any_cast(a); // 65 +// try { +// absl::any_cast(a); +// } catch(const absl::bad_any_cast& e) { +// std::cout << "Bad any cast: " << e.what() << '\n'; +// } class bad_any_cast : public std::bad_cast { public: ~bad_any_cast() override; const char* what() const noexcept override; }; -////////////////////////////////////////////// -// Implementation-details beyond this point // -////////////////////////////////////////////// - namespace any_internal { [[noreturn]] void ThrowBadAnyCast(); diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h index c4c7444..e9aa8b8 100644 --- a/absl/types/bad_optional_access.h +++ b/absl/types/bad_optional_access.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Abseil Authors. +// 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. @@ -11,6 +11,12 @@ // 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_optional_access.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::bad_optional_access` type. #ifndef ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ #define ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ @@ -19,6 +25,23 @@ namespace absl { +// ----------------------------------------------------------------------------- +// bad_optional_access +// ----------------------------------------------------------------------------- +// +// An `absl::bad_optional_access` type is an exception type that is thrown when +// attempting to access an `absl::optional` object that does not contain a +// value. +// +// Example: +// +// absl::optional o; +// +// try { +// int n = o.value(); +// } catch(const absl::bad_optional_access& e) { +// std::cout << "Bad optional access: " << e.what() << '\n'; +// } class bad_optional_access : public std::exception { public: bad_optional_access() = default; diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc new file mode 100644 index 0000000..817fd78 --- /dev/null +++ b/absl/types/bad_variant_access.cc @@ -0,0 +1,58 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/types/bad_variant_access.h" + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { + +////////////////////////// +// [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 +} // namespace absl diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h new file mode 100644 index 0000000..67abe71 --- /dev/null +++ b/absl/types/bad_variant_access.h @@ -0,0 +1,64 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// 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 + +namespace absl { + +// ----------------------------------------------------------------------------- +// 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 v; +// v = 1; +// try { +// absl::get(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]] void ThrowBadVariantAccess(); +[[noreturn]] void Rethrow(); + +} // namespace variant_internal +} // namespace absl + +#endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_ diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h new file mode 100644 index 0000000..61c56dd --- /dev/null +++ b/absl/types/internal/variant.h @@ -0,0 +1,1387 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// 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_variant_internal_H_ +#define ABSL_TYPES_variant_internal_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/internal/identity.h" +#include "absl/base/internal/inline_variable.h" +#include "absl/base/internal/invoke.h" +#include "absl/base/optimization.h" +#include "absl/meta/type_traits.h" +#include "absl/types/bad_variant_access.h" +#include "absl/utility/utility.h" + +namespace absl { + +template +class variant; + +ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1); + +template +struct variant_size; + +template +struct variant_alternative; + +namespace variant_internal { + +// NOTE: See specializations below for details. +template +struct VariantAlternativeSfinae {}; + +// Requires: I < variant_size_v. +// +// Value: The Ith type of Types... +template +struct VariantAlternativeSfinae> + : VariantAlternativeSfinae> {}; + +// Value: T0 +template +struct VariantAlternativeSfinae<0, variant> { + using type = T0; +}; + +template +using VariantAlternativeSfinaeT = typename VariantAlternativeSfinae::type; + +// NOTE: Requires T to be a reference type. +template +struct GiveQualsTo; + +template +struct GiveQualsTo { + using type = U&; +}; + +template +struct GiveQualsTo { + using type = U&&; +}; + +template +struct GiveQualsTo { + using type = const U&; +}; + +template +struct GiveQualsTo { + using type = const U&&; +}; + +template +struct GiveQualsTo { + using type = volatile U&; +}; + +template +struct GiveQualsTo { + using type = volatile U&&; +}; + +template +struct GiveQualsTo { + using type = volatile const U&; +}; + +template +struct GiveQualsTo { + using type = volatile const U&&; +}; + +template +using GiveQualsToT = typename GiveQualsTo::type; + +// Convenience alias, since size_t integral_constant is used a lot in this file. +template +using SizeT = std::integral_constant; + +template +struct IndexOfConstructedType {}; + +template +struct VariantAccessResultImpl; + +template class Variantemplate, class... T> +struct VariantAccessResultImpl&> { + using type = typename absl::variant_alternative>::type&; +}; + +template class Variantemplate, class... T> +struct VariantAccessResultImpl&> { + using type = + const typename absl::variant_alternative>::type&; +}; + +template class Variantemplate, class... T> +struct VariantAccessResultImpl&&> { + using type = typename absl::variant_alternative>::type&&; +}; + +template class Variantemplate, class... T> +struct VariantAccessResultImpl&&> { + using type = + const typename absl::variant_alternative>::type&&; +}; + +template +using VariantAccessResult = + typename VariantAccessResultImpl::type; + +// NOTE: This is used instead of std::array to reduce instantiation overhead. +template +struct SimpleArray { + static_assert(Size != 0, ""); + T value[Size]; +}; + +template +struct AccessedType { + using type = T; +}; + +template +using AccessedTypeT = typename AccessedType::type; + +template +struct AccessedType> { + using type = AccessedTypeT; +}; + +template +constexpr T AccessSimpleArray(const T& value) { + return value; +} + +template +constexpr AccessedTypeT AccessSimpleArray(const SimpleArray& table, + std::size_t head_index, + SizeT... tail_indices) { + return AccessSimpleArray(table.value[head_index], tail_indices...); +} + +// Note: Intentionally is an alias. +template +using AlwaysZero = SizeT<0>; + +template +struct VisitIndicesResultImpl { + using type = absl::result_of_t...)>; +}; + +template +using VisitIndicesResultT = typename VisitIndicesResultImpl::type; + +template +struct MakeVisitationMatrix; + +template +constexpr ReturnType call_with_indices(FunctionObject&& function) { + static_assert( + std::is_same()( + SizeT()...))>::value, + "Not all visitation overloads have the same return type."); + return absl::forward(function)(SizeT()...); +} + +template +struct MakeVisitationMatrix, + BoundIndices...> { + using ResultType = ReturnType (*)(FunctionObject&&); + static constexpr ResultType Run() { + return &call_with_indices; + } +}; + +template +struct MakeVisitationMatrixImpl; + +template +struct MakeVisitationMatrixImpl< + ReturnType, FunctionObject, index_sequence, + index_sequence, BoundIndices...> { + using ResultType = SimpleArray< + typename MakeVisitationMatrix>::ResultType, + sizeof...(CurrIndices)>; + + static constexpr ResultType Run() { + return {{MakeVisitationMatrix, + BoundIndices..., CurrIndices>::Run()...}}; + } +}; + +template +struct MakeVisitationMatrix, + BoundIndices...> + : MakeVisitationMatrixImpl< + ReturnType, FunctionObject, index_sequence, + absl::make_index_sequence, BoundIndices...> {}; + +template +VisitIndicesResultT visit_indices(Op&& op, SizeT... indices) { + return AccessSimpleArray( + MakeVisitationMatrix, Op, + index_sequence<(EndIndices + 1)...>>::Run(), + (indices + 1)...)(absl::forward(op)); +} + +template +[[noreturn]] ReturnType TypedThrowBadVariantAccess() { + absl::variant_internal::ThrowBadVariantAccess(); +} + +// 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 +template +VariantAccessResult AccessUnion(Self&& self, SizeT /*i*/) { + return reinterpret_cast>(self); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +template +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 + static typename VariantType::Variant& Derived(VariantType& self) { // NOLINT + return static_cast(self); + } + + template + static const typename VariantType::Variant& Derived( + const VariantType& self) { // NOLINT + return static_cast(self); + } + + template + static void Destroy(VariantType& self) { // NOLINT + Derived(self).destroy(); + self.index_ = absl::variant_npos; + } + + template + static void SetIndex(Variant& self, std::size_t i) { // NOLINT + self.index_ = i; + } + + template + static void InitFrom(Variant& self, Variant&& other) { // NOLINT + variant_internal::visit_indices::value>( + InitFromVisitor{&self, + std::forward(other)}, + other.index()); + self.index_ = other.index(); + } + + template + static VariantAccessResult Access(Variant&& self) { + if (ABSL_PREDICT_FALSE(self.index_ != I)) { + TypedThrowBadVariantAccess>(); + } + + // 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>( + variant_internal::AccessUnion(self.state_, SizeT())); + } + + // The implementation of the move-assignment operation for a variant. + template + struct MoveAssignVisitor { + using DerivedType = typename VType::Variant; + template + void operator()(SizeT /*new_i*/) const { + if (left->index_ == NewIndex) { + Access(*left) = std::move(Access(*right)); + } else { + Derived(*left).template emplace( + std::move(Access(*right))); + } + } + + void operator()(SizeT /*new_i*/) const { + Destroy(*left); + } + + VType* left; + VType* right; + }; + + template + static MoveAssignVisitor MakeMoveAssignVisitor(VType* left, + VType* other) { + return {left, other}; + } + + // The implementation of the assignment operation for a variant. + template + struct CopyAssignVisitor { + using DerivedType = typename VType::Variant; + template + void operator()(SizeT /*new_i*/) const { + using New = + typename absl::variant_alternative::type; + + if (left->index_ == NewIndex) { + Access(*left) = Access(*right); + } else if (std::is_nothrow_copy_constructible::value || + !std::is_nothrow_move_constructible::value) { + Derived(*left).template emplace(Access(*right)); + } else { + Derived(*left) = DerivedType(Derived(*right)); + } + } + + void operator()(SizeT /*new_i*/) const { + Destroy(*left); + } + + VType* left; + const VType* right; + }; + + template + static CopyAssignVisitor MakeCopyAssignVisitor(VType* left, + const VType& other) { + return {left, &other}; + } + + // The implementation of conversion-assignment operations for variant. + template + struct ConversionAssignVisitor { + using NewIndex = + variant_internal::IndexOfConstructedType; + + void operator()(SizeT /*old_i*/ + ) const { + Access(*left) = absl::forward(other); + } + + template + void operator()(SizeT /*old_i*/ + ) const { + using New = + typename absl::variant_alternative::type; + if (std::is_nothrow_constructible::value || + !std::is_nothrow_move_constructible::value) { + left->template emplace( + absl::forward(other)); + } else { + // the standard says "equivalent to + // operator=(variant(std::forward(t)))", but we use `emplace` here + // because the variant's move assignment operator could be deleted. + left->template emplace( + New(absl::forward(other))); + } + } + + Left* left; + QualifiedNew&& other; + }; + + template + static ConversionAssignVisitor + MakeConversionAssignVisitor(Left* left, QualifiedNew&& qual) { + return {left, absl::forward(qual)}; + } + + // Backend for operations for `emplace()` which destructs `*self` then + // construct a new alternative with `Args...`. + template + static typename absl::variant_alternative::type& Replace( + Self* self, Args&&... args) { + Destroy(*self); + using New = typename absl::variant_alternative::type; + New* const result = ::new (static_cast(&self->state_)) + New(absl::forward(args)...); + self->index_ = NewIndex; + return *result; + } + + template + struct InitFromVisitor { + template + void operator()(SizeT /*new_i*/) const { + using Alternative = + typename variant_alternative::type; + ::new (static_cast(&left->state_)) Alternative( + Access(std::forward(right))); + } + + void operator()(SizeT /*new_i*/) const { + // This space intentionally left blank. + } + LeftVariant* left; + QualifiedRightVariant&& right; + }; +}; + +template +struct IndexOfImpl; + +template +struct IndexOfImpl { + using IndexFromEnd = SizeT<0>; + using MatchedIndexFromEnd = IndexFromEnd; + using MultipleMatches = std::false_type; +}; + +template +struct IndexOfImpl : IndexOfImpl { + using IndexFromEnd = + SizeT::IndexFromEnd::value + 1>; +}; + +template +struct IndexOfImpl + : IndexOfImpl { + using IndexFromEnd = + SizeT::IndexFromEnd::value + 1>; + using MatchedIndexFromEnd = IndexFromEnd; + using MultipleMatches = std::integral_constant< + bool, IndexOfImpl::MatchedIndexFromEnd::value != 0>; +}; + +template +struct IndexOfMeta { + using Results = IndexOfImpl; + 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; +}; + +template +using IndexOf = typename IndexOfMeta::type; + +template +struct UnambiguousIndexOfImpl; + +// Terminating case encountered once we've checked all of the alternatives +template +struct UnambiguousIndexOfImpl, T, CurrIndex> : SizeT {}; + +// Case where T is not Head +template +struct UnambiguousIndexOfImpl, T, CurrIndex> + : UnambiguousIndexOfImpl, T, CurrIndex + 1>::type {}; + +// Case where T is Head +template +struct UnambiguousIndexOfImpl, Head, CurrIndex> + : SizeT, Head, 0>::value == + sizeof...(Tail) + ? CurrIndex + : CurrIndex + sizeof...(Tail) + 1> {}; + +template +struct UnambiguousIndexOf; + +struct NoMatch { + struct type {}; +}; + +template +struct UnambiguousIndexOf, T> + : std::conditional, T, 0>::value != + sizeof...(Alts), + UnambiguousIndexOfImpl, T, 0>, + NoMatch>::type::type {}; + +template +using UnambiguousTypeOfImpl = T; + +template +using UnambiguousTypeOfT = + UnambiguousTypeOfImpl::value>; + +template +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 +struct ImaginaryFun; + +template +struct ImaginaryFun, I> { + static void Run() = delete; +}; + +template +struct ImaginaryFun, I> : ImaginaryFun, I + 1> { + using ImaginaryFun, 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 Run(const H&); + static SizeT Run(H&&); +}; + +// The following metafunctions are used in constructor and assignment +// constraints. +template +struct IsNeitherSelfNorInPlace : std::true_type {}; + +template +struct IsNeitherSelfNorInPlace : std::false_type {}; + +template +struct IsNeitherSelfNorInPlace> : std::false_type {}; + +template +struct IsNeitherSelfNorInPlace> : std::false_type {}; + +template +struct ConversionIsPossibleImpl : std::false_type {}; + +template +struct ConversionIsPossibleImpl< + Variant, T, void_t::Run(std::declval()))>> + : std::true_type {}; + +template +struct ConversionIsPossible : ConversionIsPossibleImpl::type {}; + +template +struct IndexOfConstructedType< + Variant, T, void_t::Run(std::declval()))>> + : decltype(ImaginaryFun::Run(std::declval())) {}; + +template +struct ContainsVariantNPos + : absl::negation, + absl::integer_sequence>> {}; + +template +using RawVisitResult = + absl::result_of_t...)>; + +// 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 +struct VisitResultImpl { + using type = + absl::result_of_t...)>; +}; + +// Done in two steps intentionally so that we don't cause substitution to fail. +template +using VisitResult = typename VisitResultImpl::type; + +template +struct PerformVisitation { + using ReturnType = VisitResult; + + template + constexpr ReturnType operator()(SizeT... indices) const { + return Run(typename ContainsVariantNPos::type{}, + absl::index_sequence_for(), indices...); + } + + template + constexpr ReturnType Run(std::false_type /*has_valueless*/, + index_sequence, SizeT...) const { + return absl::base_internal::Invoke( + absl::forward(op), + VariantCoreAccess::Access( + absl::forward(std::get(variant_tup)))...); + } + + template + [[noreturn]] ReturnType Run(std::true_type /*has_valueless*/, + index_sequence, SizeT...) 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 variant_tup; + Op&& op; +}; + +template +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 +struct EmplaceTag {}; + +template <> +union Union<> { + constexpr explicit Union(NoopConstructorTag) noexcept {} +}; + +// Suppress bogus warning on MSVC: MSVC complains that Union 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 +union Union { + using TailUnion = Union; + + explicit constexpr Union(NoopConstructorTag /*tag*/) noexcept + : tail(NoopConstructorTag()) {} + + template + explicit constexpr Union(EmplaceTag<0>, P&&... args) + : head(absl::forward

(args)...) {} + + template + explicit constexpr Union(EmplaceTag, P&&... args) + : tail(EmplaceTag{}, absl::forward

(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 +union DestructibleUnionImpl; + +template <> +union DestructibleUnionImpl<> { + constexpr explicit DestructibleUnionImpl(NoopConstructorTag) noexcept {} +}; + +template +union DestructibleUnionImpl { + using TailUnion = DestructibleUnionImpl; + + explicit constexpr DestructibleUnionImpl(NoopConstructorTag /*tag*/) noexcept + : tail(NoopConstructorTag()) {} + + template + explicit constexpr DestructibleUnionImpl(EmplaceTag<0>, P&&... args) + : head(absl::forward

(args)...) {} + + template + explicit constexpr DestructibleUnionImpl(EmplaceTag, P&&... args) + : tail(EmplaceTag{}, absl::forward

(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 +using DestructibleUnion = + absl::conditional_t>::value, Union, + DestructibleUnionImpl>; + +// Deepest base, containing the actual union and the discriminator +template +class VariantStateBase { + protected: + using Variant = variant; + + template ::value, LazyH>> + constexpr VariantStateBase() noexcept( + std::is_nothrow_default_constructible::value) + : state_(EmplaceTag<0>()), index_(0) {} + + template + explicit constexpr VariantStateBase(EmplaceTag tag, P&&... args) + : state_(tag, absl::forward

(args)...), index_(I) {} + + explicit constexpr VariantStateBase(NoopConstructorTag) + : state_(NoopConstructorTag()), index_(variant_npos) {} + + void destroy() {} // Does nothing (shadowed in child if non-trivial) + + DestructibleUnion state_; + std::size_t index_; +}; + +using absl::internal::identity; + +// OverloadSet::Overload() is a unary function which is overloaded to +// take any of the element types of the variant, by reference-to-const. +// The return type of the overload on T is identity, so that you +// can statically determine which overload was called. +// +// Overload() is not defined, so it can only be called in unevaluated +// contexts. +template +struct OverloadSet; + +template +struct OverloadSet : OverloadSet { + using Base = OverloadSet; + static identity Overload(const T&); + using Base::Overload; +}; + +template <> +struct OverloadSet<> { + // For any case not handled above. + static void Overload(...); +}; + +//////////////////////////////// +// Library Fundamentals V2 TS // +//////////////////////////////// + +// TODO(calabrese): Consider moving this to absl/meta/type_traits.h + +// The following is a rough implementation of parts of the detection idiom. +// It is used for the comparison operator checks. + +template class Op, class... Args> +struct is_detected_convertible_impl { + using type = std::false_type; +}; + +template class Op, class... Args> +struct is_detected_convertible_impl< + absl::enable_if_t, To>::value>, To, Op, + Args...> { + using type = std::true_type; +}; + +// NOTE: This differs from library fundamentals by being lazy. +template class Op, class... Args> +struct is_detected_convertible + : is_detected_convertible_impl::type {}; + +template +using LessThanResult = decltype(std::declval() < std::declval()); + +template +using GreaterThanResult = decltype(std::declval() > std::declval()); + +template +using LessThanOrEqualResult = decltype(std::declval() <= std::declval()); + +template +using GreaterThanOrEqualResult = + decltype(std::declval() >= std::declval()); + +template +using EqualResult = decltype(std::declval() == std::declval()); + +template +using NotEqualResult = decltype(std::declval() != std::declval()); + +template +using HasLessThan = is_detected_convertible; + +template +using HasGreaterThan = is_detected_convertible; + +template +using HasLessThanOrEqual = + is_detected_convertible; + +template +using HasGreaterThanOrEqual = + is_detected_convertible; + +template +using HasEqual = is_detected_convertible; + +template +using HasNotEqual = is_detected_convertible; + +template +using RequireAllHaveEqualT = + absl::enable_if_t...>::value, bool>; + +template +using RequireAllHaveNotEqualT = + absl::enable_if_t...>::value, bool>; + +template +using RequireAllHaveLessThanT = + absl::enable_if_t...>::value, bool>; + +template +using RequireAllHaveLessThanOrEqualT = + absl::enable_if_t...>::value, bool>; + +template +using RequireAllHaveGreaterThanOrEqualT = + absl::enable_if_t...>::value, bool>; + +template +using RequireAllHaveGreaterThanT = + absl::enable_if_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 +struct VariantHelper; + +template +struct VariantHelper> { + // Type metafunction which returns the element type selected if + // OverloadSet::Overload() is well-formed when called with argument type U. + template + using BestMatch = decltype( + variant_internal::OverloadSet::Overload(std::declval())); + + // 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 + struct CanAccept : + std::integral_constant>::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 + struct CanConvertFrom; + + template + struct CanConvertFrom> + : public absl::conjunction...> {}; +}; + +// 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 +struct IsTriviallyMoveConstructible: + std::is_move_constructible> {}; + +// 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 VariantStateBaseDestructorNontrivial; + +template +class VariantMoveBaseNontrivial; + +template +class VariantCopyBaseNontrivial; + +template +class VariantMoveAssignBaseNontrivial; + +template +class VariantCopyAssignBaseNontrivial; + +// Base that is dependent on whether or not the destructor can be trivial. +template +using VariantStateBaseDestructor = + absl::conditional_t>::value, + VariantStateBase, + VariantStateBaseDestructorNontrivial>; + +// 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>` 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 +using VariantMoveBase = absl::conditional_t< + absl::disjunction< + absl::negation...>>, + absl::conjunction...>>::value, + VariantStateBaseDestructor, VariantMoveBaseNontrivial>; + +// Base that is dependent on whether or not the copy-constructor can be trivial. +template +using VariantCopyBase = absl::conditional_t< + absl::disjunction< + absl::negation...>>, + std::is_copy_constructible>>::value, + VariantMoveBase, VariantCopyBaseNontrivial>; + +// Base that is dependent on whether or not the move-assign can be trivial. +template +using VariantMoveAssignBase = absl::conditional_t< + absl::disjunction>, + std::is_move_constructible>, + std::is_destructible>>, + absl::negation..., + std::is_move_assignable...>>>::value, + VariantCopyBase, VariantMoveAssignBaseNontrivial>; + +// Base that is dependent on whether or not the copy-assign can be trivial. +template +using VariantCopyAssignBase = absl::conditional_t< + absl::disjunction>, + std::is_copy_constructible>, + std::is_destructible>>, + absl::negation..., + std::is_copy_assignable...>>>::value, + VariantMoveAssignBase, VariantCopyAssignBaseNontrivial>; + +template +using VariantBase = VariantCopyAssignBase; + +template +class VariantStateBaseDestructorNontrivial : protected VariantStateBase { + private: + using Base = VariantStateBase; + + 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 + void operator()(SizeT i) const { + using Alternative = + typename absl::variant_alternative>::type; + variant_internal::AccessUnion(self->state_, i).~Alternative(); + } + + void operator()(SizeT /*i*/) const { + // This space intentionally left blank + } + + VariantStateBaseDestructorNontrivial* self; + }; + + void destroy() { + variant_internal::visit_indices(Destroyer{this}, index_); + } + + ~VariantStateBaseDestructorNontrivial() { destroy(); } + + protected: + using Base::index_; + using Base::state_; +}; + +template +class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor { + private: + using Base = VariantStateBaseDestructor; + + protected: + using Base::Base; + + struct Construct { + template + void operator()(SizeT i) const { + using Alternative = + typename absl::variant_alternative>::type; + ::new (static_cast(&self->state_)) Alternative( + variant_internal::AccessUnion(absl::move(other->state_), i)); + } + + void operator()(SizeT /*i*/) const {} + + VariantMoveBaseNontrivial* self; + VariantMoveBaseNontrivial* other; + }; + + VariantMoveBaseNontrivial() = default; + VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept( + absl::conjunction...>::value) + : Base(NoopConstructorTag()) { + variant_internal::visit_indices(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 VariantCopyBaseNontrivial : protected VariantMoveBase { + private: + using Base = VariantMoveBase; + + protected: + using Base::Base; + + VariantCopyBaseNontrivial() = default; + VariantCopyBaseNontrivial(VariantCopyBaseNontrivial&&) = default; + + struct Construct { + template + void operator()(SizeT i) const { + using Alternative = + typename absl::variant_alternative>::type; + ::new (static_cast(&self->state_)) + Alternative(variant_internal::AccessUnion(other->state_, i)); + } + + void operator()(SizeT /*i*/) const {} + + VariantCopyBaseNontrivial* self; + const VariantCopyBaseNontrivial* other; + }; + + VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other) + : Base(NoopConstructorTag()) { + variant_internal::visit_indices(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 VariantMoveAssignBaseNontrivial : protected VariantCopyBase { + friend struct VariantCoreAccess; + + private: + using Base = VariantCopyBase; + + 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_assignable...>::value) { + variant_internal::visit_indices( + VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_); + return *this; + } + + protected: + using Base::index_; + using Base::state_; +}; + +template +class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase { + friend struct VariantCoreAccess; + + private: + using Base = VariantMoveAssignBase; + + protected: + using Base::Base; + + VariantCopyAssignBaseNontrivial() = default; + VariantCopyAssignBaseNontrivial(VariantCopyAssignBaseNontrivial&&) = default; + VariantCopyAssignBaseNontrivial(const VariantCopyAssignBaseNontrivial&) = + default; + VariantCopyAssignBaseNontrivial& operator=( + VariantCopyAssignBaseNontrivial&&) = default; + + VariantCopyAssignBaseNontrivial& operator=( + const VariantCopyAssignBaseNontrivial& other) { + variant_internal::visit_indices( + VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_); + return *this; + } + + protected: + using Base::index_; + using Base::state_; +}; + +//////////////////////////////////////// +// Visitors for Comparison Operations // +//////////////////////////////////////// + +template +struct EqualsOp { + const variant* v; + const variant* w; + + constexpr bool operator()(SizeT /*v_i*/) const { + return true; + } + + template + constexpr bool operator()(SizeT /*v_i*/) const { + return VariantCoreAccess::Access(*v) == VariantCoreAccess::Access(*w); + } +}; + +template +struct NotEqualsOp { + const variant* v; + const variant* w; + + constexpr bool operator()(SizeT /*v_i*/) const { + return false; + } + + template + constexpr bool operator()(SizeT /*v_i*/) const { + return VariantCoreAccess::Access(*v) != VariantCoreAccess::Access(*w); + } +}; + +template +struct LessThanOp { + const variant* v; + const variant* w; + + constexpr bool operator()(SizeT /*v_i*/) const { + return false; + } + + template + constexpr bool operator()(SizeT /*v_i*/) const { + return VariantCoreAccess::Access(*v) < VariantCoreAccess::Access(*w); + } +}; + +template +struct GreaterThanOp { + const variant* v; + const variant* w; + + constexpr bool operator()(SizeT /*v_i*/) const { + return false; + } + + template + constexpr bool operator()(SizeT /*v_i*/) const { + return VariantCoreAccess::Access(*v) > VariantCoreAccess::Access(*w); + } +}; + +template +struct LessThanOrEqualsOp { + const variant* v; + const variant* w; + + constexpr bool operator()(SizeT /*v_i*/) const { + return true; + } + + template + constexpr bool operator()(SizeT /*v_i*/) const { + return VariantCoreAccess::Access(*v) <= VariantCoreAccess::Access(*w); + } +}; + +template +struct GreaterThanOrEqualsOp { + const variant* v; + const variant* w; + + constexpr bool operator()(SizeT /*v_i*/) const { + return true; + } + + template + constexpr bool operator()(SizeT /*v_i*/) const { + return VariantCoreAccess::Access(*v) >= VariantCoreAccess::Access(*w); + } +}; + +// Precondition: v.index() == w.index(); +template +struct SwapSameIndex { + variant* v; + variant* w; + template + void operator()(SizeT) const { + using std::swap; + swap(VariantCoreAccess::Access(*v), VariantCoreAccess::Access(*w)); + } + + void operator()(SizeT) const {} +}; + +// TODO(calabrese) do this from a different namespace for proper adl usage +template +struct Swap { + variant* v; + variant* w; + + void generic_swap() const { + variant 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 /*w_i*/) const { + if (!v->valueless_by_exception()) { + generic_swap(); + } + } + + template + void operator()(SizeT /*w_i*/) { + if (v->index() == Wi) { + visit_indices(SwapSameIndex{v, w}, Wi); + } else { + generic_swap(); + } + } +}; + +template +struct VariantHashBase { + VariantHashBase() = delete; + VariantHashBase(const VariantHashBase&) = delete; + VariantHashBase(VariantHashBase&&) = delete; + VariantHashBase& operator=(const VariantHashBase&) = delete; + VariantHashBase& operator=(VariantHashBase&&) = delete; +}; + +struct VariantHashVisitor { + template + size_t operator()(const T& t) { + return std::hash{}(t); + } +}; + +template +struct VariantHashBase...>::value>, + Ts...> { + using argument_type = Variant; + using result_type = size_t; + size_t operator()(const Variant& var) const { + if (var.valueless_by_exception()) { + return 239799884; + } + size_t result = + variant_internal::visit_indices::value>( + PerformVisitation{ + std::forward_as_tuple(var), VariantHashVisitor{}}, + var.index()); + // Combine the index and the hash result in order to distinguish + // std::variant holding the same value as different alternative. + return result ^ var.index(); + } +}; + +} // namespace variant_internal +} // namespace absl + +#endif // ABSL_TYPES_variant_internal_H_ diff --git a/absl/types/optional.h b/absl/types/optional.h index 9313fd2..581321d 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h @@ -1,4 +1,3 @@ -// // Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -94,7 +93,9 @@ using std::nullopt; namespace absl { -// optional +// ----------------------------------------------------------------------------- +// absl::optional +// ----------------------------------------------------------------------------- // // A value of type `absl::optional` holds either a value of `T` or an // "empty" value. When it holds a value of `T`, it stores it as a direct diff --git a/absl/types/variant.h b/absl/types/variant.h new file mode 100644 index 0000000..52a311e --- /dev/null +++ b/absl/types/variant.h @@ -0,0 +1,838 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// 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-constructable. +// +// 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. + +#ifndef ABSL_TYPES_VARIANT_H_ +#define ABSL_TYPES_VARIANT_H_ + +#include "absl/base/config.h" +#include "absl/utility/utility.h" + +#ifdef ABSL_HAVE_STD_VARIANT + +#include + +namespace absl { +using std::bad_variant_access; +using std::get; +using std::get_if; +using std::holds_alternative; +using std::monostate; +using std::variant; +using std::variant_alternative; +using std::variant_alternative_t; +using std::variant_npos; +using std::variant_size; +using std::variant_size_v; +using std::visit; +} // namespace absl + +#else // ABSL_HAVE_STD_VARIANT + +#include +#include +#include +#include + +#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::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 v = std::string("abc"); +// +// // A default-contructed variant will hold a value-initialized value of +// // the first alternative type. +// auto a = absl::variant(); // Holds an int of value '0'. +// +// // variants are assignable. +// +// // copy assignment +// auto v1 = absl::variant("abc"); +// auto v2 = absl::variant(10); +// v2 = v1; // copy assign +// +// // move assignment +// auto v1 = absl::variant("abc"); +// v1 = absl::variant(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 +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 +void swap(variant& v, variant& w) noexcept(noexcept(v.swap(w))) { + v.swap(w); +} + +// variant_size +// +// Returns the number of alterative 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; +// constexpr int num_types = +// absl::variant_size>(); +// +// // You can also use the member constant `value`. +// constexpr int num_types = +// absl::variant_size>::value; +// +// // `absl::variant_size` is more valuable for use in generic code: +// template +// constexpr bool IsVariantMultivalue() { +// return absl::variant_size() > 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 +struct variant_size; + +template +struct variant_size> + : std::integral_constant {}; + +// Specialization of `variant_size` for const qualified variants. +template +struct variant_size : variant_size::type {}; + +// Specialization of `variant_size` for volatile qualified variants. +template +struct variant_size : variant_size::type {}; + +// Specialization of `variant_size` for const volatile qualified variants. +template +struct variant_size : variant_size::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>::type; +// +// static_assert(std::is_same::value, ""); +// +// // `absl::variant_alternative` is more valuable for use in generic code: +// template +// constexpr bool IsFirstElementTrivial() { +// return std::is_trivial_v::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 +struct variant_alternative; + +template +struct variant_alternative> { + using type = + variant_internal::VariantAlternativeSfinaeT>; +}; + +// Specialization of `variant_alternative` for const qualified variants. +template +struct variant_alternative { + using type = const typename variant_alternative::type; +}; + +// Specialization of `variant_alternative` for volatile qualified variants. +template +struct variant_alternative { + using type = volatile typename variant_alternative::type; +}; + +// Specialization of `variant_alternative` for const volatile qualified +// variants. +template +struct variant_alternative { + using type = const volatile typename variant_alternative::type; +}; + +// Template type alias for variant_alternative::type. +// +// Example: +// +// using alternative_type_0 +// = absl::variant_alternative_t<0, absl::variant>; +// static_assert(std::is_same::value, ""); +template +using variant_alternative_t = typename variant_alternative::type; + +// holds_alternative() +// +// Checks whether the given variant currently holds a given alternative type, +// returning `true` if so. +// +// Example: +// +// absl::variant bar = 42; +// if (absl::holds_alternative(foo)) { +// std::cout << "The variant holds an integer"; +// } +template +constexpr bool holds_alternative(const variant& v) noexcept { + static_assert( + variant_internal::UnambiguousIndexOfImpl, T, + 0>::value != sizeof...(Types), + "The type T must occur exactly once in Types..."); + return v.index() == + variant_internal::UnambiguousIndexOf, 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; +// +// // Get the value by type (if unique). +// int i = absl::get(a); +// +// auto b = absl::variant; +// +// // Getting the value by a type that is not unique is ill-formed. +// int j = absl::get(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 +constexpr T& get(variant& v) { // NOLINT + return variant_internal::VariantCoreAccess::Access< + variant_internal::IndexOf::value>(v); +} + +// Overload for getting a variant's rvalue by type. +// Note: `absl::move()` is required to allow use of constexpr in C++11. +template +constexpr T&& get(variant&& v) { + return variant_internal::VariantCoreAccess::Access< + variant_internal::IndexOf::value>(absl::move(v)); +} + +// Overload for getting a variant's const lvalue by type. +template +constexpr const T& get(const variant& v) { + return variant_internal::VariantCoreAccess::Access< + variant_internal::IndexOf::value>(v); +} + +// Overload for getting a variant's const rvalue by type. +// Note: `absl::move()` is required to allow use of constexpr in C++11. +template +constexpr const T&& get(const variant&& v) { + return variant_internal::VariantCoreAccess::Access< + variant_internal::IndexOf::value>(absl::move(v)); +} + +// Overload for getting a variant's lvalue by index. +template +constexpr variant_alternative_t>& get( + variant& v) { // NOLINT + return variant_internal::VariantCoreAccess::Access(v); +} + +// Overload for getting a variant's rvalue by index. +// Note: `absl::move()` is required to allow use of constexpr in C++11. +template +constexpr variant_alternative_t>&& get( + variant&& v) { + return variant_internal::VariantCoreAccess::Access(absl::move(v)); +} + +// Overload for getting a variant's const lvalue by index. +template +constexpr const variant_alternative_t>& get( + const variant& v) { + return variant_internal::VariantCoreAccess::Access(v); +} + +// Overload for getting a variant's const rvalue by index. +// Note: `absl::move()` is required to allow use of constexpr in C++11. +template +constexpr const variant_alternative_t>&& get( + const variant&& v) { + return variant_internal::VariantCoreAccess::Access(absl::move(v)); +} + +// get_if() +// +// Returns a pointer to the value currently stored within a given variant, if +// present, using either a unique alternative type amonst 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 +constexpr absl::add_pointer_t>> +get_if(variant* v) noexcept { + return (v != nullptr && v->index() == I) ? std::addressof(absl::get(*v)) + : nullptr; +} + +// Overload for getting a pointer to the const value stored in the given +// variant by index. +template +constexpr absl::add_pointer_t>> +get_if(const variant* v) noexcept { + return (v != nullptr && v->index() == I) ? std::addressof(absl::get(*v)) + : nullptr; +} + +// Overload for getting a pointer to the value stored in the given variant by +// type. +template +constexpr absl::add_pointer_t get_if(variant* v) noexcept { + return absl::get_if::value>(v); +} + +// Overload for getting a pointer to the const value stored in the given variant +// by type. +template +constexpr absl::add_pointer_t get_if( + const variant* v) noexcept { + return absl::get_if::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). +// Requires: The expression in the Effects: element shall be a valid expression +// of the same type and value category, for all combinations of alternative +// types of all variants. Otherwise, the program is ill-formed. +// +// Example: +// +// // Define a visitor functor +// struct GetVariant { +// template +// void operator()(const T& i) const { +// std::cout << "The variant's value is: " << i; +// } +// }; +// +// // Declare our variant, and call `absl::visit()` on it. +// std::variant foo = std::string("foo"); +// GetVariant visitor; +// std::visit(visitor, foo); // Prints `The variant's value is: foo' +template +variant_internal::VisitResult visit(Visitor&& vis, + Variants&&... vars) { + return variant_internal::visit_indices< + variant_size>::value...>( + variant_internal::PerformVisitation{ + std::forward_as_tuple(absl::forward(vars)...), + absl::forward(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 +class variant : private variant_internal::VariantBase { + static_assert(absl::conjunction, std::is_object..., + absl::negation>, + absl::negation>..., + std::is_nothrow_destructible, + std::is_nothrow_destructible...>::value, + "Attempted to instantiate a variant with an unsupported type."); + + friend struct variant_internal::VariantCoreAccess; + + private: + using Base = variant_internal::VariantBase; + + 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>::value, + variant_internal::IndexOfConstructedType>::type::value, + class Tj = absl::variant_alternative_t, + absl::enable_if_t::value>* = + nullptr> + constexpr variant(T&& t) noexcept(std::is_nothrow_constructible::value) + : Base(variant_internal::EmplaceTag(), absl::forward(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 , + Args...>::value>::type* = nullptr> + constexpr explicit variant(in_place_type_t, Args&&... args) + : Base(variant_internal::EmplaceTag< + variant_internal::UnambiguousIndexOf::value>(), + absl::forward(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 , + std::initializer_list&, Args...>::value>::type* = nullptr> + constexpr explicit variant(in_place_type_t, std::initializer_list il, + Args&&... args) + : Base(variant_internal::EmplaceTag< + variant_internal::UnambiguousIndexOf::value>(), + il, absl::forward(args)...) {} + + // Constructs a variant of an alternative type from a provided index, + // through value-initialization using the provided forwarded arguments. + template , + Args...>::value>::type* = nullptr> + constexpr explicit variant(in_place_index_t, Args&&... args) + : Base(variant_internal::EmplaceTag(), absl::forward(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::initializer_list&, Args...>::value>::type* = nullptr> + constexpr explicit variant(in_place_index_t, std::initializer_list il, + Args&&... args) + : Base(variant_internal::EmplaceTag(), il, + absl::forward(args)...) {} + + // Destructors + + // Destroys the variant's currently contained value, provided that + // `absl::valueless_by_exception()` is false. + ~variant() = default; + + // Assignment Operators + + // Copy assignement 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, variant>::value, + variant_internal::IndexOfConstructedType>::type::value, + class Tj = absl::variant_alternative_t, + typename std::enable_if::value && + std::is_constructible::value>::type* = + nullptr> + variant& operator=(T&& t) noexcept( + std::is_nothrow_assignable::value&& + std::is_nothrow_constructible::value) { + variant_internal::visit_indices( + variant_internal::VariantCoreAccess::MakeConversionAssignVisitor( + this, absl::forward(t)), + index()); + + return *this; + } + + + // emplace() Functions + + // Constructs a value of the given alternative type T within the variant. + // + // Example: + // + // absl::variant, int, std::string> v; + // v.emplace(99); + // v.emplace("abc"); + template < + class T, class... Args, + typename std::enable_if::value, variant>, + Args...>::value>::type* = nullptr> + T& emplace(Args&&... args) { + return variant_internal::VariantCoreAccess::Replace< + variant_internal::UnambiguousIndexOf::value>( + this, absl::forward(args)...); + } + + // Constructs a value of the given alternative type T within the variant using + // an initializer list. + // + // Example: + // + // absl::variant, int, std::string> v; + // v.emplace>({0, 1, 2}); + template < + class T, class U, class... Args, + typename std::enable_if::value, variant>, + std::initializer_list&, Args...>::value>::type* = nullptr> + T& emplace(std::initializer_list il, Args&&... args) { + return variant_internal::VariantCoreAccess::Replace< + variant_internal::UnambiguousIndexOf::value>( + this, il, absl::forward(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, int, int> v; + // v.emplace<1>(99); + // v.emplace<2>(98); + // v.emplace(99); // Won't compile. 'int' isn't a unique type. + template , + Args...>::value>::type* = nullptr> + absl::variant_alternative_t& emplace(Args&&... args) { + return variant_internal::VariantCoreAccess::Replace( + this, absl::forward(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, int, int> v; + // v.emplace<0>({0, 1, 2}); + template , + std::initializer_list&, Args...>::value>::type* = nullptr> + absl::variant_alternative_t& emplace(std::initializer_list il, + Args&&... args) { + return variant_internal::VariantCoreAccess::Replace( + this, il, absl::forward(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. + // + // TODO(calabrese) + // `variant::swap()` and `swap()` rely on `std::is_(nothrow)_swappable()` + // which is introduced in C++17. So we assume `is_swappable()` is always + // true and `is_nothrow_swappable()` is same as `std::is_trivial()`. + void swap(variant& rhs) noexcept( + absl::conjunction, std::is_trivial...>::value) { + return variant_internal::visit_indices( + variant_internal::Swap{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 +constexpr variant_internal::RequireAllHaveEqualT operator==( + const variant& a, const variant& b) { + return (a.index() == b.index()) && + variant_internal::visit_indices( + variant_internal::EqualsOp{&a, &b}, a.index()); +} + +// Not equal operator +template +constexpr variant_internal::RequireAllHaveNotEqualT operator!=( + const variant& a, const variant& b) { + return (a.index() != b.index()) || + variant_internal::visit_indices( + variant_internal::NotEqualsOp{&a, &b}, a.index()); +} + +// Less-than operator +template +constexpr variant_internal::RequireAllHaveLessThanT operator<( + const variant& a, const variant& b) { + return (a.index() != b.index()) + ? (a.index() + 1) < (b.index() + 1) + : variant_internal::visit_indices( + variant_internal::LessThanOp{&a, &b}, a.index()); +} + +// Greater-than operator +template +constexpr variant_internal::RequireAllHaveGreaterThanT operator>( + const variant& a, const variant& b) { + return (a.index() != b.index()) + ? (a.index() + 1) > (b.index() + 1) + : variant_internal::visit_indices( + variant_internal::GreaterThanOp{&a, &b}, + a.index()); +} + +// Less-than or equal-to operator +template +constexpr variant_internal::RequireAllHaveLessThanOrEqualT operator<=( + const variant& a, const variant& b) { + return (a.index() != b.index()) + ? (a.index() + 1) < (b.index() + 1) + : variant_internal::visit_indices( + variant_internal::LessThanOrEqualsOp{&a, &b}, + a.index()); +} + +// Greater-than or equal-to operator +template +constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT +operator>=(const variant& a, const variant& b) { + return (a.index() != b.index()) + ? (a.index() + 1) > (b.index() + 1) + : variant_internal::visit_indices( + variant_internal::GreaterThanOrEqualsOp{&a, &b}, + a.index()); +} + +} // namespace absl + +namespace std { + +// hash() +template <> // NOLINT +struct hash { + std::size_t operator()(absl::monostate) const { return 0; } +}; + +template // NOLINT +struct hash> + : absl::variant_internal::VariantHashBase, void, + absl::remove_const_t...> {}; + +} // namespace std + +#endif // ABSL_HAVE_STD_VARIANT + +namespace absl { +namespace variant_internal { + +// Helper visitor for converting a variant` into another type (mostly +// variant) that can be constructed from any type. +template +struct ConversionVisitor { + template + To operator()(T&& v) const { + return To(std::forward(v)); + } +}; + +} // namespace variant_internal + +// ConvertVariantTo() +// +// Helper functions to convert an `absl::variant` to a variant of another set of +// types, provided that the alternative type of the new variant type can be +// converted from any type in the source variant. +// +// Example: +// +// absl::variant InternalReq(const Req&); +// +// // name1 and name2 are convertible to name +// absl::variant ExternalReq(const Req& req) { +// return absl::ConvertVariantTo>( +// InternalReq(req)); +// } +template +To ConvertVariantTo(Variant&& variant) { + return absl::visit(variant_internal::ConversionVisitor{}, + std::forward(variant)); +} + +} // namespace absl + +#endif // ABSL_TYPES_VARIANT_H_ diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc new file mode 100644 index 0000000..c4676c1 --- /dev/null +++ b/absl/types/variant_test.cc @@ -0,0 +1,2622 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(expr, text) + +#endif // ABSL_HAVE_EXCEPTIONS + +#define ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(...) \ + ABSL_VARIANT_TEST_EXPECT_FAIL((__VA_ARGS__), absl::bad_variant_access, \ + "Bad variant access") + +struct Hashable {}; + +namespace std { +template <> +struct hash { + size_t operator()(const Hashable&); +}; +} // namespace std + +struct NonHashable {}; + +namespace absl { +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 /*ilist*/, + int value) + : value(value) {} + MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default; + MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) = + default; + + int value = 0; +}; + +#ifdef ABSL_HAVE_EXCEPTIONS + +struct ConversionException {}; + +template +struct ExceptionOnConversion { + // Suppress MSVC 2017 warning "noreturn function has a non-void return type". +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4646) +#endif // _MSC_VER + + [[noreturn]] operator T() const { // NOLINT(runtime/explicit) + throw ConversionException(); + } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER +}; + +// Forces a variant into the valueless by exception state. +template +void ToValuelessByException(absl::variant& v) { // NOLINT + try { + v.template emplace<0>(ExceptionOnConversion()); + } 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 +struct ValueHolder { + explicit ValueHolder(const T& x) : value(x) {} + typedef T value_type; + value_type value; + static const size_t kIndex = N; +}; +template +const size_t ValueHolder::kIndex; + +// The following three functions make ValueHolder compatible with +// EXPECT_EQ and EXPECT_NE +template +inline bool operator==(const ValueHolder& left, + const ValueHolder& right) { + return left.value == right.value; +} + +template +inline bool operator!=(const ValueHolder& left, + const ValueHolder& right) { + return left.value != right.value; +} + +template +inline std::ostream& operator<<( + std::ostream& stream, const ValueHolder& object) { + return stream << object.value; +} + +// Makes a variant holding twelve uniquely typed T wrappers. +template +struct VariantFactory { + typedef variant, ValueHolder, ValueHolder, + ValueHolder> + Type; +}; + +// A typelist in 1:1 with VariantFactory, to use type driven unit tests. +typedef ::testing::Types, ValueHolder, + ValueHolder, + ValueHolder> 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 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 +class VariantTypesTest : public ::testing::Test {}; +TYPED_TEST_CASE(VariantTypesTest, VariantTypes); + +//////////////////// +// [variant.ctor] // +//////////////////// + +struct NonNoexceptDefaultConstructible { + NonNoexceptDefaultConstructible() {} + int value = 5; +}; + +struct NonDefaultConstructible { + NonDefaultConstructible() = delete; +}; + +TEST(VariantTest, TestDefaultConstructor) { + { + using X = variant; + constexpr variant x{}; + ASSERT_FALSE(x.valueless_by_exception()); + ASSERT_EQ(0, x.index()); + EXPECT_EQ(0, absl::get<0>(x)); + EXPECT_TRUE(std::is_nothrow_default_constructible::value); + } + + { + using X = variant; + X x{}; + ASSERT_FALSE(x.valueless_by_exception()); + ASSERT_EQ(0, x.index()); + EXPECT_EQ(5, absl::get<0>(x).value); + EXPECT_FALSE(std::is_nothrow_default_constructible::value); + } + + { + using X = variant; + X x{}; + ASSERT_FALSE(x.valueless_by_exception()); + ASSERT_EQ(0, x.index()); + EXPECT_EQ(0, absl::get<0>(x)); + EXPECT_TRUE(std::is_nothrow_default_constructible::value); + } + + { + using X = variant; + X x{}; + ASSERT_FALSE(x.valueless_by_exception()); + ASSERT_EQ(0, x.index()); + EXPECT_EQ(5, absl::get<0>(x).value); + EXPECT_FALSE(std::is_nothrow_default_constructible::value); + } + EXPECT_FALSE( + std::is_default_constructible>::value); + EXPECT_FALSE((std::is_default_constructible< + variant>::value)); + EXPECT_TRUE((std::is_default_constructible< + variant>::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::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(copied) || + TypeParam::kIndex != 1); + EXPECT_TRUE(absl::holds_alternative(copied) || + TypeParam::kIndex != 2); + EXPECT_TRUE(absl::holds_alternative(copied) || + TypeParam::kIndex != 3); + EXPECT_TRUE(absl::holds_alternative(copied) || + TypeParam::kIndex != 4); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 1); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 2); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 3); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 4); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 1); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 2); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 3); + EXPECT_TRUE((absl::get_if(&original) == + absl::get_if(&copied)) || + TypeParam::kIndex == 4); + const TypeParam* ovalptr = absl::get_if(&original); + const TypeParam* cvalptr = absl::get_if(&copied); + ASSERT_TRUE(ovalptr != nullptr); + ASSERT_TRUE(cvalptr != nullptr); + EXPECT_EQ(*ovalptr, *cvalptr); + TypeParam* mutable_ovalptr = absl::get_if(&original); + TypeParam* mutable_cvalptr = absl::get_if(&copied); + ASSERT_TRUE(mutable_ovalptr != nullptr); + ASSERT_TRUE(mutable_cvalptr != nullptr); + EXPECT_EQ(*mutable_ovalptr, *mutable_cvalptr); +} + +template +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, MoveOnly>; + + V v(in_place_index_t<1>{}, 10); + V v2 = absl::move(v); + EXPECT_EQ(10, absl::get<1>(v2).value); +} + +// Used internally to emulate missing triviality traits for tests. +template +union SingleUnion { + T member; +}; + +// NOTE: These don't work with types that can't be union members. +// They are just for testing. +template +struct is_trivially_move_constructible + : std::is_move_constructible>::type {}; + +template +struct is_trivially_move_assignable + : std::is_move_assignable>::type {}; + +TEST(VariantTest, NothrowMoveConstructible) { + // Verify that variant is nothrow move constructible iff its template + // arguments are. + using U = std::unique_ptr; + struct E { + E(E&&) {} + }; + static_assert(std::is_nothrow_move_constructible>::value, ""); + static_assert(std::is_nothrow_move_constructible>::value, ""); + static_assert(!std::is_nothrow_move_constructible>::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::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(v) || + TypeParam::kIndex != 1); + EXPECT_TRUE(absl::holds_alternative(v) || + TypeParam::kIndex != 2); + EXPECT_TRUE(absl::holds_alternative(v) || + TypeParam::kIndex != 3); + EXPECT_TRUE(absl::holds_alternative(v) || + TypeParam::kIndex != 4); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 1); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 2); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 3); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 4); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 1); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 2); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 3); + EXPECT_TRUE(nullptr != absl::get_if(&v) || + TypeParam::kIndex != 4); + const TypeParam* valptr = absl::get_if(&v); + ASSERT_TRUE(nullptr != valptr); + EXPECT_EQ(value.value, valptr->value); + const TypeParam* mutable_valptr = absl::get_if(&v); + ASSERT_TRUE(nullptr != mutable_valptr); + EXPECT_EQ(value.value, mutable_valptr->value); +} + +TEST(VariantTest, InPlaceType) { + using Var = variant>; + + Var v1(in_place_type_t(), 7); + ASSERT_TRUE(absl::holds_alternative(v1)); + EXPECT_EQ(7, absl::get(v1)); + + Var v2(in_place_type_t(), "ABC"); + ASSERT_TRUE(absl::holds_alternative(v2)); + EXPECT_EQ("ABC", absl::get(v2)); + + Var v3(in_place_type_t(), "ABC", 2); + ASSERT_TRUE(absl::holds_alternative(v3)); + EXPECT_EQ("AB", absl::get(v3)); + + Var v4(in_place_type_t{}); + ASSERT_TRUE(absl::holds_alternative(v4)); + + Var v5(in_place_type_t>(), {1, 2, 3}); + ASSERT_TRUE(absl::holds_alternative>(v5)); + EXPECT_THAT(absl::get>(v5), ::testing::ElementsAre(1, 2, 3)); +} + +TEST(VariantTest, InPlaceTypeInitializerList) { + using Var = variant; + + Var v1(in_place_type_t(), {1, 2, 3, 4, 5}, 6); + ASSERT_TRUE(absl::holds_alternative(v1)); + EXPECT_EQ(6, absl::get(v1).value); +} + +TEST(VariantTest, InPlaceIndex) { + using Var = variant>; + + Var v1(in_place_index_t<0>(), 7); + ASSERT_TRUE(absl::holds_alternative(v1)); + EXPECT_EQ(7, absl::get(v1)); + + Var v2(in_place_index_t<1>(), "ABC"); + ASSERT_TRUE(absl::holds_alternative(v2)); + EXPECT_EQ("ABC", absl::get(v2)); + + Var v3(in_place_index_t<1>(), "ABC", 2); + ASSERT_TRUE(absl::holds_alternative(v3)); + EXPECT_EQ("AB", absl::get(v3)); + + Var v4(in_place_index_t<2>{}); + EXPECT_TRUE(absl::holds_alternative(v4)); + + // Verify that a variant with only non-copyables can still be constructed. + EXPECT_TRUE(absl::holds_alternative( + variant(in_place_index_t<0>{}))); + + Var v5(in_place_index_t<3>(), {1, 2, 3}); + ASSERT_TRUE(absl::holds_alternative>(v5)); + EXPECT_THAT(absl::get>(v5), ::testing::ElementsAre(1, 2, 3)); +} + +TEST(VariantTest, InPlaceIndexInitializerList) { + using Var = variant; + + Var v1(in_place_index_t<3>(), {1, 2, 3, 4, 5}, 6); + ASSERT_TRUE(absl::holds_alternative(v1)); + EXPECT_EQ(6, absl::get(v1).value); +} + +//////////////////// +// [variant.dtor] // +//////////////////// + +// Make sure that the destructor destroys the contained value +TEST(VariantTest, TestDtor) { + typedef VariantFactory::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 + +// Test destruction when in the valueless_by_exception state. +TEST(VariantTest, TestDtorValuelessByException) { + int counter = 0; + IncrementInDtor counter_adjuster(&counter); + + { + using Variant = VariantFactory::Type; + + Variant v(in_place_index_t<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::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 std::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 so = long_str; + ASSERT_EQ(1, so.index()); + EXPECT_EQ(long_str, absl::get<1>(so)); + so = *&so; + + ASSERT_EQ(1, 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::Type Variant; + const TypeParam value(TypeParam::kIndex); + const Variant source(value); + Variant target(TypeParam(value.value + 1)); + ASSERT_TRUE(absl::holds_alternative(source)); + ASSERT_TRUE(absl::holds_alternative(target)); + ASSERT_NE(absl::get(source), absl::get(target)); + target = source; + ASSERT_TRUE(absl::holds_alternative(source)); + ASSERT_TRUE(absl::holds_alternative(target)); + EXPECT_EQ(absl::get(source), absl::get(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::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(source)); + Variant target(value_type1(1)); + ASSERT_TRUE(absl::holds_alternative(target)); + target = source; + EXPECT_TRUE(absl::holds_alternative(source)); + EXPECT_TRUE(absl::holds_alternative(target)); + EXPECT_EQ(absl::get(source), absl::get(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::Type Variant; + using value_type1 = absl::variant_alternative_t<0, Variant>; + const Variant source(value_type1(1)); + ASSERT_TRUE(absl::holds_alternative(source)); + const TypeParam value(TypeParam::kIndex); + Variant target(value); + ASSERT_TRUE(absl::holds_alternative(target)); + target = source; + EXPECT_TRUE(absl::holds_alternative(target)); + EXPECT_TRUE(absl::holds_alternative(source)); + EXPECT_EQ(absl::get(source), absl::get(target)); +} + +// Test that operator= 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::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 counter_adjustor[kSize]; + for (int i = 0; i != kSize; i++) { + counter[i] = 0; + counter_adjustor[i] = absl::make_unique(&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::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 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_HAVE_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; + + Variant v(absl::in_place_index_t<0>{}, 0); + + { + char& emplace_result = v.emplace(); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(absl::get(v), 0); + EXPECT_EQ(&emplace_result, &absl::get(v)); + } + + // Make sure that another emplace does zero-initialization + absl::get(v) = 'a'; + v.emplace('b'); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(absl::get(v), 'b'); + + { + int& emplace_result = v.emplace(); + EXPECT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(absl::get(v), 0); + EXPECT_EQ(&emplace_result, &absl::get(v)); + } +} + +TEST(VariantTest, TestEmplaceInitializerList) { + using Var = variant; + + Var v1(absl::in_place_index_t<0>{}, 555); + MoveOnlyWithListConstructor& emplace_result = + v1.emplace({1, 2, 3, 4, 5}, 6); + ASSERT_TRUE(absl::holds_alternative(v1)); + EXPECT_EQ(6, absl::get(v1).value); + EXPECT_EQ(&emplace_result, &absl::get(v1)); +} + +TEST(VariantTest, TestEmplaceIndex) { + using Variant = variant; + + Variant v(absl::in_place_index_t<0>{}, 555); + + { + char& emplace_result = v.emplace<1>(); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(absl::get(v), 0); + EXPECT_EQ(&emplace_result, &absl::get(v)); + } + + // Make sure that another emplace does zero-initialization + absl::get(v) = 'a'; + v.emplace<1>('b'); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(absl::get(v), 'b'); + + { + int& emplace_result = v.emplace<0>(); + EXPECT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(absl::get(v), 0); + EXPECT_EQ(&emplace_result, &absl::get(v)); + } +} + +TEST(VariantTest, TestEmplaceIndexInitializerList) { + using Var = variant; + + Var v1(absl::in_place_index_t<0>{}, 555); + MoveOnlyWithListConstructor& emplace_result = + v1.emplace<3>({1, 2, 3, 4, 5}, 6); + ASSERT_TRUE(absl::holds_alternative(v1)); + EXPECT_EQ(6, absl::get(v1).value); + EXPECT_EQ(&emplace_result, &absl::get(v1)); +} + +////////////////////// +// [variant.status] // +////////////////////// + +TEST(VariantTest, Index) { + using Var = variant; + + Var v = 1; + EXPECT_EQ(0, v.index()); + v = "str"; + EXPECT_EQ(1, v.index()); + v = 0.; + EXPECT_EQ(2, v.index()); + + Var v2 = v; + EXPECT_EQ(2, v2.index()); + v2.emplace(3); + EXPECT_EQ(0, v2.index()); +} + +TEST(VariantTest, NotValuelessByException) { + using Var = variant; + + 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(3); + EXPECT_FALSE(v.valueless_by_exception()); +} + +#ifdef ABSL_HAVE_EXCEPTIONS + +TEST(VariantTest, IndexValuelessByException) { + using Var = variant; + + Var v(absl::in_place_index_t<0>{}); + EXPECT_EQ(0, v.index()); + ToValuelessByException(v); + EXPECT_EQ(absl::variant_npos, v.index()); + v = "str"; + EXPECT_EQ(1, v.index()); +} + +TEST(VariantTest, ValuelessByException) { + using Var = variant; + + Var v(absl::in_place_index_t<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 a = v1, b = v2; + + EXPECT_THAT(a, VariantWith(v1)); + EXPECT_THAT(b, VariantWith(v2)); + + a.swap(b); + EXPECT_THAT(a, VariantWith(v2)); + EXPECT_THAT(b, VariantWith(v1)); + EXPECT_TRUE(absl::get(a).special_swap); + + using V = variant; + int i = 33; + std::string s = "abc"; + V valueless(in_place_index_t<0>{}); + ToValuelessByException(valueless); + { + // lhs and rhs holds different alternative + V lhs(i), rhs(s); + lhs.swap(rhs); + EXPECT_THAT(lhs, VariantWith(s)); + EXPECT_THAT(rhs, VariantWith(i)); + } + { + // lhs is valueless + V lhs(valueless), rhs(i); + lhs.swap(rhs); + EXPECT_THAT(lhs, VariantWith(i)); + EXPECT_TRUE(rhs.valueless_by_exception()); + } + { + // rhs is valueless + V lhs(s), rhs(valueless); + lhs.swap(rhs); + EXPECT_THAT(rhs, VariantWith(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()); + } +} + +////////////////////// +// [variant.helper] // +////////////////////// + +TEST(VariantTest, VariantSize) { + { + using Size1Variant = absl::variant; + EXPECT_EQ(1, absl::variant_size::value); + EXPECT_EQ(1, absl::variant_size::value); + EXPECT_EQ(1, absl::variant_size::value); + EXPECT_EQ(1, absl::variant_size::value); + } + + { + using Size3Variant = absl::variant; + EXPECT_EQ(3, absl::variant_size::value); + EXPECT_EQ(3, absl::variant_size::value); + EXPECT_EQ(3, absl::variant_size::value); + EXPECT_EQ(3, absl::variant_size::value); + } +} + +TEST(VariantTest, VariantAlternative) { + { + using V = absl::variant; + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE(( + std::is_same>::value)); + + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE(( + std::is_same>::value)); + + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE(( + std::is_same>::value)); + } + + { + using V = absl::variant; + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE(( + std::is_same>::value)); + + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE(( + std::is_same>::value)); + + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE(( + std::is_same>::value)); + } +} + +/////////////////// +// [variant.get] // +/////////////////// + +TEST(VariantTest, HoldsAlternative) { + using Var = variant; + + Var v = 1; + EXPECT_TRUE(absl::holds_alternative(v)); + EXPECT_FALSE(absl::holds_alternative(v)); + EXPECT_FALSE(absl::holds_alternative(v)); + v = "str"; + EXPECT_FALSE(absl::holds_alternative(v)); + EXPECT_TRUE(absl::holds_alternative(v)); + EXPECT_FALSE(absl::holds_alternative(v)); + v = 0.; + EXPECT_FALSE(absl::holds_alternative(v)); + EXPECT_FALSE(absl::holds_alternative(v)); + EXPECT_TRUE(absl::holds_alternative(v)); + + Var v2 = v; + EXPECT_FALSE(absl::holds_alternative(v2)); + EXPECT_FALSE(absl::holds_alternative(v2)); + EXPECT_TRUE(absl::holds_alternative(v2)); + v2.emplace(3); + EXPECT_TRUE(absl::holds_alternative(v2)); + EXPECT_FALSE(absl::holds_alternative(v2)); + EXPECT_FALSE(absl::holds_alternative(v2)); +} + +TEST(VariantTest, GetIndex) { + using Var = variant; + + { + Var v(absl::in_place_index_t<0>{}, 0); + + using LValueGetType = decltype(absl::get<0>(v)); + using RValueGetType = decltype(absl::get<0>(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<0>(v), 0); + EXPECT_EQ(absl::get<0>(absl::move(v)), 0); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get<0>(const_v)); + using ConstRValueGetType = decltype(absl::get<0>(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<0>(const_v), 0); + EXPECT_EQ(absl::get<0>(absl::move(const_v)), 0); + } + + { + Var v = std::string("Hello"); + + using LValueGetType = decltype(absl::get<1>(v)); + using RValueGetType = decltype(absl::get<1>(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<1>(v), "Hello"); + EXPECT_EQ(absl::get<1>(absl::move(v)), "Hello"); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get<1>(const_v)); + using ConstRValueGetType = decltype(absl::get<1>(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<1>(const_v), "Hello"); + EXPECT_EQ(absl::get<1>(absl::move(const_v)), "Hello"); + } + + { + Var v = 2.0; + + using LValueGetType = decltype(absl::get<2>(v)); + using RValueGetType = decltype(absl::get<2>(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<2>(v), 2.); + EXPECT_EQ(absl::get<2>(absl::move(v)), 2.); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get<2>(const_v)); + using ConstRValueGetType = decltype(absl::get<2>(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<2>(const_v), 2.); + EXPECT_EQ(absl::get<2>(absl::move(const_v)), 2.); + } + + { + Var v(absl::in_place_index_t<0>{}, 0); + v.emplace<3>(1); + + using LValueGetType = decltype(absl::get<3>(v)); + using RValueGetType = decltype(absl::get<3>(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<3>(v), 1); + EXPECT_EQ(absl::get<3>(absl::move(v)), 1); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get<3>(const_v)); + using ConstRValueGetType = decltype(absl::get<3>(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get<3>(const_v), 1); + EXPECT_EQ(absl::get<3>(absl::move(const_v)), 1); // NOLINT + } +} + +TEST(VariantTest, BadGetIndex) { + using Var = variant; + + { + 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; + + { + Var v = 1; + + using LValueGetType = decltype(absl::get(v)); + using RValueGetType = decltype(absl::get(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get(v), 1); + EXPECT_EQ(absl::get(absl::move(v)), 1); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get(const_v)); + using ConstRValueGetType = decltype(absl::get(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get(const_v), 1); + EXPECT_EQ(absl::get(absl::move(const_v)), 1); + } + + { + Var v = std::string("Hello"); + + using LValueGetType = decltype(absl::get<1>(v)); + using RValueGetType = decltype(absl::get<1>(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get(v), "Hello"); + EXPECT_EQ(absl::get(absl::move(v)), "Hello"); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get<1>(const_v)); + using ConstRValueGetType = decltype(absl::get<1>(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get(const_v), "Hello"); + EXPECT_EQ(absl::get(absl::move(const_v)), "Hello"); + } + + { + Var v = 2.0; + + using LValueGetType = decltype(absl::get<2>(v)); + using RValueGetType = decltype(absl::get<2>(absl::move(v))); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get(v), 2.); + EXPECT_EQ(absl::get(absl::move(v)), 2.); + + const Var& const_v = v; + using ConstLValueGetType = decltype(absl::get<2>(const_v)); + using ConstRValueGetType = decltype(absl::get<2>(absl::move(const_v))); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(absl::get(const_v), 2.); + EXPECT_EQ(absl::get(absl::move(const_v)), 2.); + } +} + +TEST(VariantTest, BadGetType) { + using Var = variant; + + { + Var v = 1; + + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(v)); + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( + absl::get(std::move(v))); + + const Var& const_v = v; + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(const_v)); + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( + absl::get(std::move(const_v))); // NOLINT + } + + { + Var v = std::string("Hello"); + + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(v)); + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(std::move(v))); + + const Var& const_v = v; + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(const_v)); + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( + absl::get(std::move(const_v))); // NOLINT + } +} + +TEST(VariantTest, GetIfIndex) { + using Var = variant; + + { + Var v(absl::in_place_index_t<0>{}, 0); + EXPECT_TRUE(noexcept(absl::get_if<0>(&v))); + + { + auto* elem = absl::get_if<0>(&v); + EXPECT_TRUE((std::is_same::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, 0); + { + auto* bad_elem = absl::get_if<1>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<2>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<3>(&v); + EXPECT_TRUE((std::is_same::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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, 0); + { + auto* bad_elem = absl::get_if<1>(&const_v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<2>(&const_v); + EXPECT_TRUE((std::is_same::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::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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, "Hello"); + { + auto* bad_elem = absl::get_if<0>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<2>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<3>(&v); + EXPECT_TRUE((std::is_same::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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, "Hello"); + { + auto* bad_elem = absl::get_if<0>(&const_v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<2>(&const_v); + EXPECT_TRUE((std::is_same::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::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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, 2.0); + { + auto* bad_elem = absl::get_if<0>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<1>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<3>(&v); + EXPECT_TRUE((std::is_same::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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, 2.0); + { + auto* bad_elem = absl::get_if<0>(&const_v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<1>(&const_v); + EXPECT_TRUE((std::is_same::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::value)); + } + } + } + + { + Var v(absl::in_place_index_t<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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, 1); + { + auto* bad_elem = absl::get_if<0>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<1>(&v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<2>(&v); + EXPECT_TRUE((std::is_same::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::value)); + ASSERT_NE(elem, nullptr); + EXPECT_EQ(*elem, 1); + { + auto* bad_elem = absl::get_if<0>(&const_v); + EXPECT_TRUE((std::is_same::value)); + EXPECT_EQ(bad_elem, nullptr); + } + { + auto* bad_elem = absl::get_if<1>(&const_v); + EXPECT_TRUE((std::is_same::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::value)); + } + } + } +} + +////////////////////// +// [variant.relops] // +////////////////////// + +TEST(VariantTest, OperatorEquals) { + variant 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 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 int_v(1), string_v("Hello"), + valueless(absl::in_place_index_t<0>{}), + other_valueless(absl::in_place_index_t<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 int_v(1), string_v("Hello"), + valueless(absl::in_place_index_t<0>{}), + other_valueless(absl::in_place_index_t<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 +struct ConvertTo { + template + T operator()(const U& u) const { + return u; + } +}; + +TEST(VariantTest, VisitSimple) { + variant v = "A"; + + std::string str = absl::visit(ConvertTo{}, v); + EXPECT_EQ("A", str); + + v = std::string("B"); + + absl::string_view piece = absl::visit(ConvertTo{}, v); + EXPECT_EQ("B", piece); + + struct StrLen { + int operator()(const std::string& s) const { return s.size(); } + int operator()(const char* s) const { return strlen(s); } + }; + + v = "SomeStr"; + EXPECT_EQ(7, absl::visit(StrLen{}, v)); + v = std::string("VeryLargeThisTime"); + EXPECT_EQ(17, absl::visit(StrLen{}, v)); +} + +TEST(VariantTest, VisitRValue) { + variant 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{}, absl::move(v))); + + // Also test the variadic overload. + EXPECT_EQ(0, absl::visit(Visitor{}, v, v)); + EXPECT_EQ(1, absl::visit(Visitor{}, v, absl::move(v))); + EXPECT_EQ(2, absl::visit(Visitor{}, absl::move(v), v)); + EXPECT_EQ(3, absl::visit(Visitor{}, absl::move(v), absl::move(v))); +} + +TEST(VariantTest, VisitRValueVisitor) { + variant 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 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::value)); + EXPECT_TRUE( + (std::is_same::value)); + EXPECT_TRUE(( + std::is_same::value)); + EXPECT_TRUE( + (std::is_same::value)); +} + +TEST(VariantTest, VisitVariadic) { + using A = variant; + using B = variant, absl::string_view>; + + struct Visitor { + std::pair operator()(int a, std::unique_ptr b) const { + return {a, *b}; + } + std::pair operator()(absl::string_view a, + std::unique_ptr b) const { + return {static_cast(a.size()), static_cast(*b)}; + } + std::pair operator()(int a, absl::string_view b) const { + return {a, static_cast(b.size())}; + } + std::pair operator()(absl::string_view a, + absl::string_view b) const { + return {static_cast(a.size()), static_cast(b.size())}; + } + }; + + EXPECT_THAT(absl::visit(Visitor(), A(1), B(std::unique_ptr(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(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 three = 3; + absl::variant 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_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +TEST(VariantTest, VisitMemberFunction) { + absl::variant> p(absl::make_unique()); + absl::variant> cp( + absl::make_unique()); + absl::variant three = 3; + absl::variant 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> p(absl::make_unique(Class{42})); + absl::variant> cp( + absl::make_unique(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_HAVE_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::value); + EXPECT_TRUE(is_trivially_move_constructible::value); + EXPECT_TRUE(absl::is_trivially_copy_constructible::value); + EXPECT_TRUE(is_trivially_move_assignable::value); + EXPECT_TRUE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_destructible::value); +} + +TEST(VariantTest, VariantMonostateDefaultConstruction) { + absl::variant var; + EXPECT_EQ(var.index(), 0); +} + +//////////////////////////////// +// [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() == + std::declval())); + EXPECT_TRUE(noexcept(std::declval() != + std::declval())); + EXPECT_TRUE(noexcept(std::declval() < + std::declval())); + EXPECT_TRUE(noexcept(std::declval() > + std::declval())); + EXPECT_TRUE(noexcept(std::declval() <= + std::declval())); + EXPECT_TRUE(noexcept(std::declval() >= + std::declval())); +} + +/////////////////////// +// [variant.specalg] // +/////////////////////// + +TEST(VariantTest, NonmemberSwap) { + using std::swap; + + SpecialSwap v1(3); + SpecialSwap v2(7); + + variant a = v1, b = v2; + + EXPECT_THAT(a, VariantWith(v1)); + EXPECT_THAT(b, VariantWith(v2)); + + std::swap(a, b); + EXPECT_THAT(a, VariantWith(v2)); + EXPECT_THAT(b, VariantWith(v1)); +#ifndef ABSL_HAVE_STD_VARIANT + EXPECT_FALSE(absl::get(a).special_swap); +#endif + + swap(a, b); + EXPECT_THAT(a, VariantWith(v1)); + EXPECT_THAT(b, VariantWith(v2)); + EXPECT_TRUE(absl::get(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 const hasher{}; + static_assert(std::is_same::value, ""); + EXPECT_EQ(hasher(mono), hasher(other_mono)); +} + +TEST(VariantTest, Hash) { + static_assert(type_traits_internal::IsHashEnabled>::value, ""); + static_assert(type_traits_internal::IsHashEnabled>::value, + ""); + static_assert( + type_traits_internal::IsHashEnabled>::value, ""); + +#if defined(_MSC_VER) || \ + (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 4000 && \ + _LIBCPP_STD_VER > 11) || \ + defined(__APPLE__) + // For MSVC and libc++ (< 4.0 and c++14), std::hash primary template has a + // static_assert to catch any user-defined type T that doesn't provide a hash + // specialization. So instantiating std::hash> will result + // in a hard error which is not SFINAE friendly. +#define ABSL_STD_HASH_NOT_SFINAE_FRIENDLY 1 +#endif + +#ifndef ABSL_STD_HASH_NOT_SFINAE_FRIENDLY + static_assert( + !type_traits_internal::IsHashEnabled>::value, ""); + static_assert(!type_traits_internal::IsHashEnabled< + variant>::value, + ""); +#endif + +// MSVC std::hash does not use the index, thus produce the same +// result on the same value as different alternative. +#if !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) + { + // same value as different alternative + variant v0(in_place_index_t<0>{}, 42); + variant v1(in_place_index_t<1>{}, 42); + std::hash> hash; + EXPECT_NE(hash(v0), hash(v1)); + } +#endif // !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) + + { + std::hash> hash; + std::set hashcodes; + for (int i = 0; i < 100; ++i) { + hashcodes.insert(hash(i)); + } + EXPECT_GT(hashcodes.size(), 90); + + // test const-qualified + static_assert( + type_traits_internal::IsHashEnabled>::value, ""); + static_assert( + type_traits_internal::IsHashEnabled>::value, + ""); + std::hash> 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. +TEST(VariantTest, TestConvertingSet) { + typedef variant Variant; + Variant v(1.0); + const int two = 2; + v = two; + EXPECT_TRUE(absl::holds_alternative(v)); + ASSERT_TRUE(nullptr != absl::get_if(&v)); + EXPECT_DOUBLE_EQ(2, absl::get(v)); +} + +// Test that a vector of variants behaves reasonably. +TEST(VariantTest, Container) { + typedef variant Variant; + + // Creation of vector should work + std::vector 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 Variant; + const int kValue = 1; + Variant v(kValue); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(kValue, absl::get(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 Variant; + const int kValue = 1; + Variant v(kValue); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(kValue, absl::get(v)); + + // emplace with zero arguments, then back to 'int' + v.emplace(); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(0, absl::get(v).value); + v = kValue; + ASSERT_TRUE(absl::holds_alternative(v)); + + // emplace with one argument: + v.emplace(1); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(1, absl::get(v).value); + v = kValue; + ASSERT_TRUE(absl::holds_alternative(v)); + + // emplace with two arguments: + v.emplace(1, 2); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(3, absl::get(v).value); + v = kValue; + ASSERT_TRUE(absl::holds_alternative(v)); + + // emplace with three arguments + v.emplace(1, 2, 3); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(6, absl::get(v).value); + v = kValue; + ASSERT_TRUE(absl::holds_alternative(v)); + + // emplace with four arguments + v.emplace(1, 2, 3, 4); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(10, absl::get(v).value); + v = kValue; + ASSERT_TRUE(absl::holds_alternative(v)); +} + +TEST(VariantTest, TestEmplaceDestroysCurrentValue) { + typedef variant Variant; + int counter = 0; + Variant v(0); + ASSERT_TRUE(absl::holds_alternative(v)); + v.emplace(&counter); + ASSERT_TRUE(absl::holds_alternative(v)); + ASSERT_EQ(0, counter); + v.emplace(); + ASSERT_TRUE(absl::holds_alternative(v)); + EXPECT_EQ(1, counter); +} + +TEST(VariantTest, TestMoveSemantics) { + typedef variant, std::unique_ptr> Variant; + + // Construct a variant by moving from an element value. + Variant v(absl::WrapUnique(new int(10))); + EXPECT_TRUE(absl::holds_alternative>(v)); + + // Construct a variant by moving from another variant. + Variant v2(absl::move(v)); + ASSERT_TRUE(absl::holds_alternative>(v2)); + ASSERT_NE(nullptr, absl::get>(v2)); + EXPECT_EQ(10, *absl::get>(v2)); + + // Moving from a variant object leaves it holding moved-from value of the + // same element type. + EXPECT_TRUE(absl::holds_alternative>(v)); + ASSERT_NE(nullptr, absl::get_if>(&v)); + EXPECT_EQ(nullptr, absl::get>(v)); + + // Assign a variant from an element value by move. + v = absl::make_unique("foo"); + ASSERT_TRUE(absl::holds_alternative>(v)); + EXPECT_EQ("foo", *absl::get>(v)); + + // Move-assign a variant. + v2 = absl::move(v); + ASSERT_TRUE(absl::holds_alternative>(v2)); + EXPECT_EQ("foo", *absl::get>(v2)); + EXPECT_TRUE(absl::holds_alternative>(v)); +} + +variant PassThrough(const variant& arg) { + return arg; +} + +TEST(VariantTest, TestImplicitConversion) { + EXPECT_TRUE(absl::holds_alternative(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(PassThrough(std::string("foo")))); +} + +struct Convertible2; +struct Convertible1 { + Convertible1() {} + Convertible1(const Convertible1&) {} + Convertible1& operator=(const Convertible1&) { return *this; } + + // implicit conversion from Convertible2 + Convertible1(const Convertible2&) {} // NOLINT(runtime/explicit) +}; + +struct Convertible2 { + Convertible2() {} + Convertible2(const Convertible2&) {} + Convertible2& operator=(const Convertible2&) { return *this; } + + // implicit conversion from Convertible1 + Convertible2(const Convertible1&) {} // NOLINT(runtime/explicit) +}; + +TEST(VariantTest, TestRvalueConversion) { + variant var( + ConvertVariantTo>(variant(0))); + ASSERT_TRUE(absl::holds_alternative(var)); + EXPECT_EQ(0.0, absl::get(var)); + + var = ConvertVariantTo>( + variant("foo")); + ASSERT_TRUE(absl::holds_alternative(var)); + EXPECT_EQ("foo", absl::get(var)); + + variant singleton( + ConvertVariantTo>(variant(42))); + ASSERT_TRUE(absl::holds_alternative(singleton)); + EXPECT_EQ(42.0, absl::get(singleton)); + + singleton = ConvertVariantTo>(variant(3.14f)); + ASSERT_TRUE(absl::holds_alternative(singleton)); + EXPECT_FLOAT_EQ(3.14f, static_cast(absl::get(singleton))); + + singleton = ConvertVariantTo>(variant(0)); + ASSERT_TRUE(absl::holds_alternative(singleton)); + EXPECT_EQ(0.0, absl::get(singleton)); + + variant variant2( + ConvertVariantTo>(variant(42))); + ASSERT_TRUE(absl::holds_alternative(variant2)); + EXPECT_EQ(42, absl::get(variant2)); + + variant2 = ConvertVariantTo>(variant(42)); + ASSERT_TRUE(absl::holds_alternative(variant2)); + EXPECT_EQ(42, absl::get(variant2)); + + variant variant3( + ConvertVariantTo>( + (variant(Convertible1())))); + ASSERT_TRUE(absl::holds_alternative(variant3)); + + variant3 = ConvertVariantTo>( + variant(Convertible2())); + ASSERT_TRUE(absl::holds_alternative(variant3)); +} + +TEST(VariantTest, TestLvalueConversion) { + variant source1 = 0; + variant destination( + ConvertVariantTo>(source1)); + ASSERT_TRUE(absl::holds_alternative(destination)); + EXPECT_EQ(0.0, absl::get(destination)); + + variant source2 = "foo"; + destination = ConvertVariantTo>(source2); + ASSERT_TRUE(absl::holds_alternative(destination)); + EXPECT_EQ("foo", absl::get(destination)); + + variant source3(42); + variant singleton(ConvertVariantTo>(source3)); + ASSERT_TRUE(absl::holds_alternative(singleton)); + EXPECT_EQ(42.0, absl::get(singleton)); + + source3 = 3.14f; + singleton = ConvertVariantTo>(source3); + ASSERT_TRUE(absl::holds_alternative(singleton)); + EXPECT_FLOAT_EQ(3.14f, static_cast(absl::get(singleton))); + + variant source4(0); + singleton = ConvertVariantTo>(source4); + ASSERT_TRUE(absl::holds_alternative(singleton)); + EXPECT_EQ(0.0, absl::get(singleton)); + + variant source5(42); + variant variant2( + ConvertVariantTo>(source5)); + ASSERT_TRUE(absl::holds_alternative(variant2)); + EXPECT_EQ(42, absl::get(variant2)); + + variant source6(42); + variant2 = ConvertVariantTo>(source6); + ASSERT_TRUE(absl::holds_alternative(variant2)); + EXPECT_EQ(42, absl::get(variant2)); + + variant source7((Convertible1())); + variant variant3( + ConvertVariantTo>(source7)); + ASSERT_TRUE(absl::holds_alternative(variant3)); + + source7 = Convertible2(); + variant3 = ConvertVariantTo>(source7); + ASSERT_TRUE(absl::holds_alternative(variant3)); +} + +TEST(VariantTest, TestMoveConversion) { + using Variant = + variant, std::unique_ptr>; + using OtherVariant = variant, std::unique_ptr>; + + Variant var( + ConvertVariantTo(OtherVariant{absl::make_unique(0)})); + ASSERT_TRUE(absl::holds_alternative>(var)); + ASSERT_NE(absl::get>(var), nullptr); + EXPECT_EQ(0, *absl::get>(var)); + + var = + ConvertVariantTo(OtherVariant(absl::make_unique("foo"))); + ASSERT_TRUE(absl::holds_alternative>(var)); + EXPECT_EQ("foo", *absl::get>(var)); +} + +TEST(VariantTest, DoesNotMoveFromLvalues) { + // 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>; + using OtherVariant = variant, std::shared_ptr>; + + Variant v1(std::make_shared(0)); + + // Test copy constructor + Variant v2(v1); + EXPECT_EQ(absl::get>(v1), + absl::get>(v2)); + + // Test copy-assignment operator + v1 = std::make_shared("foo"); + v2 = v1; + EXPECT_EQ(absl::get>(v1), + absl::get>(v2)); + + // Test converting copy constructor + OtherVariant other(std::make_shared(0)); + Variant v3(ConvertVariantTo(other)); + EXPECT_EQ(absl::get>(other), + absl::get>(v3)); + + other = std::make_shared("foo"); + v3 = ConvertVariantTo(other); + EXPECT_EQ(absl::get>(other), + absl::get>(v3)); +} + +TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { + variant var( + ConvertVariantTo>(variant(3))); + EXPECT_THAT(absl::get_if(&var), Pointee(3.0)); + + var = ConvertVariantTo>( + variant("foo")); + EXPECT_THAT(absl::get_if(&var), Pointee(std::string("foo"))); + + variant singleton( + ConvertVariantTo>(variant(42))); + EXPECT_THAT(absl::get_if(&singleton), Pointee(42.0)); + + singleton = ConvertVariantTo>(variant(3.14f)); + EXPECT_THAT(absl::get_if(&singleton), Pointee(DoubleEq(3.14f))); + + singleton = ConvertVariantTo>(variant(3)); + EXPECT_THAT(absl::get_if(&singleton), Pointee(3.0)); + + variant variant2( + ConvertVariantTo>(variant(42))); + EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); + + variant2 = ConvertVariantTo>(variant(42)); + EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); + + variant variant3( + ConvertVariantTo>( + (variant(Convertible1())))); + ASSERT_TRUE(absl::holds_alternative(variant3)); + + variant3 = ConvertVariantTo>( + variant(Convertible2())); + ASSERT_TRUE(absl::holds_alternative(variant3)); +} + +TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { + variant source1 = 3; + variant destination( + ConvertVariantTo>(source1)); + EXPECT_THAT(absl::get_if(&destination), Pointee(3.0)); + + variant source2 = "foo"; + destination = ConvertVariantTo>(source2); + EXPECT_THAT(absl::get_if(&destination), Pointee(std::string("foo"))); + + variant source3(42); + variant singleton(ConvertVariantTo>(source3)); + EXPECT_THAT(absl::get_if(&singleton), Pointee(42.0)); + + source3 = 3.14f; + singleton = ConvertVariantTo>(source3); + EXPECT_FLOAT_EQ(3.14f, static_cast(absl::get(singleton))); + EXPECT_THAT(absl::get_if(&singleton), Pointee(DoubleEq(3.14f))); + + variant source4(3); + singleton = ConvertVariantTo>(source4); + EXPECT_THAT(absl::get_if(&singleton), Pointee(3.0)); + + variant source5(42); + variant variant2( + ConvertVariantTo>(source5)); + EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); + + variant source6(42); + variant2 = ConvertVariantTo>(source6); + EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); + + variant source7((Convertible1())); + variant variant3( + ConvertVariantTo>(source7)); + ASSERT_TRUE(absl::holds_alternative(variant3)); + + source7 = Convertible2(); + variant3 = ConvertVariantTo>(source7); + ASSERT_TRUE(absl::holds_alternative(variant3)); +} + +TEST(VariantTest, TestMoveConversionViaConvertVariantTo) { + using Variant = + variant, std::unique_ptr>; + using OtherVariant = variant, std::unique_ptr>; + + Variant var( + ConvertVariantTo(OtherVariant{absl::make_unique(3)})); + EXPECT_THAT(absl::get_if>(&var), + Pointee(Pointee(3))); + + var = + ConvertVariantTo(OtherVariant(absl::make_unique("foo"))); + EXPECT_THAT(absl::get_if>(&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_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1 +#endif + +TEST(VariantTest, TestCopyAndMoveTypeTraits) { + EXPECT_TRUE(std::is_copy_constructible>::value); + EXPECT_TRUE(std::is_copy_assignable>::value); + EXPECT_TRUE(std::is_move_constructible>::value); + EXPECT_TRUE(std::is_move_assignable>::value); + EXPECT_TRUE(std::is_move_constructible>>::value); + EXPECT_TRUE(std::is_move_assignable>>::value); + EXPECT_FALSE( + std::is_copy_constructible>>::value); + EXPECT_FALSE(std::is_copy_assignable>>::value); + + EXPECT_FALSE( + absl::is_trivially_copy_constructible>::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable>::value); +#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY + EXPECT_TRUE(absl::is_trivially_copy_constructible>::value); + EXPECT_TRUE(absl::is_trivially_copy_assignable>::value); + EXPECT_TRUE(is_trivially_move_constructible>::value); + EXPECT_TRUE(is_trivially_move_assignable>::value); +#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY +} + +TEST(VariantTest, TestVectorOfMoveonlyVariant) { + // Verify that variant works correctly as a std::vector element. + std::vector, std::string>> vec; + vec.push_back(absl::make_unique(42)); + vec.emplace_back("Hello"); + vec.reserve(3); + auto another_vec = absl::move(vec); + // As a sanity check, verify vector contents. + ASSERT_EQ(2, another_vec.size()); + EXPECT_EQ(42, *absl::get>(another_vec[0])); + EXPECT_EQ("Hello", absl::get(another_vec[1])); +} + +TEST(VariantTest, NestedVariant) { +#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY + static_assert(absl::is_trivially_copy_constructible>(), ""); + static_assert(absl::is_trivially_copy_assignable>(), ""); + static_assert(is_trivially_move_constructible>(), ""); + static_assert(is_trivially_move_assignable>(), ""); + + static_assert(absl::is_trivially_copy_constructible>>(), + ""); + static_assert(absl::is_trivially_copy_assignable>>(), + ""); + static_assert(is_trivially_move_constructible>>(), ""); + static_assert(is_trivially_move_assignable>>(), ""); +#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY + + variant x(42); + variant> y(x); + variant> z(y); + EXPECT_TRUE(absl::holds_alternative>(z)); + EXPECT_EQ(x, absl::get>(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; + + EXPECT_FALSE(is_trivially_move_constructible::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible::value); + EXPECT_FALSE(is_trivially_move_assignable::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_destructible::value); + } + + { + using TrivMoveVar = absl::variant; + + EXPECT_TRUE(is_trivially_move_constructible::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible::value); + EXPECT_FALSE(is_trivially_move_assignable::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_destructible::value); + } + + { + using TrivCopyVar = absl::variant; + + EXPECT_TRUE(is_trivially_move_constructible::value); + EXPECT_TRUE(absl::is_trivially_copy_constructible::value); + EXPECT_FALSE(is_trivially_move_assignable::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_destructible::value); + } + + { + using TrivMoveAssignVar = absl::variant; + + EXPECT_TRUE(is_trivially_move_constructible::value); + EXPECT_FALSE( + absl::is_trivially_copy_constructible::value); + EXPECT_TRUE(is_trivially_move_assignable::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_destructible::value); + } + + { + using TrivCopyAssignVar = absl::variant; + + EXPECT_TRUE(is_trivially_move_constructible::value); + EXPECT_TRUE( + absl::is_trivially_copy_constructible::value); + EXPECT_TRUE(is_trivially_move_assignable::value); + EXPECT_TRUE(absl::is_trivially_copy_assignable::value); + EXPECT_TRUE(absl::is_trivially_destructible::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; + V v1(absl::in_place_index_t<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; + V v1(absl::in_place_index_t<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 +} // namespace absl diff --git a/absl/utility/utility.h b/absl/utility/utility.h index 1943c4a..5b9b84e 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h @@ -161,7 +161,7 @@ ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {}); #endif // ABSL_HAVE_STD_OPTIONAL -#ifdef ABSL_HAVE_STD_ANY +#if defined(ABSL_HAVE_STD_ANY) || defined(ABSL_HAVE_STD_VARIANT) using std::in_place_type_t; #else @@ -172,7 +172,11 @@ using std::in_place_type_t; // for C++17's `std::in_place_type_t`. template struct in_place_type_t {}; -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_HAVE_STD_ANY || ABSL_HAVE_STD_VARIANT + +#ifdef ABSL_HAVE_STD_VARIANT +using std::in_place_index_t; +#else // in_place_index_t // @@ -181,6 +185,7 @@ struct in_place_type_t {}; // for C++17's `std::in_place_index_t`. template struct in_place_index_t {}; +#endif // ABSL_HAVE_STD_VARIANT // Constexpr move and forward -- cgit v1.2.3