Commit faf0a1b9 by Abseil Team Committed by Jon Cohen

- 551e205ef49682a1cb7e6e0cda46957fbcf88edd Release absl::variant. by Xiaoyi…

  - 551e205ef49682a1cb7e6e0cda46957fbcf88edd Release absl::variant. by Xiaoyi Zhang <zhangxy@google.com>
  - 01c52f640594d073c6e54c47e7853b25522cf085 Update comments in absl::variant (and minor changes to ab... by Tom Manshreck <shreck@google.com>
  - 064465e1e6b158abd8c38fd1942b4fc464b57d6a Documentation change. by Abseil Team <absl-team@google.com>
  - 58df2c8a27e80c9ea21d87c1acee8019246377c9 Relocates SetCountdown and UnsetCountdown to the exceptio... by Abseil Team <absl-team@google.com>
  - fd9d248d0948d472f2543f7fd9c0ae4a1cd60d01 Clarify thread_annotation.h documentation around local va... by Abseil Team <absl-team@google.com>
  - 0d0abaf7f0945ac5f2c5f49e78afe1b326d5aca0 Typo fix in comments. by Abseil Team <absl-team@google.com>
  - 67286d587cbd07508a81e5b8147c245a5b5538b4 Internal change. by Xiaoyi Zhang <zhangxy@google.com>

GitOrigin-RevId: 551e205ef49682a1cb7e6e0cda46957fbcf88edd
Change-Id: I1a343b080187293cb5f892a309618b5712e7aa14
parent 5b535401
......@@ -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(<variant>) && __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 <optional>, <any>, <string_view>. So we use _MSC_VER to check
// whether we have VS 2017 RTM (when <optional>, <any>, <string_view> 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 <optional>, <any>, <string_view>, <variant>. So we use
// _MSC_VER to check whether we have VS 2017 RTM (when <optional>, <any>,
// <string_view>, <variant> 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
......
......@@ -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 <typename F>
......@@ -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);
......
......@@ -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<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
const Invariant& invariant) {
auto t_ptr = factory();
absl::optional<testing::AssertionResult> 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<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
*current_res << e.what() << " failed invariant check";
}
}
exceptions_internal::countdown = -1;
UnsetCountdown();
return current_res;
}
......@@ -196,11 +202,6 @@ inline absl::optional<testing::AssertionResult> 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 <typename T, typename... Args>
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>(args)...);
} catch (const exceptions_internal::TestException&) {
......
......@@ -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__))
......
......@@ -36,6 +36,7 @@
#define ABSL_META_TYPE_TRAITS_H_
#include <stddef.h>
#include <functional>
#include <type_traits>
#include "absl/base/config.h"
......@@ -349,5 +350,23 @@ using underlying_type_t = typename std::underlying_type<T>::type;
template <typename T>
using result_of_t = typename std::result_of<T>::type;
namespace type_traits_internal {
template <typename Key, typename = size_t>
struct IsHashable : std::false_type {};
template <typename Key>
struct IsHashable<Key,
decltype(std::declval<std::hash<Key>>()(std::declval<Key>()))>
: std::true_type {};
template <typename Key>
struct IsHashEnabled
: absl::conjunction<std::is_default_constructible<std::hash<Key>>,
std::is_copy_constructible<std::hash<Key>>,
std::is_destructible<std::hash<Key>>,
std::is_copy_assignable<std::hash<Key>>,
IsHashable<Key>> {};
} // namespace type_traits_internal
} // namespace absl
#endif // ABSL_META_TYPE_TRAITS_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<std::string> v = {"foo", "bar", "baz"};
// std::string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
......
......@@ -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
......
......@@ -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",
],
)
......@@ -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
......
......@@ -172,7 +172,9 @@ const ValueType* any_cast(const any* operand) noexcept;
template <typename ValueType>
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
......
// 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<int>(a); // 65
// try {
// absl::any_cast<char>(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();
......
// 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<int> 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;
......
// 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 <cstdlib>
#include <stdexcept>
#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
// 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 <stdexcept>
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<int, std::string> v;
// v = 1;
// try {
// absl::get<std::string>(v);
// } catch(const absl::bad_variant_access& e) {
// std::cout << "Bad variant access: " << e.what() << '\n';
// }
class bad_variant_access : public std::exception {
public:
bad_variant_access() noexcept = default;
~bad_variant_access() override;
const char* what() const noexcept override;
};
namespace variant_internal {
[[noreturn]] void ThrowBadVariantAccess();
[[noreturn]] void Rethrow();
} // namespace variant_internal
} // namespace absl
#endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_
// 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 <cstddef>
#include <memory>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#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... Types>
class variant;
ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1);
template <class T>
struct variant_size;
template <std::size_t I, class T>
struct variant_alternative;
namespace variant_internal {
// NOTE: See specializations below for details.
template <std::size_t I, class T>
struct VariantAlternativeSfinae {};
// Requires: I < variant_size_v<T>.
//
// Value: The Ith type of Types...
template <std::size_t I, class T0, class... Tn>
struct VariantAlternativeSfinae<I, variant<T0, Tn...>>
: VariantAlternativeSfinae<I - 1, variant<Tn...>> {};
// Value: T0
template <class T0, class... Ts>
struct VariantAlternativeSfinae<0, variant<T0, Ts...>> {
using type = T0;
};
template <std::size_t I, class T>
using VariantAlternativeSfinaeT = typename VariantAlternativeSfinae<I, T>::type;
// NOTE: Requires T to be a reference type.
template <class T, class U>
struct GiveQualsTo;
template <class T, class U>
struct GiveQualsTo<T&, U> {
using type = U&;
};
template <class T, class U>
struct GiveQualsTo<T&&, U> {
using type = U&&;
};
template <class T, class U>
struct GiveQualsTo<const T&, U> {
using type = const U&;
};
template <class T, class U>
struct GiveQualsTo<const T&&, U> {
using type = const U&&;
};
template <class T, class U>
struct GiveQualsTo<volatile T&, U> {
using type = volatile U&;
};
template <class T, class U>
struct GiveQualsTo<volatile T&&, U> {
using type = volatile U&&;
};
template <class T, class U>
struct GiveQualsTo<volatile const T&, U> {
using type = volatile const U&;
};
template <class T, class U>
struct GiveQualsTo<volatile const T&&, U> {
using type = volatile const U&&;
};
template <class T, class U>
using GiveQualsToT = typename GiveQualsTo<T, U>::type;
// Convenience alias, since size_t integral_constant is used a lot in this file.
template <std::size_t I>
using SizeT = std::integral_constant<std::size_t, I>;
template <class Variant, class T, class = void>
struct IndexOfConstructedType {};
template <std::size_t I, class Variant>
struct VariantAccessResultImpl;
template <std::size_t I, template <class...> class Variantemplate, class... T>
struct VariantAccessResultImpl<I, Variantemplate<T...>&> {
using type = typename absl::variant_alternative<I, variant<T...>>::type&;
};
template <std::size_t I, template <class...> class Variantemplate, class... T>
struct VariantAccessResultImpl<I, const Variantemplate<T...>&> {
using type =
const typename absl::variant_alternative<I, variant<T...>>::type&;
};
template <std::size_t I, template <class...> class Variantemplate, class... T>
struct VariantAccessResultImpl<I, Variantemplate<T...>&&> {
using type = typename absl::variant_alternative<I, variant<T...>>::type&&;
};
template <std::size_t I, template <class...> class Variantemplate, class... T>
struct VariantAccessResultImpl<I, const Variantemplate<T...>&&> {
using type =
const typename absl::variant_alternative<I, variant<T...>>::type&&;
};
template <std::size_t I, class Variant>
using VariantAccessResult =
typename VariantAccessResultImpl<I, Variant&&>::type;
// NOTE: This is used instead of std::array to reduce instantiation overhead.
template <class T, std::size_t Size>
struct SimpleArray {
static_assert(Size != 0, "");
T value[Size];
};
template <class T>
struct AccessedType {
using type = T;
};
template <class T>
using AccessedTypeT = typename AccessedType<T>::type;
template <class T, std::size_t Size>
struct AccessedType<SimpleArray<T, Size>> {
using type = AccessedTypeT<T>;
};
template <class T>
constexpr T AccessSimpleArray(const T& value) {
return value;
}
template <class T, std::size_t Size, class... SizeT>
constexpr AccessedTypeT<T> AccessSimpleArray(const SimpleArray<T, Size>& table,
std::size_t head_index,
SizeT... tail_indices) {
return AccessSimpleArray(table.value[head_index], tail_indices...);
}
// Note: Intentionally is an alias.
template <class T>
using AlwaysZero = SizeT<0>;
template <class Op, class... Vs>
struct VisitIndicesResultImpl {
using type = absl::result_of_t<Op(AlwaysZero<Vs>...)>;
};
template <class Op, class... Vs>
using VisitIndicesResultT = typename VisitIndicesResultImpl<Op, Vs...>::type;
template <class ReturnType, class FunctionObject, class EndIndices,
std::size_t... BoundIndices>
struct MakeVisitationMatrix;
template <class ReturnType, class FunctionObject, std::size_t... Indices>
constexpr ReturnType call_with_indices(FunctionObject&& function) {
static_assert(
std::is_same<ReturnType, decltype(std::declval<FunctionObject>()(
SizeT<Indices>()...))>::value,
"Not all visitation overloads have the same return type.");
return absl::forward<FunctionObject>(function)(SizeT<Indices>()...);
}
template <class ReturnType, class FunctionObject, std::size_t... BoundIndices>
struct MakeVisitationMatrix<ReturnType, FunctionObject, index_sequence<>,
BoundIndices...> {
using ResultType = ReturnType (*)(FunctionObject&&);
static constexpr ResultType Run() {
return &call_with_indices<ReturnType, FunctionObject,
(BoundIndices - 1)...>;
}
};
template <class ReturnType, class FunctionObject, class EndIndices,
class CurrIndices, std::size_t... BoundIndices>
struct MakeVisitationMatrixImpl;
template <class ReturnType, class FunctionObject, std::size_t... EndIndices,
std::size_t... CurrIndices, std::size_t... BoundIndices>
struct MakeVisitationMatrixImpl<
ReturnType, FunctionObject, index_sequence<EndIndices...>,
index_sequence<CurrIndices...>, BoundIndices...> {
using ResultType = SimpleArray<
typename MakeVisitationMatrix<ReturnType, FunctionObject,
index_sequence<EndIndices...>>::ResultType,
sizeof...(CurrIndices)>;
static constexpr ResultType Run() {
return {{MakeVisitationMatrix<ReturnType, FunctionObject,
index_sequence<EndIndices...>,
BoundIndices..., CurrIndices>::Run()...}};
}
};
template <class ReturnType, class FunctionObject, std::size_t HeadEndIndex,
std::size_t... TailEndIndices, std::size_t... BoundIndices>
struct MakeVisitationMatrix<ReturnType, FunctionObject,
index_sequence<HeadEndIndex, TailEndIndices...>,
BoundIndices...>
: MakeVisitationMatrixImpl<
ReturnType, FunctionObject, index_sequence<TailEndIndices...>,
absl::make_index_sequence<HeadEndIndex>, BoundIndices...> {};
template <std::size_t... EndIndices, class Op, class... SizeT>
VisitIndicesResultT<Op, SizeT...> visit_indices(Op&& op, SizeT... indices) {
return AccessSimpleArray(
MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op,
index_sequence<(EndIndices + 1)...>>::Run(),
(indices + 1)...)(absl::forward<Op>(op));
}
template <class ReturnType>
[[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 <class Self, std::size_t I>
VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) {
return reinterpret_cast<VariantAccessResult<I, Self>>(self);
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
template <class T>
void DeducedDestroy(T& self) { // NOLINT
self.~T();
}
// NOTE: This type exists as a single entity for variant and its bases to
// befriend. It contains helper functionality that manipulates the state of the
// variant, such as the implementation of things like assignment and emplace
// operations.
struct VariantCoreAccess {
template <class VariantType>
static typename VariantType::Variant& Derived(VariantType& self) { // NOLINT
return static_cast<typename VariantType::Variant&>(self);
}
template <class VariantType>
static const typename VariantType::Variant& Derived(
const VariantType& self) { // NOLINT
return static_cast<const typename VariantType::Variant&>(self);
}
template <class VariantType>
static void Destroy(VariantType& self) { // NOLINT
Derived(self).destroy();
self.index_ = absl::variant_npos;
}
template <class Variant>
static void SetIndex(Variant& self, std::size_t i) { // NOLINT
self.index_ = i;
}
template <class Variant>
static void InitFrom(Variant& self, Variant&& other) { // NOLINT
variant_internal::visit_indices<absl::variant_size<Variant>::value>(
InitFromVisitor<Variant, Variant&&>{&self,
std::forward<Variant>(other)},
other.index());
self.index_ = other.index();
}
template <std::size_t I, class Variant>
static VariantAccessResult<I, Variant> Access(Variant&& self) {
if (ABSL_PREDICT_FALSE(self.index_ != I)) {
TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
}
// This cast instead of invocation of AccessUnion with an rvalue is a
// workaround for msvc. Without this there is a runtime failure when dealing
// with rvalues.
// TODO(calabrese) Reduce test case and find a simpler workaround.
return static_cast<VariantAccessResult<I, Variant>>(
variant_internal::AccessUnion(self.state_, SizeT<I>()));
}
// The implementation of the move-assignment operation for a variant.
template <class VType>
struct MoveAssignVisitor {
using DerivedType = typename VType::Variant;
template <std::size_t NewIndex>
void operator()(SizeT<NewIndex> /*new_i*/) const {
if (left->index_ == NewIndex) {
Access<NewIndex>(*left) = std::move(Access<NewIndex>(*right));
} else {
Derived(*left).template emplace<NewIndex>(
std::move(Access<NewIndex>(*right)));
}
}
void operator()(SizeT<absl::variant_npos> /*new_i*/) const {
Destroy(*left);
}
VType* left;
VType* right;
};
template <class VType>
static MoveAssignVisitor<VType> MakeMoveAssignVisitor(VType* left,
VType* other) {
return {left, other};
}
// The implementation of the assignment operation for a variant.
template <class VType>
struct CopyAssignVisitor {
using DerivedType = typename VType::Variant;
template <std::size_t NewIndex>
void operator()(SizeT<NewIndex> /*new_i*/) const {
using New =
typename absl::variant_alternative<NewIndex, DerivedType>::type;
if (left->index_ == NewIndex) {
Access<NewIndex>(*left) = Access<NewIndex>(*right);
} else if (std::is_nothrow_copy_constructible<New>::value ||
!std::is_nothrow_move_constructible<New>::value) {
Derived(*left).template emplace<NewIndex>(Access<NewIndex>(*right));
} else {
Derived(*left) = DerivedType(Derived(*right));
}
}
void operator()(SizeT<absl::variant_npos> /*new_i*/) const {
Destroy(*left);
}
VType* left;
const VType* right;
};
template <class VType>
static CopyAssignVisitor<VType> MakeCopyAssignVisitor(VType* left,
const VType& other) {
return {left, &other};
}
// The implementation of conversion-assignment operations for variant.
template <class Left, class QualifiedNew>
struct ConversionAssignVisitor {
using NewIndex =
variant_internal::IndexOfConstructedType<Left, QualifiedNew>;
void operator()(SizeT<NewIndex::value> /*old_i*/
) const {
Access<NewIndex::value>(*left) = absl::forward<QualifiedNew>(other);
}
template <std::size_t OldIndex>
void operator()(SizeT<OldIndex> /*old_i*/
) const {
using New =
typename absl::variant_alternative<NewIndex::value, Left>::type;
if (std::is_nothrow_constructible<New, QualifiedNew>::value ||
!std::is_nothrow_move_constructible<New>::value) {
left->template emplace<NewIndex::value>(
absl::forward<QualifiedNew>(other));
} else {
// the standard says "equivalent to
// operator=(variant(std::forward<T>(t)))", but we use `emplace` here
// because the variant's move assignment operator could be deleted.
left->template emplace<NewIndex::value>(
New(absl::forward<QualifiedNew>(other)));
}
}
Left* left;
QualifiedNew&& other;
};
template <class Left, class QualifiedNew>
static ConversionAssignVisitor<Left, QualifiedNew>
MakeConversionAssignVisitor(Left* left, QualifiedNew&& qual) {
return {left, absl::forward<QualifiedNew>(qual)};
}
// Backend for operations for `emplace()` which destructs `*self` then
// construct a new alternative with `Args...`.
template <std::size_t NewIndex, class Self, class... Args>
static typename absl::variant_alternative<NewIndex, Self>::type& Replace(
Self* self, Args&&... args) {
Destroy(*self);
using New = typename absl::variant_alternative<NewIndex, Self>::type;
New* const result = ::new (static_cast<void*>(&self->state_))
New(absl::forward<Args>(args)...);
self->index_ = NewIndex;
return *result;
}
template <class LeftVariant, class QualifiedRightVariant>
struct InitFromVisitor {
template <std::size_t NewIndex>
void operator()(SizeT<NewIndex> /*new_i*/) const {
using Alternative =
typename variant_alternative<NewIndex, LeftVariant>::type;
::new (static_cast<void*>(&left->state_)) Alternative(
Access<NewIndex>(std::forward<QualifiedRightVariant>(right)));
}
void operator()(SizeT<absl::variant_npos> /*new_i*/) const {
// This space intentionally left blank.
}
LeftVariant* left;
QualifiedRightVariant&& right;
};
};
template <class Expected, class... T>
struct IndexOfImpl;
template <class Expected>
struct IndexOfImpl<Expected> {
using IndexFromEnd = SizeT<0>;
using MatchedIndexFromEnd = IndexFromEnd;
using MultipleMatches = std::false_type;
};
template <class Expected, class Head, class... Tail>
struct IndexOfImpl<Expected, Head, Tail...> : IndexOfImpl<Expected, Tail...> {
using IndexFromEnd =
SizeT<IndexOfImpl<Expected, Tail...>::IndexFromEnd::value + 1>;
};
template <class Expected, class... Tail>
struct IndexOfImpl<Expected, Expected, Tail...>
: IndexOfImpl<Expected, Tail...> {
using IndexFromEnd =
SizeT<IndexOfImpl<Expected, Tail...>::IndexFromEnd::value + 1>;
using MatchedIndexFromEnd = IndexFromEnd;
using MultipleMatches = std::integral_constant<
bool, IndexOfImpl<Expected, Tail...>::MatchedIndexFromEnd::value != 0>;
};
template <class Expected, class... Types>
struct IndexOfMeta {
using Results = IndexOfImpl<Expected, Types...>;
static_assert(!Results::MultipleMatches::value,
"Attempted to access a variant by specifying a type that "
"matches more than one alternative.");
static_assert(Results::MatchedIndexFromEnd::value != 0,
"Attempted to access a variant by specifying a type that does "
"not match any alternative.");
using type = SizeT<sizeof...(Types) - Results::MatchedIndexFromEnd::value>;
};
template <class Expected, class... Types>
using IndexOf = typename IndexOfMeta<Expected, Types...>::type;
template <class Variant, class T, std::size_t CurrIndex>
struct UnambiguousIndexOfImpl;
// Terminating case encountered once we've checked all of the alternatives
template <class T, std::size_t CurrIndex>
struct UnambiguousIndexOfImpl<variant<>, T, CurrIndex> : SizeT<CurrIndex> {};
// Case where T is not Head
template <class Head, class... Tail, class T, std::size_t CurrIndex>
struct UnambiguousIndexOfImpl<variant<Head, Tail...>, T, CurrIndex>
: UnambiguousIndexOfImpl<variant<Tail...>, T, CurrIndex + 1>::type {};
// Case where T is Head
template <class Head, class... Tail, std::size_t CurrIndex>
struct UnambiguousIndexOfImpl<variant<Head, Tail...>, Head, CurrIndex>
: SizeT<UnambiguousIndexOfImpl<variant<Tail...>, Head, 0>::value ==
sizeof...(Tail)
? CurrIndex
: CurrIndex + sizeof...(Tail) + 1> {};
template <class Variant, class T>
struct UnambiguousIndexOf;
struct NoMatch {
struct type {};
};
template <class... Alts, class T>
struct UnambiguousIndexOf<variant<Alts...>, T>
: std::conditional<UnambiguousIndexOfImpl<variant<Alts...>, T, 0>::value !=
sizeof...(Alts),
UnambiguousIndexOfImpl<variant<Alts...>, T, 0>,
NoMatch>::type::type {};
template <class T, std::size_t /*Dummy*/>
using UnambiguousTypeOfImpl = T;
template <class Variant, class T>
using UnambiguousTypeOfT =
UnambiguousTypeOfImpl<T, UnambiguousIndexOf<Variant, T>::value>;
template <class H, class... T>
class VariantStateBase;
// This is an implementation of the "imaginary function" that is described in
// [variant.ctor]
// It is used in order to determine which alternative to construct during
// initialization from some type T.
template <class Variant, std::size_t I = 0>
struct ImaginaryFun;
template <std::size_t I>
struct ImaginaryFun<variant<>, I> {
static void Run() = delete;
};
template <class H, class... T, std::size_t I>
struct ImaginaryFun<variant<H, T...>, I> : ImaginaryFun<variant<T...>, I + 1> {
using ImaginaryFun<variant<T...>, I + 1>::Run;
// NOTE: const& and && are used instead of by-value due to lack of guaranteed
// move elision of C++17. This may have other minor differences, but tests
// pass.
static SizeT<I> Run(const H&);
static SizeT<I> Run(H&&);
};
// The following metafunctions are used in constructor and assignment
// constraints.
template <class Self, class T>
struct IsNeitherSelfNorInPlace : std::true_type {};
template <class Self>
struct IsNeitherSelfNorInPlace<Self, Self> : std::false_type {};
template <class Self, class T>
struct IsNeitherSelfNorInPlace<Self, in_place_type_t<T>> : std::false_type {};
template <class Self, std::size_t I>
struct IsNeitherSelfNorInPlace<Self, in_place_index_t<I>> : std::false_type {};
template <class Variant, class T, class = void>
struct ConversionIsPossibleImpl : std::false_type {};
template <class Variant, class T>
struct ConversionIsPossibleImpl<
Variant, T, void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>()))>>
: std::true_type {};
template <class Variant, class T>
struct ConversionIsPossible : ConversionIsPossibleImpl<Variant, T>::type {};
template <class Variant, class T>
struct IndexOfConstructedType<
Variant, T, void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>()))>>
: decltype(ImaginaryFun<Variant>::Run(std::declval<T>())) {};
template <std::size_t... Is>
struct ContainsVariantNPos
: absl::negation<std::is_same< // NOLINT
absl::integer_sequence<bool, 0 <= Is...>,
absl::integer_sequence<bool, Is != absl::variant_npos...>>> {};
template <class Op, class... QualifiedVariants>
using RawVisitResult =
absl::result_of_t<Op(VariantAccessResult<0, QualifiedVariants>...)>;
// NOTE: The spec requires that all return-paths yield the same type and is not
// SFINAE-friendly, so we can deduce the return type by examining the first
// result. If it's not callable, then we get an error, but are compliant and
// fast to compile.
// TODO(calabrese) Possibly rewrite in a way that yields better compile errors
// at the cost of longer compile-times.
template <class Op, class... QualifiedVariants>
struct VisitResultImpl {
using type =
absl::result_of_t<Op(VariantAccessResult<0, QualifiedVariants>...)>;
};
// Done in two steps intentionally so that we don't cause substitution to fail.
template <class Op, class... QualifiedVariants>
using VisitResult = typename VisitResultImpl<Op, QualifiedVariants...>::type;
template <class Op, class... QualifiedVariants>
struct PerformVisitation {
using ReturnType = VisitResult<Op, QualifiedVariants...>;
template <std::size_t... Is>
constexpr ReturnType operator()(SizeT<Is>... indices) const {
return Run(typename ContainsVariantNPos<Is...>::type{},
absl::index_sequence_for<QualifiedVariants...>(), indices...);
}
template <std::size_t... TupIs, std::size_t... Is>
constexpr ReturnType Run(std::false_type /*has_valueless*/,
index_sequence<TupIs...>, SizeT<Is>...) const {
return absl::base_internal::Invoke(
absl::forward<Op>(op),
VariantCoreAccess::Access<Is>(
absl::forward<QualifiedVariants>(std::get<TupIs>(variant_tup)))...);
}
template <std::size_t... TupIs, std::size_t... Is>
[[noreturn]] ReturnType Run(std::true_type /*has_valueless*/,
index_sequence<TupIs...>, SizeT<Is>...) const {
absl::variant_internal::ThrowBadVariantAccess();
}
// TODO(calabrese) Avoid using a tuple, which causes lots of instantiations
// Attempts using lambda variadic captures fail on current GCC.
std::tuple<QualifiedVariants&&...> variant_tup;
Op&& op;
};
template <class... T>
union Union;
// We want to allow for variant<> to be trivial. For that, we need the default
// constructor to be trivial, which means we can't define it ourselves.
// Instead, we use a non-default constructor that takes NoopConstructorTag
// that doesn't affect the triviality of the types.
struct NoopConstructorTag {};
template <std::size_t I>
struct EmplaceTag {};
template <>
union Union<> {
constexpr explicit Union(NoopConstructorTag) noexcept {}
};
// Suppress bogus warning on MSVC: MSVC complains that Union<T...> has a defined
// deleted destructor from the `std::is_destructible` check below.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4624)
#endif // _MSC_VER
template <class Head, class... Tail>
union Union<Head, Tail...> {
using TailUnion = Union<Tail...>;
explicit constexpr Union(NoopConstructorTag /*tag*/) noexcept
: tail(NoopConstructorTag()) {}
template <class... P>
explicit constexpr Union(EmplaceTag<0>, P&&... args)
: head(absl::forward<P>(args)...) {}
template <std::size_t I, class... P>
explicit constexpr Union(EmplaceTag<I>, P&&... args)
: tail(EmplaceTag<I - 1>{}, absl::forward<P>(args)...) {}
Head head;
TailUnion tail;
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
// TODO(calabrese) Just contain a Union in this union (certain configs fail).
template <class... T>
union DestructibleUnionImpl;
template <>
union DestructibleUnionImpl<> {
constexpr explicit DestructibleUnionImpl(NoopConstructorTag) noexcept {}
};
template <class Head, class... Tail>
union DestructibleUnionImpl<Head, Tail...> {
using TailUnion = DestructibleUnionImpl<Tail...>;
explicit constexpr DestructibleUnionImpl(NoopConstructorTag /*tag*/) noexcept
: tail(NoopConstructorTag()) {}
template <class... P>
explicit constexpr DestructibleUnionImpl(EmplaceTag<0>, P&&... args)
: head(absl::forward<P>(args)...) {}
template <std::size_t I, class... P>
explicit constexpr DestructibleUnionImpl(EmplaceTag<I>, P&&... args)
: tail(EmplaceTag<I - 1>{}, absl::forward<P>(args)...) {}
~DestructibleUnionImpl() {}
Head head;
TailUnion tail;
};
// This union type is destructible even if one or more T are not trivially
// destructible. In the case that all T are trivially destructible, then so is
// this resultant type.
template <class... T>
using DestructibleUnion =
absl::conditional_t<std::is_destructible<Union<T...>>::value, Union<T...>,
DestructibleUnionImpl<T...>>;
// Deepest base, containing the actual union and the discriminator
template <class H, class... T>
class VariantStateBase {
protected:
using Variant = variant<H, T...>;
template <class LazyH = H,
class ConstructibleH = absl::enable_if_t<
std::is_default_constructible<LazyH>::value, LazyH>>
constexpr VariantStateBase() noexcept(
std::is_nothrow_default_constructible<ConstructibleH>::value)
: state_(EmplaceTag<0>()), index_(0) {}
template <std::size_t I, class... P>
explicit constexpr VariantStateBase(EmplaceTag<I> tag, P&&... args)
: state_(tag, absl::forward<P>(args)...), index_(I) {}
explicit constexpr VariantStateBase(NoopConstructorTag)
: state_(NoopConstructorTag()), index_(variant_npos) {}
void destroy() {} // Does nothing (shadowed in child if non-trivial)
DestructibleUnion<H, T...> state_;
std::size_t index_;
};
using absl::internal::identity;
// OverloadSet::Overload() is a unary function which is overloaded to
// take any of the element types of the variant, by reference-to-const.
// The return type of the overload on T is identity<T>, so that you
// can statically determine which overload was called.
//
// Overload() is not defined, so it can only be called in unevaluated
// contexts.
template <typename... Ts>
struct OverloadSet;
template <typename T, typename... Ts>
struct OverloadSet<T, Ts...> : OverloadSet<Ts...> {
using Base = OverloadSet<Ts...>;
static identity<T> Overload(const T&);
using Base::Overload;
};
template <>
struct OverloadSet<> {
// For any case not handled above.
static void Overload(...);
};
////////////////////////////////
// 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 Enabler, class To, template <class...> class Op, class... Args>
struct is_detected_convertible_impl {
using type = std::false_type;
};
template <class To, template <class...> class Op, class... Args>
struct is_detected_convertible_impl<
absl::enable_if_t<std::is_convertible<Op<Args...>, To>::value>, To, Op,
Args...> {
using type = std::true_type;
};
// NOTE: This differs from library fundamentals by being lazy.
template <class To, template <class...> class Op, class... Args>
struct is_detected_convertible
: is_detected_convertible_impl<void, To, Op, Args...>::type {};
template <class T>
using LessThanResult = decltype(std::declval<T>() < std::declval<T>());
template <class T>
using GreaterThanResult = decltype(std::declval<T>() > std::declval<T>());
template <class T>
using LessThanOrEqualResult = decltype(std::declval<T>() <= std::declval<T>());
template <class T>
using GreaterThanOrEqualResult =
decltype(std::declval<T>() >= std::declval<T>());
template <class T>
using EqualResult = decltype(std::declval<T>() == std::declval<T>());
template <class T>
using NotEqualResult = decltype(std::declval<T>() != std::declval<T>());
template <class T>
using HasLessThan = is_detected_convertible<bool, LessThanResult, T>;
template <class T>
using HasGreaterThan = is_detected_convertible<bool, GreaterThanResult, T>;
template <class T>
using HasLessThanOrEqual =
is_detected_convertible<bool, LessThanOrEqualResult, T>;
template <class T>
using HasGreaterThanOrEqual =
is_detected_convertible<bool, GreaterThanOrEqualResult, T>;
template <class T>
using HasEqual = is_detected_convertible<bool, EqualResult, T>;
template <class T>
using HasNotEqual = is_detected_convertible<bool, NotEqualResult, T>;
template <class... T>
using RequireAllHaveEqualT =
absl::enable_if_t<absl::conjunction<HasEqual<T>...>::value, bool>;
template <class... T>
using RequireAllHaveNotEqualT =
absl::enable_if_t<absl::conjunction<HasEqual<T>...>::value, bool>;
template <class... T>
using RequireAllHaveLessThanT =
absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
template <class... T>
using RequireAllHaveLessThanOrEqualT =
absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
template <class... T>
using RequireAllHaveGreaterThanOrEqualT =
absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
template <class... T>
using RequireAllHaveGreaterThanT =
absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
// Helper template containing implementations details of variant that can't go
// in the private section. For convenience, this takes the variant type as a
// single template parameter.
template <typename T>
struct VariantHelper;
template <typename... Ts>
struct VariantHelper<variant<Ts...>> {
// Type metafunction which returns the element type selected if
// OverloadSet::Overload() is well-formed when called with argument type U.
template <typename U>
using BestMatch = decltype(
variant_internal::OverloadSet<Ts...>::Overload(std::declval<U>()));
// Type metafunction which returns true if OverloadSet::Overload() is
// well-formed when called with argument type U.
// CanAccept can't be just an alias because there is a MSVC bug on parameter
// pack expansion involving decltype.
template <typename U>
struct CanAccept :
std::integral_constant<bool, !std::is_void<BestMatch<U>>::value> {};
// Type metafunction which returns true if Other is an instantiation of
// variant, and variants's converting constructor from Other will be
// well-formed. We will use this to remove constructors that would be
// ill-formed from the overload set.
template <typename Other>
struct CanConvertFrom;
template <typename... Us>
struct CanConvertFrom<variant<Us...>>
: public absl::conjunction<CanAccept<Us>...> {};
};
// A type with nontrivial copy ctor and trivial move ctor.
struct TrivialMoveOnly {
TrivialMoveOnly(TrivialMoveOnly&&) = default;
};
// Trait class to detect whether a type is trivially move constructible.
// A union's defaulted copy/move constructor is deleted if any variant member's
// copy/move constructor is nontrivial.
template <typename T>
struct IsTriviallyMoveConstructible:
std::is_move_constructible<Union<T, TrivialMoveOnly>> {};
// To guarantee triviality of all special-member functions that can be trivial,
// we use a chain of conditional bases for each one.
// The order of inheritance of bases from child to base are logically:
//
// variant
// VariantCopyAssignBase
// VariantMoveAssignBase
// VariantCopyBase
// VariantMoveBase
// VariantStateBaseDestructor
// VariantStateBase
//
// Note that there is a separate branch at each base that is dependent on
// whether or not that corresponding special-member-function can be trivial in
// the resultant variant type.
template <class... T>
class VariantStateBaseDestructorNontrivial;
template <class... T>
class VariantMoveBaseNontrivial;
template <class... T>
class VariantCopyBaseNontrivial;
template <class... T>
class VariantMoveAssignBaseNontrivial;
template <class... T>
class VariantCopyAssignBaseNontrivial;
// Base that is dependent on whether or not the destructor can be trivial.
template <class... T>
using VariantStateBaseDestructor =
absl::conditional_t<std::is_destructible<Union<T...>>::value,
VariantStateBase<T...>,
VariantStateBaseDestructorNontrivial<T...>>;
// Base that is dependent on whether or not the move-constructor can be
// implicitly generated by the compiler (trivial or deleted).
// Previously we were using `std::is_move_constructible<Union<T...>>` to check
// whether all Ts have trivial move constructor, but it ran into a GCC bug:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84866
// So we have to use a different approach (i.e. `HasTrivialMoveConstructor`) to
// work around the bug.
template <class... T>
using VariantMoveBase = absl::conditional_t<
absl::disjunction<
absl::negation<absl::conjunction<std::is_move_constructible<T>...>>,
absl::conjunction<IsTriviallyMoveConstructible<T>...>>::value,
VariantStateBaseDestructor<T...>, VariantMoveBaseNontrivial<T...>>;
// Base that is dependent on whether or not the copy-constructor can be trivial.
template <class... T>
using VariantCopyBase = absl::conditional_t<
absl::disjunction<
absl::negation<absl::conjunction<std::is_copy_constructible<T>...>>,
std::is_copy_constructible<Union<T...>>>::value,
VariantMoveBase<T...>, VariantCopyBaseNontrivial<T...>>;
// Base that is dependent on whether or not the move-assign can be trivial.
template <class... T>
using VariantMoveAssignBase = absl::conditional_t<
absl::disjunction<absl::conjunction<std::is_move_assignable<Union<T...>>,
std::is_move_constructible<Union<T...>>,
std::is_destructible<Union<T...>>>,
absl::negation<absl::conjunction<
std::is_move_constructible<T>...,
std::is_move_assignable<T>...>>>::value,
VariantCopyBase<T...>, VariantMoveAssignBaseNontrivial<T...>>;
// Base that is dependent on whether or not the copy-assign can be trivial.
template <class... T>
using VariantCopyAssignBase = absl::conditional_t<
absl::disjunction<absl::conjunction<std::is_copy_assignable<Union<T...>>,
std::is_copy_constructible<Union<T...>>,
std::is_destructible<Union<T...>>>,
absl::negation<absl::conjunction<
std::is_copy_constructible<T>...,
std::is_copy_assignable<T>...>>>::value,
VariantMoveAssignBase<T...>, VariantCopyAssignBaseNontrivial<T...>>;
template <class... T>
using VariantBase = VariantCopyAssignBase<T...>;
template <class... T>
class VariantStateBaseDestructorNontrivial : protected VariantStateBase<T...> {
private:
using Base = VariantStateBase<T...>;
protected:
using Base::Base;
VariantStateBaseDestructorNontrivial() = default;
VariantStateBaseDestructorNontrivial(VariantStateBaseDestructorNontrivial&&) =
default;
VariantStateBaseDestructorNontrivial(
const VariantStateBaseDestructorNontrivial&) = default;
VariantStateBaseDestructorNontrivial& operator=(
VariantStateBaseDestructorNontrivial&&) = default;
VariantStateBaseDestructorNontrivial& operator=(
const VariantStateBaseDestructorNontrivial&) = default;
struct Destroyer {
template <std::size_t I>
void operator()(SizeT<I> i) const {
using Alternative =
typename absl::variant_alternative<I, variant<T...>>::type;
variant_internal::AccessUnion(self->state_, i).~Alternative();
}
void operator()(SizeT<absl::variant_npos> /*i*/) const {
// This space intentionally left blank
}
VariantStateBaseDestructorNontrivial* self;
};
void destroy() {
variant_internal::visit_indices<sizeof...(T)>(Destroyer{this}, index_);
}
~VariantStateBaseDestructorNontrivial() { destroy(); }
protected:
using Base::index_;
using Base::state_;
};
template <class... T>
class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor<T...> {
private:
using Base = VariantStateBaseDestructor<T...>;
protected:
using Base::Base;
struct Construct {
template <std::size_t I>
void operator()(SizeT<I> i) const {
using Alternative =
typename absl::variant_alternative<I, variant<T...>>::type;
::new (static_cast<void*>(&self->state_)) Alternative(
variant_internal::AccessUnion(absl::move(other->state_), i));
}
void operator()(SizeT<absl::variant_npos> /*i*/) const {}
VariantMoveBaseNontrivial* self;
VariantMoveBaseNontrivial* other;
};
VariantMoveBaseNontrivial() = default;
VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept(
absl::conjunction<std::is_nothrow_move_constructible<T>...>::value)
: Base(NoopConstructorTag()) {
variant_internal::visit_indices<sizeof...(T)>(Construct{this, &other},
other.index_);
index_ = other.index_;
}
VariantMoveBaseNontrivial(VariantMoveBaseNontrivial const&) = default;
VariantMoveBaseNontrivial& operator=(VariantMoveBaseNontrivial&&) = default;
VariantMoveBaseNontrivial& operator=(VariantMoveBaseNontrivial const&) =
default;
protected:
using Base::index_;
using Base::state_;
};
template <class... T>
class VariantCopyBaseNontrivial : protected VariantMoveBase<T...> {
private:
using Base = VariantMoveBase<T...>;
protected:
using Base::Base;
VariantCopyBaseNontrivial() = default;
VariantCopyBaseNontrivial(VariantCopyBaseNontrivial&&) = default;
struct Construct {
template <std::size_t I>
void operator()(SizeT<I> i) const {
using Alternative =
typename absl::variant_alternative<I, variant<T...>>::type;
::new (static_cast<void*>(&self->state_))
Alternative(variant_internal::AccessUnion(other->state_, i));
}
void operator()(SizeT<absl::variant_npos> /*i*/) const {}
VariantCopyBaseNontrivial* self;
const VariantCopyBaseNontrivial* other;
};
VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other)
: Base(NoopConstructorTag()) {
variant_internal::visit_indices<sizeof...(T)>(Construct{this, &other},
other.index_);
index_ = other.index_;
}
VariantCopyBaseNontrivial& operator=(VariantCopyBaseNontrivial&&) = default;
VariantCopyBaseNontrivial& operator=(VariantCopyBaseNontrivial const&) =
default;
protected:
using Base::index_;
using Base::state_;
};
template <class... T>
class VariantMoveAssignBaseNontrivial : protected VariantCopyBase<T...> {
friend struct VariantCoreAccess;
private:
using Base = VariantCopyBase<T...>;
protected:
using Base::Base;
VariantMoveAssignBaseNontrivial() = default;
VariantMoveAssignBaseNontrivial(VariantMoveAssignBaseNontrivial&&) = default;
VariantMoveAssignBaseNontrivial(const VariantMoveAssignBaseNontrivial&) =
default;
VariantMoveAssignBaseNontrivial& operator=(
VariantMoveAssignBaseNontrivial const&) = default;
VariantMoveAssignBaseNontrivial&
operator=(VariantMoveAssignBaseNontrivial&& other) noexcept(
absl::conjunction<std::is_nothrow_move_constructible<T>...,
std::is_nothrow_move_assignable<T>...>::value) {
variant_internal::visit_indices<sizeof...(T)>(
VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_);
return *this;
}
protected:
using Base::index_;
using Base::state_;
};
template <class... T>
class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase<T...> {
friend struct VariantCoreAccess;
private:
using Base = VariantMoveAssignBase<T...>;
protected:
using Base::Base;
VariantCopyAssignBaseNontrivial() = default;
VariantCopyAssignBaseNontrivial(VariantCopyAssignBaseNontrivial&&) = default;
VariantCopyAssignBaseNontrivial(const VariantCopyAssignBaseNontrivial&) =
default;
VariantCopyAssignBaseNontrivial& operator=(
VariantCopyAssignBaseNontrivial&&) = default;
VariantCopyAssignBaseNontrivial& operator=(
const VariantCopyAssignBaseNontrivial& other) {
variant_internal::visit_indices<sizeof...(T)>(
VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_);
return *this;
}
protected:
using Base::index_;
using Base::state_;
};
////////////////////////////////////////
// Visitors for Comparison Operations //
////////////////////////////////////////
template <class... Types>
struct EqualsOp {
const variant<Types...>* v;
const variant<Types...>* w;
constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
return true;
}
template <std::size_t I>
constexpr bool operator()(SizeT<I> /*v_i*/) const {
return VariantCoreAccess::Access<I>(*v) == VariantCoreAccess::Access<I>(*w);
}
};
template <class... Types>
struct NotEqualsOp {
const variant<Types...>* v;
const variant<Types...>* w;
constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
return false;
}
template <std::size_t I>
constexpr bool operator()(SizeT<I> /*v_i*/) const {
return VariantCoreAccess::Access<I>(*v) != VariantCoreAccess::Access<I>(*w);
}
};
template <class... Types>
struct LessThanOp {
const variant<Types...>* v;
const variant<Types...>* w;
constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
return false;
}
template <std::size_t I>
constexpr bool operator()(SizeT<I> /*v_i*/) const {
return VariantCoreAccess::Access<I>(*v) < VariantCoreAccess::Access<I>(*w);
}
};
template <class... Types>
struct GreaterThanOp {
const variant<Types...>* v;
const variant<Types...>* w;
constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
return false;
}
template <std::size_t I>
constexpr bool operator()(SizeT<I> /*v_i*/) const {
return VariantCoreAccess::Access<I>(*v) > VariantCoreAccess::Access<I>(*w);
}
};
template <class... Types>
struct LessThanOrEqualsOp {
const variant<Types...>* v;
const variant<Types...>* w;
constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
return true;
}
template <std::size_t I>
constexpr bool operator()(SizeT<I> /*v_i*/) const {
return VariantCoreAccess::Access<I>(*v) <= VariantCoreAccess::Access<I>(*w);
}
};
template <class... Types>
struct GreaterThanOrEqualsOp {
const variant<Types...>* v;
const variant<Types...>* w;
constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
return true;
}
template <std::size_t I>
constexpr bool operator()(SizeT<I> /*v_i*/) const {
return VariantCoreAccess::Access<I>(*v) >= VariantCoreAccess::Access<I>(*w);
}
};
// Precondition: v.index() == w.index();
template <class... Types>
struct SwapSameIndex {
variant<Types...>* v;
variant<Types...>* w;
template <std::size_t I>
void operator()(SizeT<I>) const {
using std::swap;
swap(VariantCoreAccess::Access<I>(*v), VariantCoreAccess::Access<I>(*w));
}
void operator()(SizeT<variant_npos>) const {}
};
// TODO(calabrese) do this from a different namespace for proper adl usage
template <class... Types>
struct Swap {
variant<Types...>* v;
variant<Types...>* w;
void generic_swap() const {
variant<Types...> tmp(std::move(*w));
VariantCoreAccess::Destroy(*w);
VariantCoreAccess::InitFrom(*w, std::move(*v));
VariantCoreAccess::Destroy(*v);
VariantCoreAccess::InitFrom(*v, std::move(tmp));
}
void operator()(SizeT<absl::variant_npos> /*w_i*/) const {
if (!v->valueless_by_exception()) {
generic_swap();
}
}
template <std::size_t Wi>
void operator()(SizeT<Wi> /*w_i*/) {
if (v->index() == Wi) {
visit_indices<sizeof...(Types)>(SwapSameIndex<Types...>{v, w}, Wi);
} else {
generic_swap();
}
}
};
template <typename Variant, typename = void, typename... Ts>
struct VariantHashBase {
VariantHashBase() = delete;
VariantHashBase(const VariantHashBase&) = delete;
VariantHashBase(VariantHashBase&&) = delete;
VariantHashBase& operator=(const VariantHashBase&) = delete;
VariantHashBase& operator=(VariantHashBase&&) = delete;
};
struct VariantHashVisitor {
template <typename T>
size_t operator()(const T& t) {
return std::hash<T>{}(t);
}
};
template <typename Variant, typename... Ts>
struct VariantHashBase<Variant,
absl::enable_if_t<absl::conjunction<
type_traits_internal::IsHashEnabled<Ts>...>::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<variant_size<Variant>::value>(
PerformVisitation<VariantHashVisitor, const Variant&>{
std::forward_as_tuple(var), VariantHashVisitor{}},
var.index());
// Combine the index and the hash result in order to distinguish
// std::variant<int, int> holding the same value as different alternative.
return result ^ var.index();
}
};
} // namespace variant_internal
} // namespace absl
#endif // ABSL_TYPES_variant_internal_H_
//
// 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<T>` holds either a value of `T` or an
// "empty" value. When it holds a value of `T`, it stores it as a direct
......
// 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 <variant>
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 <functional>
#include <new>
#include <type_traits>
#include <utility>
#include "absl/base/macros.h"
#include "absl/base/port.h"
#include "absl/meta/type_traits.h"
#include "absl/types/internal/variant.h"
namespace absl {
// -----------------------------------------------------------------------------
// absl::variant
// -----------------------------------------------------------------------------
//
// An 'absl::variant` type is a form of type-safe union. An `absl::variant` --
// except in exceptional cases -- always holds a value of one of its alternative
// types.
//
// Example:
//
// // Construct a variant that holds either an integer or a std::string and
// // assign it to a std::string.
// absl::variant<int, std::string> v = std::string("abc");
//
// // A default-contructed variant will hold a value-initialized value of
// // the first alternative type.
// auto a = absl::variant<int, std::string>(); // Holds an int of value '0'.
//
// // variants are assignable.
//
// // copy assignment
// auto v1 = absl::variant<int, std::string>("abc");
// auto v2 = absl::variant<int, std::string>(10);
// v2 = v1; // copy assign
//
// // move assignment
// auto v1 = absl::variant<int, std::string>("abc");
// v1 = absl::variant<int, std::string>(10);
//
// // assignment through type conversion
// a = 128; // variant contains int
// a = "128"; // variant contains std::string
//
// An `absl::variant` holding a value of one of its alternative types `T` holds
// an allocation of `T` directly within the variant itself. An `absl::variant`
// is not allowed to allocate additional storage, such as dynamic memory, to
// allocate the contained value. The contained value shall be allocated in a
// region of the variant storage suitably aligned for all alternative types.
template <typename... Ts>
class variant;
// swap()
//
// Swaps two `absl::variant` values. This function is equivalent to `v.swap(w)`
// where `v` and `w` are `absl::variant` types.
//
// Note that this function requires all alternative types to be both swappable
// and move-constructible, because any two variants may refer to either the same
// type (in which case, they will be swapped) or to two different types (in
// which case the values will need to be moved).
//
template <typename... Ts>
void swap(variant<Ts...>& v, variant<Ts...>& 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<int, std::string>;
// constexpr int num_types =
// absl::variant_size<absl::variant<int, std::string>>();
//
// // You can also use the member constant `value`.
// constexpr int num_types =
// absl::variant_size<absl::variant<int, std::string>>::value;
//
// // `absl::variant_size` is more valuable for use in generic code:
// template <typename Variant>
// constexpr bool IsVariantMultivalue() {
// return absl::variant_size<Variant>() > 1;
// }
//
// Note that the set of cv-qualified specializations of `variant_size` are
// provided to ensure that those specializations compile (especially when passed
// within template logic).
template <class T>
struct variant_size;
template <class... Ts>
struct variant_size<variant<Ts...>>
: std::integral_constant<std::size_t, sizeof...(Ts)> {};
// Specialization of `variant_size` for const qualified variants.
template <class T>
struct variant_size<const T> : variant_size<T>::type {};
// Specialization of `variant_size` for volatile qualified variants.
template <class T>
struct variant_size<volatile T> : variant_size<T>::type {};
// Specialization of `variant_size` for const volatile qualified variants.
template <class T>
struct variant_size<const volatile T> : variant_size<T>::type {};
// variant_alternative
//
// Returns the alternative type for a given `absl::variant` at the passed
// index value as a compile-time constant expression. As this is a class
// template resulting in a type, it is not useful for access of the run-time
// value of any given `absl::variant` variable.
//
// Example:
//
// // The type of the 0th alternative is "int".
// using alternative_type_0
// = absl::variant_alternative<0, absl::variant<int, std::string>>::type;
//
// static_assert(std::is_same<alternative_type_0, int>::value, "");
//
// // `absl::variant_alternative` is more valuable for use in generic code:
// template <typename Variant>
// constexpr bool IsFirstElementTrivial() {
// return std::is_trivial_v<variant_alternative<0, Variant>::type>;
// }
//
// Note that the set of cv-qualified specializations of `variant_alternative`
// are provided to ensure that those specializations compile (especially when
// passed within template logic).
template <std::size_t I, class T>
struct variant_alternative;
template <std::size_t I, class... Types>
struct variant_alternative<I, variant<Types...>> {
using type =
variant_internal::VariantAlternativeSfinaeT<I, variant<Types...>>;
};
// Specialization of `variant_alternative` for const qualified variants.
template <std::size_t I, class T>
struct variant_alternative<I, const T> {
using type = const typename variant_alternative<I, T>::type;
};
// Specialization of `variant_alternative` for volatile qualified variants.
template <std::size_t I, class T>
struct variant_alternative<I, volatile T> {
using type = volatile typename variant_alternative<I, T>::type;
};
// Specialization of `variant_alternative` for const volatile qualified
// variants.
template <std::size_t I, class T>
struct variant_alternative<I, const volatile T> {
using type = const volatile typename variant_alternative<I, T>::type;
};
// Template type alias for variant_alternative<I, T>::type.
//
// Example:
//
// using alternative_type_0
// = absl::variant_alternative_t<0, absl::variant<int, std::string>>;
// static_assert(std::is_same<alternative_type_0, int>::value, "");
template <std::size_t I, class T>
using variant_alternative_t = typename variant_alternative<I, T>::type;
// holds_alternative()
//
// Checks whether the given variant currently holds a given alternative type,
// returning `true` if so.
//
// Example:
//
// absl::variant<int, std::string> bar = 42;
// if (absl::holds_alternative<int>(foo)) {
// std::cout << "The variant holds an integer";
// }
template <class T, class... Types>
constexpr bool holds_alternative(const variant<Types...>& v) noexcept {
static_assert(
variant_internal::UnambiguousIndexOfImpl<variant<Types...>, T,
0>::value != sizeof...(Types),
"The type T must occur exactly once in Types...");
return v.index() ==
variant_internal::UnambiguousIndexOf<variant<Types...>, T>::value;
}
// get()
//
// Returns a reference to the value currently within a given variant, using
// either a unique alternative type amongst the variant's set of alternative
// types, or the variant's index value. Attempting to get a variant's value
// using a type that is not unique within the variant's set of alternative types
// is a compile-time error. If the index of the alternative being specified is
// different from the index of the alternative that is currently stored, throws
// `absl::bad_variant_access`.
//
// Example:
//
// auto a = absl::variant<int, std::string>;
//
// // Get the value by type (if unique).
// int i = absl::get<int>(a);
//
// auto b = absl::variant<int, int>;
//
// // Getting the value by a type that is not unique is ill-formed.
// int j = absl::get<int>(b); // Compile Error!
//
// // Getting value by index not ambiguous and allowed.
// int k = absl::get<1>(b);
// Overload for getting a variant's lvalue by type.
template <class T, class... Types>
constexpr T& get(variant<Types...>& v) { // NOLINT
return variant_internal::VariantCoreAccess::Access<
variant_internal::IndexOf<T, Types...>::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 <class T, class... Types>
constexpr T&& get(variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<
variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
}
// Overload for getting a variant's const lvalue by type.
template <class T, class... Types>
constexpr const T& get(const variant<Types...>& v) {
return variant_internal::VariantCoreAccess::Access<
variant_internal::IndexOf<T, Types...>::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 <class T, class... Types>
constexpr const T&& get(const variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<
variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
}
// Overload for getting a variant's lvalue by index.
template <std::size_t I, class... Types>
constexpr variant_alternative_t<I, variant<Types...>>& get(
variant<Types...>& v) { // NOLINT
return variant_internal::VariantCoreAccess::Access<I>(v);
}
// Overload for getting a variant's rvalue by index.
// Note: `absl::move()` is required to allow use of constexpr in C++11.
template <std::size_t I, class... Types>
constexpr variant_alternative_t<I, variant<Types...>>&& get(
variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
}
// Overload for getting a variant's const lvalue by index.
template <std::size_t I, class... Types>
constexpr const variant_alternative_t<I, variant<Types...>>& get(
const variant<Types...>& v) {
return variant_internal::VariantCoreAccess::Access<I>(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 <std::size_t I, class... Types>
constexpr const variant_alternative_t<I, variant<Types...>>&& get(
const variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<I>(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 <std::size_t I, class... Types>
constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>>
get_if(variant<Types...>* v) noexcept {
return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
: nullptr;
}
// Overload for getting a pointer to the const value stored in the given
// variant by index.
template <std::size_t I, class... Types>
constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
get_if(const variant<Types...>* v) noexcept {
return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
: nullptr;
}
// Overload for getting a pointer to the value stored in the given variant by
// type.
template <class T, class... Types>
constexpr absl::add_pointer_t<T> get_if(variant<Types...>* v) noexcept {
return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v);
}
// Overload for getting a pointer to the const value stored in the given variant
// by type.
template <class T, class... Types>
constexpr absl::add_pointer_t<const T> get_if(
const variant<Types...>* v) noexcept {
return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v);
}
// visit()
//
// Calls a provided functor on a given set of variants. `absl::visit()` is
// commonly used to conditionally inspect the state of a given variant (or set
// of variants).
// 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<typename T>
// void operator()(const T& i) const {
// std::cout << "The variant's value is: " << i;
// }
// };
//
// // Declare our variant, and call `absl::visit()` on it.
// std::variant<int, std::string> foo = std::string("foo");
// GetVariant visitor;
// std::visit(visitor, foo); // Prints `The variant's value is: foo'
template <typename Visitor, typename... Variants>
variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
Variants&&... vars) {
return variant_internal::visit_indices<
variant_size<absl::decay_t<Variants>>::value...>(
variant_internal::PerformVisitation<Visitor, Variants...>{
std::forward_as_tuple(absl::forward<Variants>(vars)...),
absl::forward<Visitor>(vis)},
vars.index()...);
}
// monostate
//
// The monostate class serves as a first alternative type for a variant for
// which the first variant type is otherwise not default-constructible.
struct monostate {};
// `absl::monostate` Relational Operators
constexpr bool operator<(monostate, monostate) noexcept { return false; }
constexpr bool operator>(monostate, monostate) noexcept { return false; }
constexpr bool operator<=(monostate, monostate) noexcept { return true; }
constexpr bool operator>=(monostate, monostate) noexcept { return true; }
constexpr bool operator==(monostate, monostate) noexcept { return true; }
constexpr bool operator!=(monostate, monostate) noexcept { return false; }
//------------------------------------------------------------------------------
// `absl::variant` Template Definition
//------------------------------------------------------------------------------
template <typename T0, typename... Tn>
class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
static_assert(absl::conjunction<std::is_object<T0>, std::is_object<Tn>...,
absl::negation<std::is_array<T0>>,
absl::negation<std::is_array<Tn>>...,
std::is_nothrow_destructible<T0>,
std::is_nothrow_destructible<Tn>...>::value,
"Attempted to instantiate a variant with an unsupported type.");
friend struct variant_internal::VariantCoreAccess;
private:
using Base = variant_internal::VariantBase<T0, Tn...>;
public:
// Constructors
// Constructs a variant holding a default-initialized value of the first
// alternative type.
constexpr variant() /*noexcept(see 111above)*/ = default;
// Copy constructor, standard semantics
variant(const variant& other) = default;
// Move constructor, standard semantics
variant(variant&& other) /*noexcept(see above)*/ = default;
// Constructs a variant of an alternative type specified by overload
// resolution of the provided forwarding arguments through
// direct-initialization.
//
// Note: If the selected constructor is a constexpr constructor, this
// constructor shall be a constexpr constructor.
//
// NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html
// has been voted passed the design phase in the C++ standard meeting in Mar
// 2018. It will be implemented and integrated into `absl::variant`.
template <
class T,
std::size_t I = std::enable_if<
variant_internal::IsNeitherSelfNorInPlace<variant,
absl::decay_t<T>>::value,
variant_internal::IndexOfConstructedType<variant, T>>::type::value,
class Tj = absl::variant_alternative_t<I, variant>,
absl::enable_if_t<std::is_constructible<Tj, T>::value>* =
nullptr>
constexpr variant(T&& t) noexcept(std::is_nothrow_constructible<Tj, T>::value)
: Base(variant_internal::EmplaceTag<I>(), absl::forward<T>(t)) {}
// Constructs a variant of an alternative type from the arguments through
// direct-initialization.
//
// Note: If the selected constructor is a constexpr constructor, this
// constructor shall be a constexpr constructor.
template <class T, class... Args,
typename std::enable_if<std::is_constructible<
variant_internal::UnambiguousTypeOfT<variant, T>,
Args...>::value>::type* = nullptr>
constexpr explicit variant(in_place_type_t<T>, Args&&... args)
: Base(variant_internal::EmplaceTag<
variant_internal::UnambiguousIndexOf<variant, T>::value>(),
absl::forward<Args>(args)...) {}
// Constructs a variant of an alternative type from an initializer list
// and other arguments through direct-initialization.
//
// Note: If the selected constructor is a constexpr constructor, this
// constructor shall be a constexpr constructor.
template <class T, class U, class... Args,
typename std::enable_if<std::is_constructible<
variant_internal::UnambiguousTypeOfT<variant, T>,
std::initializer_list<U>&, Args...>::value>::type* = nullptr>
constexpr explicit variant(in_place_type_t<T>, std::initializer_list<U> il,
Args&&... args)
: Base(variant_internal::EmplaceTag<
variant_internal::UnambiguousIndexOf<variant, T>::value>(),
il, absl::forward<Args>(args)...) {}
// Constructs a variant of an alternative type from a provided index,
// through value-initialization using the provided forwarded arguments.
template <std::size_t I, class... Args,
typename std::enable_if<std::is_constructible<
variant_internal::VariantAlternativeSfinaeT<I, variant>,
Args...>::value>::type* = nullptr>
constexpr explicit variant(in_place_index_t<I>, Args&&... args)
: Base(variant_internal::EmplaceTag<I>(), absl::forward<Args>(args)...) {}
// Constructs a variant of an alternative type from a provided index,
// through value-initialization of an initializer list and the provided
// forwarded arguments.
template <std::size_t I, class U, class... Args,
typename std::enable_if<std::is_constructible<
variant_internal::VariantAlternativeSfinaeT<I, variant>,
std::initializer_list<U>&, Args...>::value>::type* = nullptr>
constexpr explicit variant(in_place_index_t<I>, std::initializer_list<U> il,
Args&&... args)
: Base(variant_internal::EmplaceTag<I>(), il,
absl::forward<Args>(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<absl::decay_t<T>, variant>::value,
variant_internal::IndexOfConstructedType<variant, T>>::type::value,
class Tj = absl::variant_alternative_t<I, variant>,
typename std::enable_if<std::is_assignable<Tj&, T>::value &&
std::is_constructible<Tj, T>::value>::type* =
nullptr>
variant& operator=(T&& t) noexcept(
std::is_nothrow_assignable<Tj&, T>::value&&
std::is_nothrow_constructible<Tj, T>::value) {
variant_internal::visit_indices<sizeof...(Tn) + 1>(
variant_internal::VariantCoreAccess::MakeConversionAssignVisitor(
this, absl::forward<T>(t)),
index());
return *this;
}
// emplace() Functions
// Constructs a value of the given alternative type T within the variant.
//
// Example:
//
// absl::variant<std::vector<int>, int, std::string> v;
// v.emplace<int>(99);
// v.emplace<std::string>("abc");
template <
class T, class... Args,
typename std::enable_if<std::is_constructible<
absl::variant_alternative_t<
variant_internal::UnambiguousIndexOf<variant, T>::value, variant>,
Args...>::value>::type* = nullptr>
T& emplace(Args&&... args) {
return variant_internal::VariantCoreAccess::Replace<
variant_internal::UnambiguousIndexOf<variant, T>::value>(
this, absl::forward<Args>(args)...);
}
// Constructs a value of the given alternative type T within the variant using
// an initializer list.
//
// Example:
//
// absl::variant<std::vector<int>, int, std::string> v;
// v.emplace<std::vector<int>>({0, 1, 2});
template <
class T, class U, class... Args,
typename std::enable_if<std::is_constructible<
absl::variant_alternative_t<
variant_internal::UnambiguousIndexOf<variant, T>::value, variant>,
std::initializer_list<U>&, Args...>::value>::type* = nullptr>
T& emplace(std::initializer_list<U> il, Args&&... args) {
return variant_internal::VariantCoreAccess::Replace<
variant_internal::UnambiguousIndexOf<variant, T>::value>(
this, il, absl::forward<Args>(args)...);
}
// Destroys the current value of the variant (provided that
// `absl::valueless_by_exception()` is false, and constructs a new value at
// the given index.
//
// Example:
//
// absl::variant<std::vector<int>, int, int> v;
// v.emplace<1>(99);
// v.emplace<2>(98);
// v.emplace<int>(99); // Won't compile. 'int' isn't a unique type.
template <std::size_t I, class... Args,
typename std::enable_if<
std::is_constructible<absl::variant_alternative_t<I, variant>,
Args...>::value>::type* = nullptr>
absl::variant_alternative_t<I, variant>& emplace(Args&&... args) {
return variant_internal::VariantCoreAccess::Replace<I>(
this, absl::forward<Args>(args)...);
}
// Destroys the current value of the variant (provided that
// `absl::valueless_by_exception()` is false, and constructs a new value at
// the given index using an initializer list and the provided arguments.
//
// Example:
//
// absl::variant<std::vector<int>, int, int> v;
// v.emplace<0>({0, 1, 2});
template <std::size_t I, class U, class... Args,
typename std::enable_if<std::is_constructible<
absl::variant_alternative_t<I, variant>,
std::initializer_list<U>&, Args...>::value>::type* = nullptr>
absl::variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il,
Args&&... args) {
return variant_internal::VariantCoreAccess::Replace<I>(
this, il, absl::forward<Args>(args)...);
}
// variant::valueless_by_exception()
//
// Returns false if and only if the variant currently holds a valid value.
constexpr bool valueless_by_exception() const noexcept {
return this->index_ == absl::variant_npos;
}
// variant::index()
//
// Returns the index value of the variant's currently selected alternative
// type.
constexpr std::size_t index() const noexcept { return this->index_; }
// variant::swap()
//
// Swaps the values of two variant objects.
//
// 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<T0>, std::is_trivial<Tn>...>::value) {
return variant_internal::visit_indices<sizeof...(Tn) + 1>(
variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index());
}
};
// We need a valid declaration of variant<> for SFINAE and overload resolution
// to work properly above, but we don't need a full declaration since this type
// will never be constructed. This declaration, though incomplete, suffices.
template <>
class variant<>;
//------------------------------------------------------------------------------
// Relational Operators
//------------------------------------------------------------------------------
//
// If neither operand is in the `variant::valueless_by_exception` state:
//
// * If the index of both variants is the same, the relational operator
// returns the result of the corresponding relational operator for the
// corresponding alternative type.
// * If the index of both variants is not the same, the relational operator
// returns the result of that operation applied to the value of the left
// operand's index and the value of the right operand's index.
// * If at least one operand is in the valueless_by_exception state:
// - A variant in the valueless_by_exception state is only considered equal
// to another variant in the valueless_by_exception state.
// - If exactly one operand is in the valueless_by_exception state, the
// variant in the valueless_by_exception state is less than the variant
// that is not in the valueless_by_exception state.
//
// Note: The value 1 is added to each index in the relational comparisons such
// that the index corresponding to the valueless_by_exception state wraps around
// to 0 (the lowest value for the index type), and the remaining indices stay in
// the same relative order.
// Equal-to operator
template <typename... Types>
constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==(
const variant<Types...>& a, const variant<Types...>& b) {
return (a.index() == b.index()) &&
variant_internal::visit_indices<sizeof...(Types)>(
variant_internal::EqualsOp<Types...>{&a, &b}, a.index());
}
// Not equal operator
template <typename... Types>
constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=(
const variant<Types...>& a, const variant<Types...>& b) {
return (a.index() != b.index()) ||
variant_internal::visit_indices<sizeof...(Types)>(
variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index());
}
// Less-than operator
template <typename... Types>
constexpr variant_internal::RequireAllHaveLessThanT<Types...> operator<(
const variant<Types...>& a, const variant<Types...>& b) {
return (a.index() != b.index())
? (a.index() + 1) < (b.index() + 1)
: variant_internal::visit_indices<sizeof...(Types)>(
variant_internal::LessThanOp<Types...>{&a, &b}, a.index());
}
// Greater-than operator
template <typename... Types>
constexpr variant_internal::RequireAllHaveGreaterThanT<Types...> operator>(
const variant<Types...>& a, const variant<Types...>& b) {
return (a.index() != b.index())
? (a.index() + 1) > (b.index() + 1)
: variant_internal::visit_indices<sizeof...(Types)>(
variant_internal::GreaterThanOp<Types...>{&a, &b},
a.index());
}
// Less-than or equal-to operator
template <typename... Types>
constexpr variant_internal::RequireAllHaveLessThanOrEqualT<Types...> operator<=(
const variant<Types...>& a, const variant<Types...>& b) {
return (a.index() != b.index())
? (a.index() + 1) < (b.index() + 1)
: variant_internal::visit_indices<sizeof...(Types)>(
variant_internal::LessThanOrEqualsOp<Types...>{&a, &b},
a.index());
}
// Greater-than or equal-to operator
template <typename... Types>
constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT<Types...>
operator>=(const variant<Types...>& a, const variant<Types...>& b) {
return (a.index() != b.index())
? (a.index() + 1) > (b.index() + 1)
: variant_internal::visit_indices<sizeof...(Types)>(
variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b},
a.index());
}
} // namespace absl
namespace std {
// hash()
template <> // NOLINT
struct hash<absl::monostate> {
std::size_t operator()(absl::monostate) const { return 0; }
};
template <class... T> // NOLINT
struct hash<absl::variant<T...>>
: absl::variant_internal::VariantHashBase<absl::variant<T...>, void,
absl::remove_const_t<T>...> {};
} // namespace std
#endif // ABSL_HAVE_STD_VARIANT
namespace absl {
namespace variant_internal {
// Helper visitor for converting a variant<Ts...>` into another type (mostly
// variant) that can be constructed from any type.
template <typename To>
struct ConversionVisitor {
template <typename T>
To operator()(T&& v) const {
return To(std::forward<T>(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<name1, name2, float> InternalReq(const Req&);
//
// // name1 and name2 are convertible to name
// absl::variant<name, float> ExternalReq(const Req& req) {
// return absl::ConvertVariantTo<absl::variant<name, float>>(
// InternalReq(req));
// }
template <typename To, typename Variant>
To ConvertVariantTo(Variant&& variant) {
return absl::visit(variant_internal::ConversionVisitor<To>{},
std::forward<Variant>(variant));
}
} // namespace absl
#endif // ABSL_TYPES_VARIANT_H_
// 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 <algorithm>
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <ostream>
#include <queue>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>
#include "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<Hashable> {
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<int> /*ilist*/,
int value)
: value(value) {}
MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default;
MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) =
default;
int value = 0;
};
#ifdef ABSL_HAVE_EXCEPTIONS
struct ConversionException {};
template <class T>
struct ExceptionOnConversion {
// 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 <class H, class... T>
void ToValuelessByException(absl::variant<H, T...>& v) { // NOLINT
try {
v.template emplace<0>(ExceptionOnConversion<H>());
} catch (ConversionException& /*e*/) {
// This space intentionally left blank.
}
}
#endif // ABSL_HAVE_EXCEPTIONS
// An indexed sequence of distinct structures holding a single
// value of type T
template<typename T, size_t N>
struct ValueHolder {
explicit ValueHolder(const T& x) : value(x) {}
typedef T value_type;
value_type value;
static const size_t kIndex = N;
};
template<typename T, size_t N>
const size_t ValueHolder<T, N>::kIndex;
// The following three functions make ValueHolder compatible with
// EXPECT_EQ and EXPECT_NE
template<typename T, size_t N>
inline bool operator==(const ValueHolder<T, N>& left,
const ValueHolder<T, N>& right) {
return left.value == right.value;
}
template<typename T, size_t N>
inline bool operator!=(const ValueHolder<T, N>& left,
const ValueHolder<T, N>& right) {
return left.value != right.value;
}
template<typename T, size_t N>
inline std::ostream& operator<<(
std::ostream& stream, const ValueHolder<T, N>& object) {
return stream << object.value;
}
// Makes a variant holding twelve uniquely typed T wrappers.
template<typename T>
struct VariantFactory {
typedef variant<ValueHolder<T, 1>, ValueHolder<T, 2>, ValueHolder<T, 3>,
ValueHolder<T, 4>>
Type;
};
// A typelist in 1:1 with VariantFactory, to use type driven unit tests.
typedef ::testing::Types<ValueHolder<size_t, 1>, ValueHolder<size_t, 2>,
ValueHolder<size_t, 3>,
ValueHolder<size_t, 4>> VariantTypes;
// Increments the provided counter pointer in the destructor
struct IncrementInDtor {
explicit IncrementInDtor(int* counter) : counter(counter) {}
~IncrementInDtor() { *counter += 1; }
int* counter;
};
struct IncrementInDtorCopyCanThrow {
explicit IncrementInDtorCopyCanThrow(int* counter) : counter(counter) {}
IncrementInDtorCopyCanThrow(IncrementInDtorCopyCanThrow&& other) noexcept =
default;
IncrementInDtorCopyCanThrow(const IncrementInDtorCopyCanThrow& other)
: counter(other.counter) {}
IncrementInDtorCopyCanThrow& operator=(
IncrementInDtorCopyCanThrow&&) noexcept = default;
IncrementInDtorCopyCanThrow& operator=(
IncrementInDtorCopyCanThrow const& other) {
counter = other.counter;
return *this;
}
~IncrementInDtorCopyCanThrow() { *counter += 1; }
int* counter;
};
// This is defined so operator== for ValueHolder<IncrementInDtor> will
// return true if two IncrementInDtor objects increment the same
// counter
inline bool operator==(const IncrementInDtor& left,
const IncrementInDtor& right) {
return left.counter == right.counter;
}
// This is defined so EXPECT_EQ can work with IncrementInDtor
inline std::ostream& operator<<(
std::ostream& stream, const IncrementInDtor& object) {
return stream << object.counter;
}
// A class that can be copied, but not assigned.
class CopyNoAssign {
public:
explicit CopyNoAssign(int value) : foo(value) {}
CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {}
int foo;
private:
const CopyNoAssign& operator=(const CopyNoAssign&);
};
// A class that can neither be copied nor assigned. We provide
// overloads for the constructor with up to four parameters so we can
// test the overloads of variant::emplace.
class NonCopyable {
public:
NonCopyable()
: value(0) {}
explicit NonCopyable(int value1)
: value(value1) {}
NonCopyable(int value1, int value2)
: value(value1 + value2) {}
NonCopyable(int value1, int value2, int value3)
: value(value1 + value2 + value3) {}
NonCopyable(int value1, int value2, int value3, int value4)
: value(value1 + value2 + value3 + value4) {}
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
int value;
};
// A typed test and typed test case over the VariantTypes typelist,
// from which we derive a number of tests that will execute for one of
// each type.
template <typename T>
class VariantTypesTest : public ::testing::Test {};
TYPED_TEST_CASE(VariantTypesTest, VariantTypes);
////////////////////
// [variant.ctor] //
////////////////////
struct NonNoexceptDefaultConstructible {
NonNoexceptDefaultConstructible() {}
int value = 5;
};
struct NonDefaultConstructible {
NonDefaultConstructible() = delete;
};
TEST(VariantTest, TestDefaultConstructor) {
{
using X = variant<int>;
constexpr variant<int> x{};
ASSERT_FALSE(x.valueless_by_exception());
ASSERT_EQ(0, x.index());
EXPECT_EQ(0, absl::get<0>(x));
EXPECT_TRUE(std::is_nothrow_default_constructible<X>::value);
}
{
using X = variant<NonNoexceptDefaultConstructible>;
X x{};
ASSERT_FALSE(x.valueless_by_exception());
ASSERT_EQ(0, x.index());
EXPECT_EQ(5, absl::get<0>(x).value);
EXPECT_FALSE(std::is_nothrow_default_constructible<X>::value);
}
{
using X = variant<int, NonNoexceptDefaultConstructible>;
X x{};
ASSERT_FALSE(x.valueless_by_exception());
ASSERT_EQ(0, x.index());
EXPECT_EQ(0, absl::get<0>(x));
EXPECT_TRUE(std::is_nothrow_default_constructible<X>::value);
}
{
using X = variant<NonNoexceptDefaultConstructible, int>;
X x{};
ASSERT_FALSE(x.valueless_by_exception());
ASSERT_EQ(0, x.index());
EXPECT_EQ(5, absl::get<0>(x).value);
EXPECT_FALSE(std::is_nothrow_default_constructible<X>::value);
}
EXPECT_FALSE(
std::is_default_constructible<variant<NonDefaultConstructible>>::value);
EXPECT_FALSE((std::is_default_constructible<
variant<NonDefaultConstructible, int>>::value));
EXPECT_TRUE((std::is_default_constructible<
variant<int, NonDefaultConstructible>>::value));
}
// Test that for each slot, copy constructing a variant with that type
// produces a sensible object that correctly reports its type, and
// that copies the provided value.
TYPED_TEST(VariantTypesTest, TestCopyCtor) {
typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
using value_type2 = absl::variant_alternative_t<1, Variant>;
using value_type3 = absl::variant_alternative_t<2, Variant>;
using value_type4 = absl::variant_alternative_t<3, Variant>;
const TypeParam value(TypeParam::kIndex);
Variant original(value);
Variant copied(original);
EXPECT_TRUE(absl::holds_alternative<value_type1>(copied) ||
TypeParam::kIndex != 1);
EXPECT_TRUE(absl::holds_alternative<value_type2>(copied) ||
TypeParam::kIndex != 2);
EXPECT_TRUE(absl::holds_alternative<value_type3>(copied) ||
TypeParam::kIndex != 3);
EXPECT_TRUE(absl::holds_alternative<value_type4>(copied) ||
TypeParam::kIndex != 4);
EXPECT_TRUE((absl::get_if<value_type1>(&original) ==
absl::get_if<value_type1>(&copied)) ||
TypeParam::kIndex == 1);
EXPECT_TRUE((absl::get_if<value_type2>(&original) ==
absl::get_if<value_type2>(&copied)) ||
TypeParam::kIndex == 2);
EXPECT_TRUE((absl::get_if<value_type3>(&original) ==
absl::get_if<value_type3>(&copied)) ||
TypeParam::kIndex == 3);
EXPECT_TRUE((absl::get_if<value_type4>(&original) ==
absl::get_if<value_type4>(&copied)) ||
TypeParam::kIndex == 4);
EXPECT_TRUE((absl::get_if<value_type1>(&original) ==
absl::get_if<value_type1>(&copied)) ||
TypeParam::kIndex == 1);
EXPECT_TRUE((absl::get_if<value_type2>(&original) ==
absl::get_if<value_type2>(&copied)) ||
TypeParam::kIndex == 2);
EXPECT_TRUE((absl::get_if<value_type3>(&original) ==
absl::get_if<value_type3>(&copied)) ||
TypeParam::kIndex == 3);
EXPECT_TRUE((absl::get_if<value_type4>(&original) ==
absl::get_if<value_type4>(&copied)) ||
TypeParam::kIndex == 4);
const TypeParam* ovalptr = absl::get_if<TypeParam>(&original);
const TypeParam* cvalptr = absl::get_if<TypeParam>(&copied);
ASSERT_TRUE(ovalptr != nullptr);
ASSERT_TRUE(cvalptr != nullptr);
EXPECT_EQ(*ovalptr, *cvalptr);
TypeParam* mutable_ovalptr = absl::get_if<TypeParam>(&original);
TypeParam* mutable_cvalptr = absl::get_if<TypeParam>(&copied);
ASSERT_TRUE(mutable_ovalptr != nullptr);
ASSERT_TRUE(mutable_cvalptr != nullptr);
EXPECT_EQ(*mutable_ovalptr, *mutable_cvalptr);
}
template <class>
struct MoveOnly {
MoveOnly() = default;
explicit MoveOnly(int value) : value(value) {}
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
int value = 5;
};
TEST(VariantTest, TestMoveConstruct) {
using V = variant<MoveOnly<class A>, MoveOnly<class B>, MoveOnly<class C>>;
V v(in_place_index_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 <class T>
union SingleUnion {
T member;
};
// NOTE: These don't work with types that can't be union members.
// They are just for testing.
template <class T>
struct is_trivially_move_constructible
: std::is_move_constructible<SingleUnion<T>>::type {};
template <class T>
struct is_trivially_move_assignable
: std::is_move_assignable<SingleUnion<T>>::type {};
TEST(VariantTest, NothrowMoveConstructible) {
// Verify that variant is nothrow move constructible iff its template
// arguments are.
using U = std::unique_ptr<int>;
struct E {
E(E&&) {}
};
static_assert(std::is_nothrow_move_constructible<variant<U>>::value, "");
static_assert(std::is_nothrow_move_constructible<variant<U, int>>::value, "");
static_assert(!std::is_nothrow_move_constructible<variant<U, E>>::value, "");
}
// Test that for each slot, constructing a variant with that type
// produces a sensible object that correctly reports its type, and
// that copies the provided value.
TYPED_TEST(VariantTypesTest, TestValueCtor) {
typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
using value_type2 = absl::variant_alternative_t<1, Variant>;
using value_type3 = absl::variant_alternative_t<2, Variant>;
using value_type4 = absl::variant_alternative_t<3, Variant>;
const TypeParam value(TypeParam::kIndex);
Variant v(value);
EXPECT_TRUE(absl::holds_alternative<value_type1>(v) ||
TypeParam::kIndex != 1);
EXPECT_TRUE(absl::holds_alternative<value_type2>(v) ||
TypeParam::kIndex != 2);
EXPECT_TRUE(absl::holds_alternative<value_type3>(v) ||
TypeParam::kIndex != 3);
EXPECT_TRUE(absl::holds_alternative<value_type4>(v) ||
TypeParam::kIndex != 4);
EXPECT_TRUE(nullptr != absl::get_if<value_type1>(&v) ||
TypeParam::kIndex != 1);
EXPECT_TRUE(nullptr != absl::get_if<value_type2>(&v) ||
TypeParam::kIndex != 2);
EXPECT_TRUE(nullptr != absl::get_if<value_type3>(&v) ||
TypeParam::kIndex != 3);
EXPECT_TRUE(nullptr != absl::get_if<value_type4>(&v) ||
TypeParam::kIndex != 4);
EXPECT_TRUE(nullptr != absl::get_if<value_type1>(&v) ||
TypeParam::kIndex != 1);
EXPECT_TRUE(nullptr != absl::get_if<value_type2>(&v) ||
TypeParam::kIndex != 2);
EXPECT_TRUE(nullptr != absl::get_if<value_type3>(&v) ||
TypeParam::kIndex != 3);
EXPECT_TRUE(nullptr != absl::get_if<value_type4>(&v) ||
TypeParam::kIndex != 4);
const TypeParam* valptr = absl::get_if<TypeParam>(&v);
ASSERT_TRUE(nullptr != valptr);
EXPECT_EQ(value.value, valptr->value);
const TypeParam* mutable_valptr = absl::get_if<TypeParam>(&v);
ASSERT_TRUE(nullptr != mutable_valptr);
EXPECT_EQ(value.value, mutable_valptr->value);
}
TEST(VariantTest, InPlaceType) {
using Var = variant<int, std::string, NonCopyable, std::vector<int>>;
Var v1(in_place_type_t<int>(), 7);
ASSERT_TRUE(absl::holds_alternative<int>(v1));
EXPECT_EQ(7, absl::get<int>(v1));
Var v2(in_place_type_t<std::string>(), "ABC");
ASSERT_TRUE(absl::holds_alternative<std::string>(v2));
EXPECT_EQ("ABC", absl::get<std::string>(v2));
Var v3(in_place_type_t<std::string>(), "ABC", 2);
ASSERT_TRUE(absl::holds_alternative<std::string>(v3));
EXPECT_EQ("AB", absl::get<std::string>(v3));
Var v4(in_place_type_t<NonCopyable>{});
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v4));
Var v5(in_place_type_t<std::vector<int>>(), {1, 2, 3});
ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5));
EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3));
}
TEST(VariantTest, InPlaceTypeInitializerList) {
using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
Var v1(in_place_type_t<MoveOnlyWithListConstructor>(), {1, 2, 3, 4, 5}, 6);
ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
}
TEST(VariantTest, InPlaceIndex) {
using Var = variant<int, std::string, NonCopyable, std::vector<int>>;
Var v1(in_place_index_t<0>(), 7);
ASSERT_TRUE(absl::holds_alternative<int>(v1));
EXPECT_EQ(7, absl::get<int>(v1));
Var v2(in_place_index_t<1>(), "ABC");
ASSERT_TRUE(absl::holds_alternative<std::string>(v2));
EXPECT_EQ("ABC", absl::get<std::string>(v2));
Var v3(in_place_index_t<1>(), "ABC", 2);
ASSERT_TRUE(absl::holds_alternative<std::string>(v3));
EXPECT_EQ("AB", absl::get<std::string>(v3));
Var v4(in_place_index_t<2>{});
EXPECT_TRUE(absl::holds_alternative<NonCopyable>(v4));
// Verify that a variant with only non-copyables can still be constructed.
EXPECT_TRUE(absl::holds_alternative<NonCopyable>(
variant<NonCopyable>(in_place_index_t<0>{})));
Var v5(in_place_index_t<3>(), {1, 2, 3});
ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5));
EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3));
}
TEST(VariantTest, InPlaceIndexInitializerList) {
using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
Var v1(in_place_index_t<3>(), {1, 2, 3, 4, 5}, 6);
ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
}
////////////////////
// [variant.dtor] //
////////////////////
// Make sure that the destructor destroys the contained value
TEST(VariantTest, TestDtor) {
typedef VariantFactory<IncrementInDtor>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
using value_type2 = absl::variant_alternative_t<1, Variant>;
using value_type3 = absl::variant_alternative_t<2, Variant>;
using value_type4 = absl::variant_alternative_t<3, Variant>;
int counter = 0;
IncrementInDtor counter_adjuster(&counter);
EXPECT_EQ(0, counter);
value_type1 value1(counter_adjuster);
{ Variant object(value1); }
EXPECT_EQ(1, counter);
value_type2 value2(counter_adjuster);
{ Variant object(value2); }
EXPECT_EQ(2, counter);
value_type3 value3(counter_adjuster);
{ Variant object(value3); }
EXPECT_EQ(3, counter);
value_type4 value4(counter_adjuster);
{ Variant object(value4); }
EXPECT_EQ(4, counter);
}
#ifdef ABSL_HAVE_EXCEPTIONS
// Test destruction when in the valueless_by_exception state.
TEST(VariantTest, TestDtorValuelessByException) {
int counter = 0;
IncrementInDtor counter_adjuster(&counter);
{
using Variant = VariantFactory<IncrementInDtor>::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<IncrementInDtor>::Type Variant;
int counter = 0;
IncrementInDtor counter_adjuster(&counter);
absl::variant_alternative_t<0, Variant> value(counter_adjuster);
Variant object(value);
object.operator=(object);
EXPECT_EQ(0, counter);
// A 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<int, std::string> 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<typename TypeParam::value_type>::Type Variant;
const TypeParam value(TypeParam::kIndex);
const Variant source(value);
Variant target(TypeParam(value.value + 1));
ASSERT_TRUE(absl::holds_alternative<TypeParam>(source));
ASSERT_TRUE(absl::holds_alternative<TypeParam>(target));
ASSERT_NE(absl::get<TypeParam>(source), absl::get<TypeParam>(target));
target = source;
ASSERT_TRUE(absl::holds_alternative<TypeParam>(source));
ASSERT_TRUE(absl::holds_alternative<TypeParam>(target));
EXPECT_EQ(absl::get<TypeParam>(source), absl::get<TypeParam>(target));
}
// Test that assisnging a variant<..., T, ...> to a variant<1, ...>
// produces a variant<..., T, ...> with the correct value.
TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingSourceType) {
typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
const TypeParam value(TypeParam::kIndex);
const Variant source(value);
ASSERT_TRUE(absl::holds_alternative<TypeParam>(source));
Variant target(value_type1(1));
ASSERT_TRUE(absl::holds_alternative<value_type1>(target));
target = source;
EXPECT_TRUE(absl::holds_alternative<TypeParam>(source));
EXPECT_TRUE(absl::holds_alternative<TypeParam>(target));
EXPECT_EQ(absl::get<TypeParam>(source), absl::get<TypeParam>(target));
}
// Test that assigning a variant<1, ...> to a variant<..., T, ...>
// produces a variant<1, ...> with the correct value.
TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingTargetType) {
typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
const Variant source(value_type1(1));
ASSERT_TRUE(absl::holds_alternative<value_type1>(source));
const TypeParam value(TypeParam::kIndex);
Variant target(value);
ASSERT_TRUE(absl::holds_alternative<TypeParam>(target));
target = source;
EXPECT_TRUE(absl::holds_alternative<value_type1>(target));
EXPECT_TRUE(absl::holds_alternative<value_type1>(source));
EXPECT_EQ(absl::get<value_type1>(source), absl::get<value_type1>(target));
}
// Test that operator=<T> works, that assigning a new value destroys
// the old and that assigning the new value again does not redestroy
// the old
TEST(VariantTest, TestAssign) {
typedef VariantFactory<IncrementInDtor>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
using value_type2 = absl::variant_alternative_t<1, Variant>;
using value_type3 = absl::variant_alternative_t<2, Variant>;
using value_type4 = absl::variant_alternative_t<3, Variant>;
const int kSize = 4;
int counter[kSize];
std::unique_ptr<IncrementInDtor> counter_adjustor[kSize];
for (int i = 0; i != kSize; i++) {
counter[i] = 0;
counter_adjustor[i] = absl::make_unique<IncrementInDtor>(&counter[i]);
}
value_type1 v1(*counter_adjustor[0]);
value_type2 v2(*counter_adjustor[1]);
value_type3 v3(*counter_adjustor[2]);
value_type4 v4(*counter_adjustor[3]);
// Test that reassignment causes destruction of old value
{
Variant object(v1);
object = v2;
object = v3;
object = v4;
object = v1;
}
EXPECT_EQ(2, counter[0]);
EXPECT_EQ(1, counter[1]);
EXPECT_EQ(1, counter[2]);
EXPECT_EQ(1, counter[3]);
std::fill(std::begin(counter), std::end(counter), 0);
// Test that self-assignment does not cause destruction of old value
{
Variant object(v1);
object.operator=(object);
EXPECT_EQ(0, counter[0]);
}
{
Variant object(v2);
object.operator=(object);
EXPECT_EQ(0, counter[1]);
}
{
Variant object(v3);
object.operator=(object);
EXPECT_EQ(0, counter[2]);
}
{
Variant object(v4);
object.operator=(object);
EXPECT_EQ(0, counter[3]);
}
EXPECT_EQ(1, counter[0]);
EXPECT_EQ(1, counter[1]);
EXPECT_EQ(1, counter[2]);
EXPECT_EQ(1, counter[3]);
}
// This tests that we perform a backup if the copy-assign can throw but the move
// cannot throw.
TEST(VariantTest, TestBackupAssign) {
typedef VariantFactory<IncrementInDtorCopyCanThrow>::Type Variant;
using value_type1 = absl::variant_alternative_t<0, Variant>;
using value_type2 = absl::variant_alternative_t<1, Variant>;
using value_type3 = absl::variant_alternative_t<2, Variant>;
using value_type4 = absl::variant_alternative_t<3, Variant>;
const int kSize = 4;
int counter[kSize];
std::unique_ptr<IncrementInDtorCopyCanThrow> counter_adjustor[kSize];
for (int i = 0; i != kSize; i++) {
counter[i] = 0;
counter_adjustor[i].reset(new IncrementInDtorCopyCanThrow(&counter[i]));
}
value_type1 v1(*counter_adjustor[0]);
value_type2 v2(*counter_adjustor[1]);
value_type3 v3(*counter_adjustor[2]);
value_type4 v4(*counter_adjustor[3]);
// Test that reassignment causes destruction of old value
{
Variant object(v1);
object = v2;
object = v3;
object = v4;
object = v1;
}
// libstdc++ doesn't pass this test
#if !(defined(ABSL_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<int, char>;
Variant v(absl::in_place_index_t<0>{}, 0);
{
char& emplace_result = v.emplace<char>();
ASSERT_TRUE(absl::holds_alternative<char>(v));
EXPECT_EQ(absl::get<char>(v), 0);
EXPECT_EQ(&emplace_result, &absl::get<char>(v));
}
// Make sure that another emplace does zero-initialization
absl::get<char>(v) = 'a';
v.emplace<char>('b');
ASSERT_TRUE(absl::holds_alternative<char>(v));
EXPECT_EQ(absl::get<char>(v), 'b');
{
int& emplace_result = v.emplace<int>();
EXPECT_TRUE(absl::holds_alternative<int>(v));
EXPECT_EQ(absl::get<int>(v), 0);
EXPECT_EQ(&emplace_result, &absl::get<int>(v));
}
}
TEST(VariantTest, TestEmplaceInitializerList) {
using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
Var v1(absl::in_place_index_t<0>{}, 555);
MoveOnlyWithListConstructor& emplace_result =
v1.emplace<MoveOnlyWithListConstructor>({1, 2, 3, 4, 5}, 6);
ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
EXPECT_EQ(&emplace_result, &absl::get<MoveOnlyWithListConstructor>(v1));
}
TEST(VariantTest, TestEmplaceIndex) {
using Variant = variant<int, char>;
Variant v(absl::in_place_index_t<0>{}, 555);
{
char& emplace_result = v.emplace<1>();
ASSERT_TRUE(absl::holds_alternative<char>(v));
EXPECT_EQ(absl::get<char>(v), 0);
EXPECT_EQ(&emplace_result, &absl::get<char>(v));
}
// Make sure that another emplace does zero-initialization
absl::get<char>(v) = 'a';
v.emplace<1>('b');
ASSERT_TRUE(absl::holds_alternative<char>(v));
EXPECT_EQ(absl::get<char>(v), 'b');
{
int& emplace_result = v.emplace<0>();
EXPECT_TRUE(absl::holds_alternative<int>(v));
EXPECT_EQ(absl::get<int>(v), 0);
EXPECT_EQ(&emplace_result, &absl::get<int>(v));
}
}
TEST(VariantTest, TestEmplaceIndexInitializerList) {
using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
Var v1(absl::in_place_index_t<0>{}, 555);
MoveOnlyWithListConstructor& emplace_result =
v1.emplace<3>({1, 2, 3, 4, 5}, 6);
ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
EXPECT_EQ(&emplace_result, &absl::get<MoveOnlyWithListConstructor>(v1));
}
//////////////////////
// [variant.status] //
//////////////////////
TEST(VariantTest, Index) {
using Var = variant<int, std::string, double>;
Var v = 1;
EXPECT_EQ(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<int>(3);
EXPECT_EQ(0, v2.index());
}
TEST(VariantTest, NotValuelessByException) {
using Var = variant<int, std::string, double>;
Var v = 1;
EXPECT_FALSE(v.valueless_by_exception());
v = "str";
EXPECT_FALSE(v.valueless_by_exception());
v = 0.;
EXPECT_FALSE(v.valueless_by_exception());
Var v2 = v;
EXPECT_FALSE(v.valueless_by_exception());
v2.emplace<int>(3);
EXPECT_FALSE(v.valueless_by_exception());
}
#ifdef ABSL_HAVE_EXCEPTIONS
TEST(VariantTest, IndexValuelessByException) {
using Var = variant<MoveCanThrow, std::string, double>;
Var v(absl::in_place_index_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<MoveCanThrow, std::string, double>;
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<SpecialSwap> a = v1, b = v2;
EXPECT_THAT(a, VariantWith<SpecialSwap>(v1));
EXPECT_THAT(b, VariantWith<SpecialSwap>(v2));
a.swap(b);
EXPECT_THAT(a, VariantWith<SpecialSwap>(v2));
EXPECT_THAT(b, VariantWith<SpecialSwap>(v1));
EXPECT_TRUE(absl::get<SpecialSwap>(a).special_swap);
using V = variant<MoveCanThrow, std::string, int>;
int i = 33;
std::string s = "abc";
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<std::string>(s));
EXPECT_THAT(rhs, VariantWith<int>(i));
}
{
// lhs is valueless
V lhs(valueless), rhs(i);
lhs.swap(rhs);
EXPECT_THAT(lhs, VariantWith<int>(i));
EXPECT_TRUE(rhs.valueless_by_exception());
}
{
// rhs is valueless
V lhs(s), rhs(valueless);
lhs.swap(rhs);
EXPECT_THAT(rhs, VariantWith<std::string>(s));
EXPECT_TRUE(lhs.valueless_by_exception());
}
{
// both are valueless
V lhs(valueless), rhs(valueless);
lhs.swap(rhs);
EXPECT_TRUE(lhs.valueless_by_exception());
EXPECT_TRUE(rhs.valueless_by_exception());
}
}
//////////////////////
// [variant.helper] //
//////////////////////
TEST(VariantTest, VariantSize) {
{
using Size1Variant = absl::variant<int>;
EXPECT_EQ(1, absl::variant_size<Size1Variant>::value);
EXPECT_EQ(1, absl::variant_size<const Size1Variant>::value);
EXPECT_EQ(1, absl::variant_size<volatile Size1Variant>::value);
EXPECT_EQ(1, absl::variant_size<const volatile Size1Variant>::value);
}
{
using Size3Variant = absl::variant<int, float, int>;
EXPECT_EQ(3, absl::variant_size<Size3Variant>::value);
EXPECT_EQ(3, absl::variant_size<const Size3Variant>::value);
EXPECT_EQ(3, absl::variant_size<volatile Size3Variant>::value);
EXPECT_EQ(3, absl::variant_size<const volatile Size3Variant>::value);
}
}
TEST(VariantTest, VariantAlternative) {
{
using V = absl::variant<float, int, const char*>;
EXPECT_TRUE(
(std::is_same<float, absl::variant_alternative_t<0, V>>::value));
EXPECT_TRUE((std::is_same<const float,
absl::variant_alternative_t<0, const V>>::value));
EXPECT_TRUE(
(std::is_same<volatile float,
absl::variant_alternative_t<0, volatile V>>::value));
EXPECT_TRUE((
std::is_same<const volatile float,
absl::variant_alternative_t<0, const volatile V>>::value));
EXPECT_TRUE((std::is_same<int, absl::variant_alternative_t<1, V>>::value));
EXPECT_TRUE((std::is_same<const int,
absl::variant_alternative_t<1, const V>>::value));
EXPECT_TRUE(
(std::is_same<volatile int,
absl::variant_alternative_t<1, volatile V>>::value));
EXPECT_TRUE((
std::is_same<const volatile int,
absl::variant_alternative_t<1, const volatile V>>::value));
EXPECT_TRUE(
(std::is_same<const char*, absl::variant_alternative_t<2, V>>::value));
EXPECT_TRUE((std::is_same<const char* const,
absl::variant_alternative_t<2, const V>>::value));
EXPECT_TRUE(
(std::is_same<const char* volatile,
absl::variant_alternative_t<2, volatile V>>::value));
EXPECT_TRUE((
std::is_same<const char* const volatile,
absl::variant_alternative_t<2, const volatile V>>::value));
}
{
using V = absl::variant<float, volatile int, const char*>;
EXPECT_TRUE(
(std::is_same<float, absl::variant_alternative_t<0, V>>::value));
EXPECT_TRUE((std::is_same<const float,
absl::variant_alternative_t<0, const V>>::value));
EXPECT_TRUE(
(std::is_same<volatile float,
absl::variant_alternative_t<0, volatile V>>::value));
EXPECT_TRUE((
std::is_same<const volatile float,
absl::variant_alternative_t<0, const volatile V>>::value));
EXPECT_TRUE(
(std::is_same<volatile int, absl::variant_alternative_t<1, V>>::value));
EXPECT_TRUE((std::is_same<const volatile int,
absl::variant_alternative_t<1, const V>>::value));
EXPECT_TRUE(
(std::is_same<volatile int,
absl::variant_alternative_t<1, volatile V>>::value));
EXPECT_TRUE((
std::is_same<const volatile int,
absl::variant_alternative_t<1, const volatile V>>::value));
EXPECT_TRUE(
(std::is_same<const char*, absl::variant_alternative_t<2, V>>::value));
EXPECT_TRUE((std::is_same<const char* const,
absl::variant_alternative_t<2, const V>>::value));
EXPECT_TRUE(
(std::is_same<const char* volatile,
absl::variant_alternative_t<2, volatile V>>::value));
EXPECT_TRUE((
std::is_same<const char* const volatile,
absl::variant_alternative_t<2, const volatile V>>::value));
}
}
///////////////////
// [variant.get] //
///////////////////
TEST(VariantTest, HoldsAlternative) {
using Var = variant<int, std::string, double>;
Var v = 1;
EXPECT_TRUE(absl::holds_alternative<int>(v));
EXPECT_FALSE(absl::holds_alternative<std::string>(v));
EXPECT_FALSE(absl::holds_alternative<double>(v));
v = "str";
EXPECT_FALSE(absl::holds_alternative<int>(v));
EXPECT_TRUE(absl::holds_alternative<std::string>(v));
EXPECT_FALSE(absl::holds_alternative<double>(v));
v = 0.;
EXPECT_FALSE(absl::holds_alternative<int>(v));
EXPECT_FALSE(absl::holds_alternative<std::string>(v));
EXPECT_TRUE(absl::holds_alternative<double>(v));
Var v2 = v;
EXPECT_FALSE(absl::holds_alternative<int>(v2));
EXPECT_FALSE(absl::holds_alternative<std::string>(v2));
EXPECT_TRUE(absl::holds_alternative<double>(v2));
v2.emplace<int>(3);
EXPECT_TRUE(absl::holds_alternative<int>(v2));
EXPECT_FALSE(absl::holds_alternative<std::string>(v2));
EXPECT_FALSE(absl::holds_alternative<double>(v2));
}
TEST(VariantTest, GetIndex) {
using Var = variant<int, std::string, double, int>;
{
Var v(absl::in_place_index_t<0>{}, 0);
using LValueGetType = decltype(absl::get<0>(v));
using RValueGetType = decltype(absl::get<0>(absl::move(v)));
EXPECT_TRUE((std::is_same<LValueGetType, int&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value));
EXPECT_EQ(absl::get<0>(v), 0);
EXPECT_EQ(absl::get<0>(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<ConstLValueGetType, const int&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value));
EXPECT_EQ(absl::get<0>(const_v), 0);
EXPECT_EQ(absl::get<0>(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<LValueGetType, std::string&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, std::string&&>::value));
EXPECT_EQ(absl::get<1>(v), "Hello");
EXPECT_EQ(absl::get<1>(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<ConstLValueGetType, const std::string&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const std::string&&>::value));
EXPECT_EQ(absl::get<1>(const_v), "Hello");
EXPECT_EQ(absl::get<1>(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<LValueGetType, double&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, double&&>::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<ConstLValueGetType, const double&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const double&&>::value));
EXPECT_EQ(absl::get<2>(const_v), 2.);
EXPECT_EQ(absl::get<2>(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<LValueGetType, int&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, int&&>::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<ConstLValueGetType, const int&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value));
EXPECT_EQ(absl::get<3>(const_v), 1);
EXPECT_EQ(absl::get<3>(absl::move(const_v)), 1); // NOLINT
}
}
TEST(VariantTest, BadGetIndex) {
using Var = variant<int, std::string, double>;
{
Var v = 1;
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(std::move(v)));
const Var& const_v = v;
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(const_v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
absl::get<1>(std::move(const_v))); // NOLINT
}
{
Var v = std::string("Hello");
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(std::move(v)));
const Var& const_v = v;
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(const_v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
absl::get<0>(std::move(const_v))); // NOLINT
}
}
TEST(VariantTest, GetType) {
using Var = variant<int, std::string, double>;
{
Var v = 1;
using LValueGetType = decltype(absl::get<int>(v));
using RValueGetType = decltype(absl::get<int>(absl::move(v)));
EXPECT_TRUE((std::is_same<LValueGetType, int&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value));
EXPECT_EQ(absl::get<int>(v), 1);
EXPECT_EQ(absl::get<int>(absl::move(v)), 1);
const Var& const_v = v;
using ConstLValueGetType = decltype(absl::get<int>(const_v));
using ConstRValueGetType = decltype(absl::get<int>(absl::move(const_v)));
EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value));
EXPECT_EQ(absl::get<int>(const_v), 1);
EXPECT_EQ(absl::get<int>(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<LValueGetType, std::string&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, std::string&&>::value));
EXPECT_EQ(absl::get<std::string>(v), "Hello");
EXPECT_EQ(absl::get<std::string>(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<ConstLValueGetType, const std::string&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const std::string&&>::value));
EXPECT_EQ(absl::get<std::string>(const_v), "Hello");
EXPECT_EQ(absl::get<std::string>(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<LValueGetType, double&>::value));
EXPECT_TRUE((std::is_same<RValueGetType, double&&>::value));
EXPECT_EQ(absl::get<double>(v), 2.);
EXPECT_EQ(absl::get<double>(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<ConstLValueGetType, const double&>::value));
EXPECT_TRUE((std::is_same<ConstRValueGetType, const double&&>::value));
EXPECT_EQ(absl::get<double>(const_v), 2.);
EXPECT_EQ(absl::get<double>(absl::move(const_v)), 2.);
}
}
TEST(VariantTest, BadGetType) {
using Var = variant<int, std::string, double>;
{
Var v = 1;
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<std::string>(v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
absl::get<std::string>(std::move(v)));
const Var& const_v = v;
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<std::string>(const_v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
absl::get<std::string>(std::move(const_v))); // NOLINT
}
{
Var v = std::string("Hello");
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(std::move(v)));
const Var& const_v = v;
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(const_v));
ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
absl::get<int>(std::move(const_v))); // NOLINT
}
}
TEST(VariantTest, GetIfIndex) {
using Var = variant<int, std::string, double, int>;
{
Var v(absl::in_place_index_t<0>{}, 0);
EXPECT_TRUE(noexcept(absl::get_if<0>(&v)));
{
auto* elem = absl::get_if<0>(&v);
EXPECT_TRUE((std::is_same<decltype(elem), int*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, 0);
{
auto* bad_elem = absl::get_if<1>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<2>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<3>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
}
const Var& const_v = v;
EXPECT_TRUE(noexcept(absl::get_if<0>(&const_v)));
{
auto* elem = absl::get_if<0>(&const_v);
EXPECT_TRUE((std::is_same<decltype(elem), const int*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, 0);
{
auto* bad_elem = absl::get_if<1>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const std::string*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<2>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<3>(&const_v);
EXPECT_EQ(bad_elem, nullptr);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
}
}
}
{
Var v = std::string("Hello");
EXPECT_TRUE(noexcept(absl::get_if<1>(&v)));
{
auto* elem = absl::get_if<1>(&v);
EXPECT_TRUE((std::is_same<decltype(elem), std::string*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, "Hello");
{
auto* bad_elem = absl::get_if<0>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<2>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<3>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
}
const Var& const_v = v;
EXPECT_TRUE(noexcept(absl::get_if<1>(&const_v)));
{
auto* elem = absl::get_if<1>(&const_v);
EXPECT_TRUE((std::is_same<decltype(elem), const std::string*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, "Hello");
{
auto* bad_elem = absl::get_if<0>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<2>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<3>(&const_v);
EXPECT_EQ(bad_elem, nullptr);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
}
}
}
{
Var v = 2.0;
EXPECT_TRUE(noexcept(absl::get_if<2>(&v)));
{
auto* elem = absl::get_if<2>(&v);
EXPECT_TRUE((std::is_same<decltype(elem), double*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, 2.0);
{
auto* bad_elem = absl::get_if<0>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<1>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<3>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
}
const Var& const_v = v;
EXPECT_TRUE(noexcept(absl::get_if<2>(&const_v)));
{
auto* elem = absl::get_if<2>(&const_v);
EXPECT_TRUE((std::is_same<decltype(elem), const double*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, 2.0);
{
auto* bad_elem = absl::get_if<0>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<1>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const std::string*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<3>(&const_v);
EXPECT_EQ(bad_elem, nullptr);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
}
}
}
{
Var v(absl::in_place_index_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<decltype(elem), int*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, 1);
{
auto* bad_elem = absl::get_if<0>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<1>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<2>(&v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
}
const Var& const_v = v;
EXPECT_TRUE(noexcept(absl::get_if<3>(&const_v)));
{
auto* elem = absl::get_if<3>(&const_v);
EXPECT_TRUE((std::is_same<decltype(elem), const int*>::value));
ASSERT_NE(elem, nullptr);
EXPECT_EQ(*elem, 1);
{
auto* bad_elem = absl::get_if<0>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<1>(&const_v);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const std::string*>::value));
EXPECT_EQ(bad_elem, nullptr);
}
{
auto* bad_elem = absl::get_if<2>(&const_v);
EXPECT_EQ(bad_elem, nullptr);
EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value));
}
}
}
}
//////////////////////
// [variant.relops] //
//////////////////////
TEST(VariantTest, OperatorEquals) {
variant<int, std::string> a(1), b(1);
EXPECT_TRUE(a == b);
EXPECT_TRUE(b == a);
EXPECT_FALSE(a != b);
EXPECT_FALSE(b != a);
b = "str";
EXPECT_FALSE(a == b);
EXPECT_FALSE(b == a);
EXPECT_TRUE(a != b);
EXPECT_TRUE(b != a);
b = 0;
EXPECT_FALSE(a == b);
EXPECT_FALSE(b == a);
EXPECT_TRUE(a != b);
EXPECT_TRUE(b != a);
a = b = "foo";
EXPECT_TRUE(a == b);
EXPECT_TRUE(b == a);
EXPECT_FALSE(a != b);
EXPECT_FALSE(b != a);
a = "bar";
EXPECT_FALSE(a == b);
EXPECT_FALSE(b == a);
EXPECT_TRUE(a != b);
EXPECT_TRUE(b != a);
}
TEST(VariantTest, OperatorRelational) {
variant<int, std::string> a(1), b(1);
EXPECT_FALSE(a < b);
EXPECT_FALSE(b < a);
EXPECT_FALSE(a > b);
EXPECT_FALSE(b > a);
EXPECT_TRUE(a <= b);
EXPECT_TRUE(b <= a);
EXPECT_TRUE(a >= b);
EXPECT_TRUE(b >= a);
b = "str";
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
EXPECT_FALSE(a > b);
EXPECT_TRUE(b > a);
EXPECT_TRUE(a <= b);
EXPECT_FALSE(b <= a);
EXPECT_FALSE(a >= b);
EXPECT_TRUE(b >= a);
b = 0;
EXPECT_FALSE(a < b);
EXPECT_TRUE(b < a);
EXPECT_TRUE(a > b);
EXPECT_FALSE(b > a);
EXPECT_FALSE(a <= b);
EXPECT_TRUE(b <= a);
EXPECT_TRUE(a >= b);
EXPECT_FALSE(b >= a);
a = b = "foo";
EXPECT_FALSE(a < b);
EXPECT_FALSE(b < a);
EXPECT_FALSE(a > b);
EXPECT_FALSE(b > a);
EXPECT_TRUE(a <= b);
EXPECT_TRUE(b <= a);
EXPECT_TRUE(a >= b);
EXPECT_TRUE(b >= a);
a = "bar";
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
EXPECT_FALSE(a > b);
EXPECT_TRUE(b > a);
EXPECT_TRUE(a <= b);
EXPECT_FALSE(b <= a);
EXPECT_FALSE(a >= b);
EXPECT_TRUE(b >= a);
}
#ifdef ABSL_HAVE_EXCEPTIONS
TEST(VariantTest, ValuelessOperatorEquals) {
variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"),
valueless(absl::in_place_index_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<MoveCanThrow, std::string> 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 <typename T>
struct ConvertTo {
template <typename U>
T operator()(const U& u) const {
return u;
}
};
TEST(VariantTest, VisitSimple) {
variant<std::string, const char*> v = "A";
std::string str = absl::visit(ConvertTo<std::string>{}, v);
EXPECT_EQ("A", str);
v = std::string("B");
absl::string_view piece = absl::visit(ConvertTo<absl::string_view>{}, v);
EXPECT_EQ("B", piece);
struct StrLen {
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<std::string> v = std::string("X");
struct Visitor {
bool operator()(const std::string&) const { return false; }
bool operator()(std::string&&) const { return true; } // NOLINT
int operator()(const std::string&, const std::string&) const { return 0; }
int operator()(const std::string&, std::string&&) const { return 1; } // NOLINT
int operator()(std::string&&, const std::string&) const { return 2; } // NOLINT
int operator()(std::string&&, std::string&&) const { return 3; } // NOLINT
};
EXPECT_FALSE(absl::visit(Visitor{}, v));
EXPECT_TRUE(absl::visit(Visitor{}, 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<std::string> v = std::string("X");
struct Visitor {
bool operator()(const std::string&) const& { return false; }
bool operator()(const std::string&) && { return true; }
};
Visitor visitor;
EXPECT_FALSE(absl::visit(visitor, v));
EXPECT_TRUE(absl::visit(Visitor{}, v));
}
TEST(VariantTest, VisitResultTypeDifferent) {
variant<std::string> v = std::string("X");
struct LValue_LValue {};
struct RValue_LValue {};
struct LValue_RValue {};
struct RValue_RValue {};
struct Visitor {
LValue_LValue operator()(const std::string&) const& { return {}; }
RValue_LValue operator()(std::string&&) const& { return {}; } // NOLINT
LValue_RValue operator()(const std::string&) && { return {}; }
RValue_RValue operator()(std::string&&) && { return {}; } // NOLINT
} visitor;
EXPECT_TRUE(
(std::is_same<LValue_LValue, decltype(absl::visit(visitor, v))>::value));
EXPECT_TRUE(
(std::is_same<RValue_LValue,
decltype(absl::visit(visitor, absl::move(v)))>::value));
EXPECT_TRUE((
std::is_same<LValue_RValue, decltype(absl::visit(Visitor{}, v))>::value));
EXPECT_TRUE(
(std::is_same<RValue_RValue,
decltype(absl::visit(Visitor{}, absl::move(v)))>::value));
}
TEST(VariantTest, VisitVariadic) {
using A = variant<int, std::string>;
using B = variant<std::unique_ptr<int>, absl::string_view>;
struct Visitor {
std::pair<int, int> operator()(int a, std::unique_ptr<int> b) const {
return {a, *b};
}
std::pair<int, int> operator()(absl::string_view a,
std::unique_ptr<int> b) const {
return {static_cast<int>(a.size()), static_cast<int>(*b)};
}
std::pair<int, int> operator()(int a, absl::string_view b) const {
return {a, static_cast<int>(b.size())};
}
std::pair<int, int> operator()(absl::string_view a,
absl::string_view b) const {
return {static_cast<int>(a.size()), static_cast<int>(b.size())};
}
};
EXPECT_THAT(absl::visit(Visitor(), A(1), B(std::unique_ptr<int>(new int(7)))),
::testing::Pair(1, 7));
EXPECT_THAT(absl::visit(Visitor(), A(1), B(absl::string_view("ABC"))),
::testing::Pair(1, 3));
EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")),
B(std::unique_ptr<int>(new int(7)))),
::testing::Pair(5, 7));
EXPECT_THAT(
absl::visit(Visitor(), A(std::string("BBBBB")), B(absl::string_view("ABC"))),
::testing::Pair(5, 3));
}
TEST(VariantTest, VisitNoArgs) {
EXPECT_EQ(5, absl::visit([] { return 5; }));
}
struct ConstFunctor {
int operator()(int a, int b) const { return a - b; }
};
struct MutableFunctor {
int operator()(int a, int b) { return a - b; }
};
struct Class {
int Method(int a, int b) { return a - b; }
int ConstMethod(int a, int b) const { return a - b; }
int member;
};
TEST(VariantTest, VisitReferenceWrapper) {
ConstFunctor cf;
MutableFunctor mf;
absl::variant<int> three = 3;
absl::variant<int> two = 2;
EXPECT_EQ(1, absl::visit(std::cref(cf), three, two));
EXPECT_EQ(1, absl::visit(std::ref(cf), three, two));
EXPECT_EQ(1, absl::visit(std::ref(mf), three, two));
}
// libstdc++ std::variant doesn't support the INVOKE semantics.
#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
TEST(VariantTest, VisitMemberFunction) {
absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>());
absl::variant<std::unique_ptr<const Class>> cp(
absl::make_unique<const Class>());
absl::variant<int> three = 3;
absl::variant<int> two = 2;
EXPECT_EQ(1, absl::visit(&Class::Method, p, three, two));
EXPECT_EQ(1, absl::visit(&Class::ConstMethod, p, three, two));
EXPECT_EQ(1, absl::visit(&Class::ConstMethod, cp, three, two));
}
TEST(VariantTest, VisitDataMember) {
absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>(Class{42}));
absl::variant<std::unique_ptr<const Class>> cp(
absl::make_unique<const Class>(Class{42}));
EXPECT_EQ(42, absl::visit(&Class::member, p));
absl::visit(&Class::member, p) = 5;
EXPECT_EQ(5, absl::visit(&Class::member, p));
EXPECT_EQ(42, absl::visit(&Class::member, cp));
}
#endif // !(defined(ABSL_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<absl::monostate>::value);
EXPECT_TRUE(is_trivially_move_constructible<absl::monostate>::value);
EXPECT_TRUE(absl::is_trivially_copy_constructible<absl::monostate>::value);
EXPECT_TRUE(is_trivially_move_assignable<absl::monostate>::value);
EXPECT_TRUE(absl::is_trivially_copy_assignable<absl::monostate>::value);
EXPECT_TRUE(absl::is_trivially_destructible<absl::monostate>::value);
}
TEST(VariantTest, VariantMonostateDefaultConstruction) {
absl::variant<absl::monostate, NonDefaultConstructible> var;
EXPECT_EQ(var.index(), 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<absl::monostate>() ==
std::declval<absl::monostate>()));
EXPECT_TRUE(noexcept(std::declval<absl::monostate>() !=
std::declval<absl::monostate>()));
EXPECT_TRUE(noexcept(std::declval<absl::monostate>() <
std::declval<absl::monostate>()));
EXPECT_TRUE(noexcept(std::declval<absl::monostate>() >
std::declval<absl::monostate>()));
EXPECT_TRUE(noexcept(std::declval<absl::monostate>() <=
std::declval<absl::monostate>()));
EXPECT_TRUE(noexcept(std::declval<absl::monostate>() >=
std::declval<absl::monostate>()));
}
///////////////////////
// [variant.specalg] //
///////////////////////
TEST(VariantTest, NonmemberSwap) {
using std::swap;
SpecialSwap v1(3);
SpecialSwap v2(7);
variant<SpecialSwap> a = v1, b = v2;
EXPECT_THAT(a, VariantWith<SpecialSwap>(v1));
EXPECT_THAT(b, VariantWith<SpecialSwap>(v2));
std::swap(a, b);
EXPECT_THAT(a, VariantWith<SpecialSwap>(v2));
EXPECT_THAT(b, VariantWith<SpecialSwap>(v1));
#ifndef ABSL_HAVE_STD_VARIANT
EXPECT_FALSE(absl::get<SpecialSwap>(a).special_swap);
#endif
swap(a, b);
EXPECT_THAT(a, VariantWith<SpecialSwap>(v1));
EXPECT_THAT(b, VariantWith<SpecialSwap>(v2));
EXPECT_TRUE(absl::get<SpecialSwap>(b).special_swap);
}
//////////////////////////
// [variant.bad.access] //
//////////////////////////
TEST(VariantTest, BadAccess) {
EXPECT_TRUE(noexcept(absl::bad_variant_access()));
absl::bad_variant_access exception_obj;
std::exception* base = &exception_obj;
(void)base;
}
////////////////////
// [variant.hash] //
////////////////////
TEST(VariantTest, MonostateHash) {
absl::monostate mono, other_mono;
std::hash<absl::monostate> const hasher{};
static_assert(std::is_same<decltype(hasher(mono)), std::size_t>::value, "");
EXPECT_EQ(hasher(mono), hasher(other_mono));
}
TEST(VariantTest, Hash) {
static_assert(type_traits_internal::IsHashEnabled<variant<int>>::value, "");
static_assert(type_traits_internal::IsHashEnabled<variant<Hashable>>::value,
"");
static_assert(
type_traits_internal::IsHashEnabled<variant<int, Hashable>>::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<variant<T>> 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<variant<NonHashable>>::value, "");
static_assert(!type_traits_internal::IsHashEnabled<
variant<Hashable, NonHashable>>::value,
"");
#endif
// MSVC std::hash<std::variant> does not use the index, thus produce the same
// result on the same value as different alternative.
#if !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT))
{
// same value as different alternative
variant<int, int> v0(in_place_index_t<0>{}, 42);
variant<int, int> v1(in_place_index_t<1>{}, 42);
std::hash<variant<int, int>> hash;
EXPECT_NE(hash(v0), hash(v1));
}
#endif // !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT))
{
std::hash<variant<int>> hash;
std::set<size_t> hashcodes;
for (int i = 0; i < 100; ++i) {
hashcodes.insert(hash(i));
}
EXPECT_GT(hashcodes.size(), 90);
// test const-qualified
static_assert(
type_traits_internal::IsHashEnabled<variant<const int>>::value, "");
static_assert(
type_traits_internal::IsHashEnabled<variant<const Hashable>>::value,
"");
std::hash<absl::variant<const int>> c_hash;
for (int i = 0; i < 100; ++i) {
EXPECT_EQ(hash(i), c_hash(i));
}
}
}
////////////////////////////////////////
// Miscellaneous and deprecated tests //
////////////////////////////////////////
// Test that a set requiring a basic type conversion works correctly.
TEST(VariantTest, TestConvertingSet) {
typedef variant<double> Variant;
Variant v(1.0);
const int two = 2;
v = two;
EXPECT_TRUE(absl::holds_alternative<double>(v));
ASSERT_TRUE(nullptr != absl::get_if<double>(&v));
EXPECT_DOUBLE_EQ(2, absl::get<double>(v));
}
// Test that a vector of variants behaves reasonably.
TEST(VariantTest, Container) {
typedef variant<int, float> Variant;
// Creation of vector should work
std::vector<Variant> vec;
vec.push_back(Variant(10));
vec.push_back(Variant(20.0f));
// Vector resizing should work if we supply a value for new slots
vec.resize(10, Variant(0));
}
// Test that a variant with a non-copyable type can be constructed and
// manipulated to some degree.
TEST(VariantTest, TestVariantWithNonCopyableType) {
typedef variant<int, NonCopyable> Variant;
const int kValue = 1;
Variant v(kValue);
ASSERT_TRUE(absl::holds_alternative<int>(v));
EXPECT_EQ(kValue, absl::get<int>(v));
}
// Test that a variant with a non-copyable type can be transformed to
// the non-copyable type with a call to `emplace` for different numbers
// of arguments. We do not need to test this for each of T1 ... T8
// because `emplace` does not overload on T1 ... to T8, so if this
// works for any one of T1 ... T8, then it works for all of them. We
// do need to test that it works with varying numbers of parameters
// though.
TEST(VariantTest, TestEmplace) {
typedef variant<int, NonCopyable> Variant;
const int kValue = 1;
Variant v(kValue);
ASSERT_TRUE(absl::holds_alternative<int>(v));
EXPECT_EQ(kValue, absl::get<int>(v));
// emplace with zero arguments, then back to 'int'
v.emplace<NonCopyable>();
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
EXPECT_EQ(0, absl::get<NonCopyable>(v).value);
v = kValue;
ASSERT_TRUE(absl::holds_alternative<int>(v));
// emplace with one argument:
v.emplace<NonCopyable>(1);
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
EXPECT_EQ(1, absl::get<NonCopyable>(v).value);
v = kValue;
ASSERT_TRUE(absl::holds_alternative<int>(v));
// emplace with two arguments:
v.emplace<NonCopyable>(1, 2);
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
EXPECT_EQ(3, absl::get<NonCopyable>(v).value);
v = kValue;
ASSERT_TRUE(absl::holds_alternative<int>(v));
// emplace with three arguments
v.emplace<NonCopyable>(1, 2, 3);
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
EXPECT_EQ(6, absl::get<NonCopyable>(v).value);
v = kValue;
ASSERT_TRUE(absl::holds_alternative<int>(v));
// emplace with four arguments
v.emplace<NonCopyable>(1, 2, 3, 4);
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
EXPECT_EQ(10, absl::get<NonCopyable>(v).value);
v = kValue;
ASSERT_TRUE(absl::holds_alternative<int>(v));
}
TEST(VariantTest, TestEmplaceDestroysCurrentValue) {
typedef variant<int, IncrementInDtor, NonCopyable> Variant;
int counter = 0;
Variant v(0);
ASSERT_TRUE(absl::holds_alternative<int>(v));
v.emplace<IncrementInDtor>(&counter);
ASSERT_TRUE(absl::holds_alternative<IncrementInDtor>(v));
ASSERT_EQ(0, counter);
v.emplace<NonCopyable>();
ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
EXPECT_EQ(1, counter);
}
TEST(VariantTest, TestMoveSemantics) {
typedef variant<std::unique_ptr<int>, std::unique_ptr<std::string>> Variant;
// Construct a variant by moving from an element value.
Variant v(absl::WrapUnique(new int(10)));
EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v));
// Construct a variant by moving from another variant.
Variant v2(absl::move(v));
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v2));
ASSERT_NE(nullptr, absl::get<std::unique_ptr<int>>(v2));
EXPECT_EQ(10, *absl::get<std::unique_ptr<int>>(v2));
// Moving from a variant object leaves it holding moved-from value of the
// same element type.
EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v));
ASSERT_NE(nullptr, absl::get_if<std::unique_ptr<int>>(&v));
EXPECT_EQ(nullptr, absl::get<std::unique_ptr<int>>(v));
// Assign a variant from an element value by move.
v = absl::make_unique<std::string>("foo");
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v));
EXPECT_EQ("foo", *absl::get<std::unique_ptr<std::string>>(v));
// Move-assign a variant.
v2 = absl::move(v);
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v2));
EXPECT_EQ("foo", *absl::get<std::unique_ptr<std::string>>(v2));
EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v));
}
variant<int, std::string> PassThrough(const variant<int, std::string>& arg) {
return arg;
}
TEST(VariantTest, TestImplicitConversion) {
EXPECT_TRUE(absl::holds_alternative<int>(PassThrough(0)));
// We still need the explicit cast for std::string, because C++ won't apply
// two user-defined implicit conversions in a row.
EXPECT_TRUE(absl::holds_alternative<std::string>(PassThrough(std::string("foo"))));
}
struct Convertible2;
struct Convertible1 {
Convertible1() {}
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<double, std::string> var(
ConvertVariantTo<variant<double, std::string>>(variant<std::string, int>(0)));
ASSERT_TRUE(absl::holds_alternative<double>(var));
EXPECT_EQ(0.0, absl::get<double>(var));
var = ConvertVariantTo<variant<double, std::string>>(
variant<const char*, float>("foo"));
ASSERT_TRUE(absl::holds_alternative<std::string>(var));
EXPECT_EQ("foo", absl::get<std::string>(var));
variant<double> singleton(
ConvertVariantTo<variant<double>>(variant<int, float>(42)));
ASSERT_TRUE(absl::holds_alternative<double>(singleton));
EXPECT_EQ(42.0, absl::get<double>(singleton));
singleton = ConvertVariantTo<variant<double>>(variant<int, float>(3.14f));
ASSERT_TRUE(absl::holds_alternative<double>(singleton));
EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton)));
singleton = ConvertVariantTo<variant<double>>(variant<int>(0));
ASSERT_TRUE(absl::holds_alternative<double>(singleton));
EXPECT_EQ(0.0, absl::get<double>(singleton));
variant<int32_t, uint32_t> variant2(
ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42)));
ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2));
EXPECT_EQ(42, absl::get<int32_t>(variant2));
variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42));
ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2));
EXPECT_EQ(42, absl::get<uint32_t>(variant2));
variant<Convertible1, Convertible2> variant3(
ConvertVariantTo<variant<Convertible1, Convertible2>>(
(variant<Convertible2, Convertible1>(Convertible1()))));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(
variant<Convertible2, Convertible1>(Convertible2()));
ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
}
TEST(VariantTest, TestLvalueConversion) {
variant<std::string, int> source1 = 0;
variant<double, std::string> destination(
ConvertVariantTo<variant<double, std::string>>(source1));
ASSERT_TRUE(absl::holds_alternative<double>(destination));
EXPECT_EQ(0.0, absl::get<double>(destination));
variant<const char*, float> source2 = "foo";
destination = ConvertVariantTo<variant<double, std::string>>(source2);
ASSERT_TRUE(absl::holds_alternative<std::string>(destination));
EXPECT_EQ("foo", absl::get<std::string>(destination));
variant<int, float> source3(42);
variant<double> singleton(ConvertVariantTo<variant<double>>(source3));
ASSERT_TRUE(absl::holds_alternative<double>(singleton));
EXPECT_EQ(42.0, absl::get<double>(singleton));
source3 = 3.14f;
singleton = ConvertVariantTo<variant<double>>(source3);
ASSERT_TRUE(absl::holds_alternative<double>(singleton));
EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton)));
variant<int> source4(0);
singleton = ConvertVariantTo<variant<double>>(source4);
ASSERT_TRUE(absl::holds_alternative<double>(singleton));
EXPECT_EQ(0.0, absl::get<double>(singleton));
variant<int32_t> source5(42);
variant<int32_t, uint32_t> variant2(
ConvertVariantTo<variant<int32_t, uint32_t>>(source5));
ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2));
EXPECT_EQ(42, absl::get<int32_t>(variant2));
variant<uint32_t> source6(42);
variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6);
ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2));
EXPECT_EQ(42, absl::get<uint32_t>(variant2));
variant<Convertible2, Convertible1> source7((Convertible1()));
variant<Convertible1, Convertible2> variant3(
ConvertVariantTo<variant<Convertible1, Convertible2>>(source7));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
source7 = Convertible2();
variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(source7);
ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
}
TEST(VariantTest, TestMoveConversion) {
using Variant =
variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>;
using OtherVariant = variant<std::unique_ptr<int>, std::unique_ptr<std::string>>;
Variant var(
ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(0)}));
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const int>>(var));
ASSERT_NE(absl::get<std::unique_ptr<const int>>(var), nullptr);
EXPECT_EQ(0, *absl::get<std::unique_ptr<const int>>(var));
var =
ConvertVariantTo<Variant>(OtherVariant(absl::make_unique<std::string>("foo")));
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const std::string>>(var));
EXPECT_EQ("foo", *absl::get<std::unique_ptr<const std::string>>(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<const int>, std::shared_ptr<const std::string>>;
using OtherVariant = variant<std::shared_ptr<int>, std::shared_ptr<std::string>>;
Variant v1(std::make_shared<const int>(0));
// Test copy constructor
Variant v2(v1);
EXPECT_EQ(absl::get<std::shared_ptr<const int>>(v1),
absl::get<std::shared_ptr<const int>>(v2));
// Test copy-assignment operator
v1 = std::make_shared<const std::string>("foo");
v2 = v1;
EXPECT_EQ(absl::get<std::shared_ptr<const std::string>>(v1),
absl::get<std::shared_ptr<const std::string>>(v2));
// Test converting copy constructor
OtherVariant other(std::make_shared<int>(0));
Variant v3(ConvertVariantTo<Variant>(other));
EXPECT_EQ(absl::get<std::shared_ptr<int>>(other),
absl::get<std::shared_ptr<const int>>(v3));
other = std::make_shared<std::string>("foo");
v3 = ConvertVariantTo<Variant>(other);
EXPECT_EQ(absl::get<std::shared_ptr<std::string>>(other),
absl::get<std::shared_ptr<const std::string>>(v3));
}
TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) {
variant<double, std::string> var(
ConvertVariantTo<variant<double, std::string>>(variant<std::string, int>(3)));
EXPECT_THAT(absl::get_if<double>(&var), Pointee(3.0));
var = ConvertVariantTo<variant<double, std::string>>(
variant<const char*, float>("foo"));
EXPECT_THAT(absl::get_if<std::string>(&var), Pointee(std::string("foo")));
variant<double> singleton(
ConvertVariantTo<variant<double>>(variant<int, float>(42)));
EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(42.0));
singleton = ConvertVariantTo<variant<double>>(variant<int, float>(3.14f));
EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(DoubleEq(3.14f)));
singleton = ConvertVariantTo<variant<double>>(variant<int>(3));
EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(3.0));
variant<int32_t, uint32_t> variant2(
ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42)));
EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42));
variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42));
EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42));
variant<Convertible1, Convertible2> variant3(
ConvertVariantTo<variant<Convertible1, Convertible2>>(
(variant<Convertible2, Convertible1>(Convertible1()))));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(
variant<Convertible2, Convertible1>(Convertible2()));
ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
}
TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) {
variant<std::string, int> source1 = 3;
variant<double, std::string> destination(
ConvertVariantTo<variant<double, std::string>>(source1));
EXPECT_THAT(absl::get_if<double>(&destination), Pointee(3.0));
variant<const char*, float> source2 = "foo";
destination = ConvertVariantTo<variant<double, std::string>>(source2);
EXPECT_THAT(absl::get_if<std::string>(&destination), Pointee(std::string("foo")));
variant<int, float> source3(42);
variant<double> singleton(ConvertVariantTo<variant<double>>(source3));
EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(42.0));
source3 = 3.14f;
singleton = ConvertVariantTo<variant<double>>(source3);
EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton)));
EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(DoubleEq(3.14f)));
variant<int> source4(3);
singleton = ConvertVariantTo<variant<double>>(source4);
EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(3.0));
variant<int32_t> source5(42);
variant<int32_t, uint32_t> variant2(
ConvertVariantTo<variant<int32_t, uint32_t>>(source5));
EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42));
variant<uint32_t> source6(42);
variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6);
EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42));
variant<Convertible2, Convertible1> source7((Convertible1()));
variant<Convertible1, Convertible2> variant3(
ConvertVariantTo<variant<Convertible1, Convertible2>>(source7));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
source7 = Convertible2();
variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(source7);
ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
}
TEST(VariantTest, TestMoveConversionViaConvertVariantTo) {
using Variant =
variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>;
using OtherVariant = variant<std::unique_ptr<int>, std::unique_ptr<std::string>>;
Variant var(
ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(3)}));
EXPECT_THAT(absl::get_if<std::unique_ptr<const int>>(&var),
Pointee(Pointee(3)));
var =
ConvertVariantTo<Variant>(OtherVariant(absl::make_unique<std::string>("foo")));
EXPECT_THAT(absl::get_if<std::unique_ptr<const std::string>>(&var),
Pointee(Pointee(std::string("foo"))));
}
// If all alternatives are trivially copy/move constructible, variant should
// also be trivially copy/move constructible. This is not required by the
// standard and we know that libstdc++ variant doesn't have this feature.
// For more details see the paper:
// http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html
#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
#define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1
#endif
TEST(VariantTest, TestCopyAndMoveTypeTraits) {
EXPECT_TRUE(std::is_copy_constructible<variant<std::string>>::value);
EXPECT_TRUE(std::is_copy_assignable<variant<std::string>>::value);
EXPECT_TRUE(std::is_move_constructible<variant<std::string>>::value);
EXPECT_TRUE(std::is_move_assignable<variant<std::string>>::value);
EXPECT_TRUE(std::is_move_constructible<variant<std::unique_ptr<int>>>::value);
EXPECT_TRUE(std::is_move_assignable<variant<std::unique_ptr<int>>>::value);
EXPECT_FALSE(
std::is_copy_constructible<variant<std::unique_ptr<int>>>::value);
EXPECT_FALSE(std::is_copy_assignable<variant<std::unique_ptr<int>>>::value);
EXPECT_FALSE(
absl::is_trivially_copy_constructible<variant<std::string>>::value);
EXPECT_FALSE(absl::is_trivially_copy_assignable<variant<std::string>>::value);
#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
EXPECT_TRUE(absl::is_trivially_copy_constructible<variant<int>>::value);
EXPECT_TRUE(absl::is_trivially_copy_assignable<variant<int>>::value);
EXPECT_TRUE(is_trivially_move_constructible<variant<int>>::value);
EXPECT_TRUE(is_trivially_move_assignable<variant<int>>::value);
#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
}
TEST(VariantTest, TestVectorOfMoveonlyVariant) {
// Verify that variant<MoveonlyType> works correctly as a std::vector element.
std::vector<variant<std::unique_ptr<int>, std::string>> vec;
vec.push_back(absl::make_unique<int>(42));
vec.emplace_back("Hello");
vec.reserve(3);
auto another_vec = absl::move(vec);
// As a sanity check, verify vector contents.
ASSERT_EQ(2, another_vec.size());
EXPECT_EQ(42, *absl::get<std::unique_ptr<int>>(another_vec[0]));
EXPECT_EQ("Hello", absl::get<std::string>(another_vec[1]));
}
TEST(VariantTest, NestedVariant) {
#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
static_assert(absl::is_trivially_copy_constructible<variant<int>>(), "");
static_assert(absl::is_trivially_copy_assignable<variant<int>>(), "");
static_assert(is_trivially_move_constructible<variant<int>>(), "");
static_assert(is_trivially_move_assignable<variant<int>>(), "");
static_assert(absl::is_trivially_copy_constructible<variant<variant<int>>>(),
"");
static_assert(absl::is_trivially_copy_assignable<variant<variant<int>>>(),
"");
static_assert(is_trivially_move_constructible<variant<variant<int>>>(), "");
static_assert(is_trivially_move_assignable<variant<variant<int>>>(), "");
#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
variant<int> x(42);
variant<variant<int>> y(x);
variant<variant<int>> z(y);
EXPECT_TRUE(absl::holds_alternative<variant<int>>(z));
EXPECT_EQ(x, absl::get<variant<int>>(z));
}
struct TriviallyDestructible {
TriviallyDestructible(TriviallyDestructible&&) {}
TriviallyDestructible(const TriviallyDestructible&) {}
TriviallyDestructible& operator=(TriviallyDestructible&&) { return *this; }
TriviallyDestructible& operator=(const TriviallyDestructible&) {
return *this;
}
};
struct TriviallyMovable {
TriviallyMovable(TriviallyMovable&&) = default;
TriviallyMovable(TriviallyMovable const&) {}
TriviallyMovable& operator=(const TriviallyMovable&) { return *this; }
};
struct TriviallyCopyable {
TriviallyCopyable(const TriviallyCopyable&) = default;
TriviallyCopyable& operator=(const TriviallyCopyable&) { return *this; }
};
struct TriviallyMoveAssignable {
TriviallyMoveAssignable(TriviallyMoveAssignable&&) = default;
TriviallyMoveAssignable(const TriviallyMoveAssignable&) {}
TriviallyMoveAssignable& operator=(TriviallyMoveAssignable&&) = default;
TriviallyMoveAssignable& operator=(const TriviallyMoveAssignable&) {
return *this;
}
};
struct TriviallyCopyAssignable {};
#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
TEST(VariantTest, TestTriviality) {
{
using TrivDestVar = absl::variant<TriviallyDestructible>;
EXPECT_FALSE(is_trivially_move_constructible<TrivDestVar>::value);
EXPECT_FALSE(absl::is_trivially_copy_constructible<TrivDestVar>::value);
EXPECT_FALSE(is_trivially_move_assignable<TrivDestVar>::value);
EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivDestVar>::value);
EXPECT_TRUE(absl::is_trivially_destructible<TrivDestVar>::value);
}
{
using TrivMoveVar = absl::variant<TriviallyMovable>;
EXPECT_TRUE(is_trivially_move_constructible<TrivMoveVar>::value);
EXPECT_FALSE(absl::is_trivially_copy_constructible<TrivMoveVar>::value);
EXPECT_FALSE(is_trivially_move_assignable<TrivMoveVar>::value);
EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivMoveVar>::value);
EXPECT_TRUE(absl::is_trivially_destructible<TrivMoveVar>::value);
}
{
using TrivCopyVar = absl::variant<TriviallyCopyable>;
EXPECT_TRUE(is_trivially_move_constructible<TrivCopyVar>::value);
EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivCopyVar>::value);
EXPECT_FALSE(is_trivially_move_assignable<TrivCopyVar>::value);
EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivCopyVar>::value);
EXPECT_TRUE(absl::is_trivially_destructible<TrivCopyVar>::value);
}
{
using TrivMoveAssignVar = absl::variant<TriviallyMoveAssignable>;
EXPECT_TRUE(is_trivially_move_constructible<TrivMoveAssignVar>::value);
EXPECT_FALSE(
absl::is_trivially_copy_constructible<TrivMoveAssignVar>::value);
EXPECT_TRUE(is_trivially_move_assignable<TrivMoveAssignVar>::value);
EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivMoveAssignVar>::value);
EXPECT_TRUE(absl::is_trivially_destructible<TrivMoveAssignVar>::value);
}
{
using TrivCopyAssignVar = absl::variant<TriviallyCopyAssignable>;
EXPECT_TRUE(is_trivially_move_constructible<TrivCopyAssignVar>::value);
EXPECT_TRUE(
absl::is_trivially_copy_constructible<TrivCopyAssignVar>::value);
EXPECT_TRUE(is_trivially_move_assignable<TrivCopyAssignVar>::value);
EXPECT_TRUE(absl::is_trivially_copy_assignable<TrivCopyAssignVar>::value);
EXPECT_TRUE(absl::is_trivially_destructible<TrivCopyAssignVar>::value);
}
}
#endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
// To verify that absl::variant correctly use the nontrivial move ctor of its
// member rather than use the trivial copy constructor.
TEST(VariantTest, MoveCtorBug) {
// To simulate std::tuple in libstdc++.
struct TrivialCopyNontrivialMove {
TrivialCopyNontrivialMove() = default;
TrivialCopyNontrivialMove(const TrivialCopyNontrivialMove&) = default;
TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) { called = true; }
bool called = false;
};
{
using V = absl::variant<TrivialCopyNontrivialMove, int>;
V v1(absl::in_place_index_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<int, TrivialCopyNontrivialMove>;
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
......@@ -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 <typename T>
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 <size_t I>
struct in_place_index_t {};
#endif // ABSL_HAVE_STD_VARIANT
// Constexpr move and forward
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment