Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,670 @@
//
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// type_traits.h
// -----------------------------------------------------------------------------
//
// This file contains C++11-compatible versions of standard <type_traits> API
// functions for determining the characteristics of types. Such traits can
// support type inference, classification, and transformation, as well as
// make it easier to write templates based on generic type behavior.
//
// See https://en.cppreference.com/w/cpp/header/type_traits
//
// WARNING: use of many of the constructs in this header will count as "complex
// template metaprogramming", so before proceeding, please carefully consider
// https://google.github.io/styleguide/cppguide.html#Template_metaprogramming
//
// WARNING: using template metaprogramming to detect or depend on API
// features is brittle and not guaranteed. Neither the standard library nor
// Abseil provides any guarantee that APIs are stable in the face of template
// metaprogramming. Use with caution.
#ifndef ABSL_META_TYPE_TRAITS_H_
#define ABSL_META_TYPE_TRAITS_H_
#include <cstddef>
#include <functional>
#include <string>
#include <type_traits>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#ifdef __cpp_lib_span
#include <span> // NOLINT(build/c++20)
#endif
#ifdef ABSL_HAVE_STD_STRING_VIEW
#include <string_view>
#endif
// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17
// feature.
#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__
#else // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t)
#endif // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace type_traits_internal {
template <typename... Ts>
struct VoidTImpl {
using type = void;
};
////////////////////////////////
// Library Fundamentals V2 TS //
////////////////////////////////
// NOTE: The `is_detected` family of templates here differ from the library
// fundamentals specification in that for library fundamentals, `Op<Args...>` is
// evaluated as soon as the type `is_detected<Op, Args...>` undergoes
// substitution, regardless of whether or not the `::value` is accessed. That
// is inconsistent with all other standard traits and prevents lazy evaluation
// in larger contexts (such as if the `is_detected` check is a trailing argument
// of a `conjunction`. This implementation opts to instead be lazy in the same
// way that the standard traits are (this "defect" of the detection idiom
// specifications has been reported).
template <class Enabler, template <class...> class Op, class... Args>
struct is_detected_impl {
using type = std::false_type;
};
template <template <class...> class Op, class... Args>
struct is_detected_impl<typename VoidTImpl<Op<Args...>>::type, Op, Args...> {
using type = std::true_type;
};
template <template <class...> class Op, class... Args>
struct is_detected : is_detected_impl<void, Op, Args...>::type {};
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<
typename std::enable_if<std::is_convertible<Op<Args...>, To>::value>::type,
To, Op, Args...> {
using type = std::true_type;
};
template <class To, template <class...> class Op, class... Args>
struct is_detected_convertible
: is_detected_convertible_impl<void, To, Op, Args...>::type {};
} // namespace type_traits_internal
// void_t()
//
// Ignores the type of any its arguments and returns `void`. In general, this
// metafunction allows you to create a general case that maps to `void` while
// allowing specializations that map to specific types.
//
// This metafunction is designed to be a drop-in replacement for the C++17
// `std::void_t` metafunction.
//
// NOTE: `absl::void_t` does not use the standard-specified implementation so
// that it can remain compatible with gcc < 5.1. This can introduce slightly
// different behavior, such as when ordering partial specializations.
template <typename... Ts>
using void_t = typename type_traits_internal::VoidTImpl<Ts...>::type;
// conjunction
//
// Performs a compile-time logical AND operation on the passed types (which
// must have `::value` members convertible to `bool`. Short-circuits if it
// encounters any `false` members (and does not compare the `::value` members
// of any remaining arguments).
//
// This metafunction is designed to be a drop-in replacement for the C++17
// `std::conjunction` metafunction.
template <typename... Ts>
struct conjunction : std::true_type {};
template <typename T, typename... Ts>
struct conjunction<T, Ts...>
: std::conditional<T::value, conjunction<Ts...>, T>::type {};
template <typename T>
struct conjunction<T> : T {};
// disjunction
//
// Performs a compile-time logical OR operation on the passed types (which
// must have `::value` members convertible to `bool`. Short-circuits if it
// encounters any `true` members (and does not compare the `::value` members
// of any remaining arguments).
//
// This metafunction is designed to be a drop-in replacement for the C++17
// `std::disjunction` metafunction.
template <typename... Ts>
struct disjunction : std::false_type {};
template <typename T, typename... Ts>
struct disjunction<T, Ts...>
: std::conditional<T::value, T, disjunction<Ts...>>::type {};
template <typename T>
struct disjunction<T> : T {};
// negation
//
// Performs a compile-time logical NOT operation on the passed type (which
// must have `::value` members convertible to `bool`.
//
// This metafunction is designed to be a drop-in replacement for the C++17
// `std::negation` metafunction.
template <typename T>
struct negation : std::integral_constant<bool, !T::value> {};
// is_function()
//
// Determines whether the passed type `T` is a function type.
//
// This metafunction is designed to be a drop-in replacement for the C++11
// `std::is_function()` metafunction for platforms that have incomplete C++11
// support (such as libstdc++ 4.x).
//
// This metafunction works because appending `const` to a type does nothing to
// function types and reference types (and forms a const-qualified type
// otherwise).
template <typename T>
struct is_function
: std::integral_constant<
bool, !(std::is_reference<T>::value ||
std::is_const<typename std::add_const<T>::type>::value)> {};
// is_copy_assignable()
// is_move_assignable()
// is_trivially_destructible()
// is_trivially_default_constructible()
// is_trivially_move_constructible()
// is_trivially_copy_constructible()
// is_trivially_move_assignable()
// is_trivially_copy_assignable()
//
// Historical note: Abseil once provided implementations of these type traits
// for platforms that lacked full support. New code should prefer to use the
// std variants.
//
// See the documentation for the STL <type_traits> header for more information:
// https://en.cppreference.com/w/cpp/header/type_traits
using std::is_copy_assignable;
using std::is_move_assignable;
using std::is_trivially_copy_assignable;
using std::is_trivially_copy_constructible;
using std::is_trivially_default_constructible;
using std::is_trivially_destructible;
using std::is_trivially_move_assignable;
using std::is_trivially_move_constructible;
#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L
template <typename T>
using remove_cvref = std::remove_cvref<T>;
template <typename T>
using remove_cvref_t = typename std::remove_cvref<T>::type;
#else
// remove_cvref()
//
// C++11 compatible implementation of std::remove_cvref which was added in
// C++20.
template <typename T>
struct remove_cvref {
using type =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};
template <typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
#endif
// -----------------------------------------------------------------------------
// C++14 "_t" trait aliases
// -----------------------------------------------------------------------------
template <typename T>
using remove_cv_t = typename std::remove_cv<T>::type;
template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_volatile_t = typename std::remove_volatile<T>::type;
template <typename T>
using add_cv_t = typename std::add_cv<T>::type;
template <typename T>
using add_const_t = typename std::add_const<T>::type;
template <typename T>
using add_volatile_t = typename std::add_volatile<T>::type;
template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
template <typename T>
using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;
template <typename T>
using remove_pointer_t = typename std::remove_pointer<T>::type;
template <typename T>
using add_pointer_t = typename std::add_pointer<T>::type;
template <typename T>
using make_signed_t = typename std::make_signed<T>::type;
template <typename T>
using make_unsigned_t = typename std::make_unsigned<T>::type;
template <typename T>
using remove_extent_t = typename std::remove_extent<T>::type;
template <typename T>
using remove_all_extents_t = typename std::remove_all_extents<T>::type;
template <typename T>
using decay_t = typename std::decay<T>::type;
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <typename... T>
using common_type_t = typename std::common_type<T...>::type;
template <typename T>
using underlying_type_t = typename std::underlying_type<T>::type;
namespace type_traits_internal {
#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
// std::result_of is deprecated (C++17) or removed (C++20)
template <typename>
struct result_of;
template <typename F, typename... Args>
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
#else
template <typename F>
using result_of = std::result_of<F>;
#endif
} // namespace type_traits_internal
template <typename F>
using result_of_t = typename type_traits_internal::result_of<F>::type;
namespace type_traits_internal {
// In MSVC we can't probe std::hash or stdext::hash because it triggers a
// static_assert instead of failing substitution. Libc++ prior to 4.0
// also used a static_assert.
//
#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \
_LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11)
#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 0
#else
#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 1
#endif
#if !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
template <typename Key, typename = size_t>
struct IsHashable : std::true_type {};
#else // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
template <typename Key, typename = void>
struct IsHashable : std::false_type {};
template <typename Key>
struct IsHashable<
Key,
absl::enable_if_t<std::is_convertible<
decltype(std::declval<std::hash<Key>&>()(std::declval<Key const&>())),
std::size_t>::value>> : std::true_type {};
#endif // !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
struct AssertHashEnabledHelper {
private:
static void Sink(...) {}
struct NAT {};
template <class Key>
static auto GetReturnType(int)
-> decltype(std::declval<std::hash<Key>>()(std::declval<Key const&>()));
template <class Key>
static NAT GetReturnType(...);
template <class Key>
static std::nullptr_t DoIt() {
static_assert(IsHashable<Key>::value,
"std::hash<Key> does not provide a call operator");
static_assert(
std::is_default_constructible<std::hash<Key>>::value,
"std::hash<Key> must be default constructible when it is enabled");
static_assert(
std::is_copy_constructible<std::hash<Key>>::value,
"std::hash<Key> must be copy constructible when it is enabled");
static_assert(absl::is_copy_assignable<std::hash<Key>>::value,
"std::hash<Key> must be copy assignable when it is enabled");
// is_destructible is unchecked as it's implied by each of the
// is_constructible checks.
using ReturnType = decltype(GetReturnType<Key>(0));
static_assert(std::is_same<ReturnType, NAT>::value ||
std::is_same<ReturnType, size_t>::value,
"std::hash<Key> must return size_t");
return nullptr;
}
template <class... Ts>
friend void AssertHashEnabled();
};
template <class... Ts>
inline void AssertHashEnabled() {
using Helper = AssertHashEnabledHelper;
Helper::Sink(Helper::DoIt<Ts>()...);
}
} // namespace type_traits_internal
// An internal namespace that is required to implement the C++17 swap traits.
// It is not further nested in type_traits_internal to avoid long symbol names.
namespace swap_internal {
// Necessary for the traits.
using std::swap;
// This declaration prevents global `swap` and `absl::swap` overloads from being
// considered unless ADL picks them up.
void swap();
template <class T>
using IsSwappableImpl = decltype(swap(std::declval<T&>(), std::declval<T&>()));
// NOTE: This dance with the default template parameter is for MSVC.
template <class T,
class IsNoexcept = std::integral_constant<
bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))>>
using IsNothrowSwappableImpl = typename std::enable_if<IsNoexcept::value>::type;
// IsSwappable
//
// Determines whether the standard swap idiom is a valid expression for
// arguments of type `T`.
template <class T>
struct IsSwappable
: absl::type_traits_internal::is_detected<IsSwappableImpl, T> {};
// IsNothrowSwappable
//
// Determines whether the standard swap idiom is a valid expression for
// arguments of type `T` and is noexcept.
template <class T>
struct IsNothrowSwappable
: absl::type_traits_internal::is_detected<IsNothrowSwappableImpl, T> {};
// Swap()
//
// Performs the swap idiom from a namespace where valid candidates may only be
// found in `std` or via ADL.
template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0>
void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) {
swap(lhs, rhs);
}
// StdSwapIsUnconstrained
//
// Some standard library implementations are broken in that they do not
// constrain `std::swap`. This will effectively tell us if we are dealing with
// one of those implementations.
using StdSwapIsUnconstrained = IsSwappable<void()>;
} // namespace swap_internal
namespace type_traits_internal {
// Make the swap-related traits/function accessible from this namespace.
using swap_internal::IsNothrowSwappable;
using swap_internal::IsSwappable;
using swap_internal::StdSwapIsUnconstrained;
using swap_internal::Swap;
} // namespace type_traits_internal
// absl::is_trivially_relocatable<T>
//
// Detects whether a type is known to be "trivially relocatable" -- meaning it
// can be relocated from one place to another as if by memcpy/memmove.
// This implies that its object representation doesn't depend on its address,
// and also none of its special member functions do anything strange.
//
// This trait is conservative. If it's true then the type is definitely
// trivially relocatable, but if it's false then the type may or may not be. For
// example, std::vector<int> is trivially relocatable on every known STL
// implementation, but absl::is_trivially_relocatable<std::vector<int>> remains
// false.
//
// Example:
//
// if constexpr (absl::is_trivially_relocatable<T>::value) {
// memcpy(new_location, old_location, sizeof(T));
// } else {
// new(new_location) T(std::move(*old_location));
// old_location->~T();
// }
//
// Upstream documentation:
//
// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable
// If the compiler offers a builtin that tells us the answer, we can use that.
// This covers all of the cases in the fallback below, plus types that opt in
// using e.g. [[clang::trivial_abi]].
//
// Clang on Windows has the builtin, but it falsely claims types with a
// user-provided destructor are trivial (http://b/275003464). So we opt out
// there.
//
// TODO(b/275003464): remove the opt-out once the bug is fixed.
//
// Starting with Xcode 15, the Apple compiler will falsely say a type
// with a user-provided move constructor is trivially relocatable
// (b/324278148). We will opt out without a version check, due to
// the fluidity of Apple versions.
//
// TODO(b/324278148): If all versions we use have the bug fixed, then
// remove the condition.
//
// Clang on all platforms fails to detect that a type with a user-provided
// move-assignment operator is not trivially relocatable so we also check for
// is_trivially_move_assignable for Clang.
//
// TODO(b/325479096): Remove the Clang is_trivially_move_assignable version once
// Clang's behavior is fixed.
//
// According to https://github.com/abseil/abseil-cpp/issues/1479, this does not
// work with NVCC either.
#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
(defined(__cpp_impl_trivially_relocatable) || \
(!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
template <class T>
struct is_trivially_relocatable
: std::integral_constant<bool, __is_trivially_relocatable(T)> {};
#elif ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && defined(__clang__) && \
!(defined(_WIN32) || defined(_WIN64)) && !defined(__APPLE__) && \
!defined(__NVCC__)
template <class T>
struct is_trivially_relocatable
: std::integral_constant<
bool, std::is_trivially_copyable<T>::value ||
(__is_trivially_relocatable(T) &&
std::is_trivially_move_assignable<T>::value)> {};
#else
// Otherwise we use a fallback that detects only those types we can feasibly
// detect. Any type that is trivially copyable is by definition trivially
// relocatable.
template <class T>
struct is_trivially_relocatable : std::is_trivially_copyable<T> {};
#endif
// absl::is_constant_evaluated()
//
// Detects whether the function call occurs within a constant-evaluated context.
// Returns true if the evaluation of the call occurs within the evaluation of an
// expression or conversion that is manifestly constant-evaluated; otherwise
// returns false.
//
// This function is implemented in terms of `std::is_constant_evaluated` for
// c++20 and up. For older c++ versions, the function is implemented in terms
// of `__builtin_is_constant_evaluated` if available, otherwise the function
// will fail to compile.
//
// Applications can inspect `ABSL_HAVE_CONSTANT_EVALUATED` at compile time
// to check if this function is supported.
//
// Example:
//
// constexpr MyClass::MyClass(int param) {
// #ifdef ABSL_HAVE_CONSTANT_EVALUATED
// if (!absl::is_constant_evaluated()) {
// ABSL_LOG(INFO) << "MyClass(" << param << ")";
// }
// #endif // ABSL_HAVE_CONSTANT_EVALUATED
// }
//
// Upstream documentation:
//
// http://en.cppreference.com/w/cpp/types/is_constant_evaluated
// http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#:~:text=__builtin_is_constant_evaluated
//
#if defined(ABSL_HAVE_CONSTANT_EVALUATED)
constexpr bool is_constant_evaluated() noexcept {
#ifdef __cpp_lib_is_constant_evaluated
return std::is_constant_evaluated();
#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated)
return __builtin_is_constant_evaluated();
#endif
}
#endif // ABSL_HAVE_CONSTANT_EVALUATED
namespace type_traits_internal {
// Detects if a class's definition has declared itself to be an owner by
// declaring
// using absl_internal_is_view = std::true_type;
// as a member.
// Types that don't want either must either omit this declaration entirely, or
// (if e.g. inheriting from a base class) define the member to something that
// isn't a Boolean trait class, such as `void`.
// Do not specialize or use this directly. It's an implementation detail.
template <typename T, typename = void>
struct IsOwnerImpl : std::false_type {
static_assert(std::is_same<T, absl::remove_cvref_t<T>>::value,
"type must lack qualifiers");
};
template <typename T>
struct IsOwnerImpl<
T,
std::enable_if_t<std::is_class<typename T::absl_internal_is_view>::value>>
: absl::negation<typename T::absl_internal_is_view> {};
// A trait to determine whether a type is an owner.
// Do *not* depend on the correctness of this trait for correct code behavior.
// It is only a safety feature and its value may change in the future.
// Do not specialize this; instead, define the member trait inside your type so
// that it can be auto-detected, and to prevent ODR violations.
// If it ever becomes possible to detect [[gsl::Owner]], we should leverage it:
// https://wg21.link/p1179
template <typename T>
struct IsOwner : IsOwnerImpl<T> {};
template <typename T, typename Traits, typename Alloc>
struct IsOwner<std::basic_string<T, Traits, Alloc>> : std::true_type {};
template <typename T, typename Alloc>
struct IsOwner<std::vector<T, Alloc>> : std::true_type {};
// Detects if a class's definition has declared itself to be a view by declaring
// using absl_internal_is_view = std::true_type;
// as a member.
// Do not specialize or use this directly.
template <typename T, typename = void>
struct IsViewImpl : std::false_type {
static_assert(std::is_same<T, absl::remove_cvref_t<T>>::value,
"type must lack qualifiers");
};
template <typename T>
struct IsViewImpl<
T,
std::enable_if_t<std::is_class<typename T::absl_internal_is_view>::value>>
: T::absl_internal_is_view {};
// A trait to determine whether a type is a view.
// Do *not* depend on the correctness of this trait for correct code behavior.
// It is only a safety feature, and its value may change in the future.
// Do not specialize this trait. Instead, define the member
// using absl_internal_is_view = std::true_type;
// in your class to allow its detection while preventing ODR violations.
// If it ever becomes possible to detect [[gsl::Pointer]], we should leverage
// it: https://wg21.link/p1179
template <typename T>
struct IsView : std::integral_constant<bool, std::is_pointer<T>::value ||
IsViewImpl<T>::value> {};
#ifdef ABSL_HAVE_STD_STRING_VIEW
template <typename Char, typename Traits>
struct IsView<std::basic_string_view<Char, Traits>> : std::true_type {};
#endif
#ifdef __cpp_lib_span
template <typename T>
struct IsView<std::span<T>> : std::true_type {};
#endif
// Determines whether the assignment of the given types is lifetime-bound.
// Do *not* depend on the correctness of this trait for correct code behavior.
// It is only a safety feature and its value may change in the future.
// If it ever becomes possible to detect [[clang::lifetimebound]] directly,
// we should change the implementation to leverage that.
// Until then, we consider an assignment from an "owner" (such as std::string)
// to a "view" (such as std::string_view) to be a lifetime-bound assignment.
template <typename T, typename U>
using IsLifetimeBoundAssignment = absl::conjunction<
std::integral_constant<bool, !std::is_lvalue_reference<U>::value>,
IsOwner<absl::remove_cvref_t<U>>, IsView<absl::remove_cvref_t<T>>>;
} // namespace type_traits_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_META_TYPE_TRAITS_H_

View file

@ -0,0 +1,841 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/meta/type_traits.h"
#include <cstdint>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#ifdef ABSL_HAVE_STD_STRING_VIEW
#include <string_view>
#endif
namespace {
using ::testing::StaticAssertTypeEq;
template <typename T>
using IsOwnerAndNotView =
absl::conjunction<absl::type_traits_internal::IsOwner<T>,
absl::negation<absl::type_traits_internal::IsView<T>>>;
static_assert(IsOwnerAndNotView<std::vector<int>>::value,
"vector is an owner, not a view");
static_assert(IsOwnerAndNotView<std::string>::value,
"string is an owner, not a view");
static_assert(IsOwnerAndNotView<std::wstring>::value,
"wstring is an owner, not a view");
#ifdef ABSL_HAVE_STD_STRING_VIEW
static_assert(!IsOwnerAndNotView<std::string_view>::value,
"string_view is a view, not an owner");
static_assert(!IsOwnerAndNotView<std::wstring_view>::value,
"wstring_view is a view, not an owner");
#endif
template <class T, class U>
struct simple_pair {
T first;
U second;
};
struct Dummy {};
struct ReturnType {};
struct ConvertibleToReturnType {
operator ReturnType() const; // NOLINT
};
// Unique types used as parameter types for testing the detection idiom.
struct StructA {};
struct StructB {};
struct StructC {};
struct TypeWithBarFunction {
template <class T,
absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
ReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT
};
struct TypeWithBarFunctionAndConvertibleReturnType {
template <class T,
absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
ConvertibleToReturnType bar(T&&, const StructB&, StructC&&) &&; // NOLINT
};
template <class Class, class... Ts>
using BarIsCallableImpl =
decltype(std::declval<Class>().bar(std::declval<Ts>()...));
template <class Class, class... T>
using BarIsCallable =
absl::type_traits_internal::is_detected<BarIsCallableImpl, Class, T...>;
template <class Class, class... T>
using BarIsCallableConv = absl::type_traits_internal::is_detected_convertible<
ReturnType, BarIsCallableImpl, Class, T...>;
// NOTE: Test of detail type_traits_internal::is_detected.
TEST(IsDetectedTest, BasicUsage) {
EXPECT_TRUE((BarIsCallable<TypeWithBarFunction, StructA&, const StructB&,
StructC>::value));
EXPECT_TRUE(
(BarIsCallable<TypeWithBarFunction, StructA&, StructB&, StructC>::value));
EXPECT_TRUE(
(BarIsCallable<TypeWithBarFunction, StructA&, StructB, StructC>::value));
EXPECT_FALSE((BarIsCallable<int, StructA&, const StructB&, StructC>::value));
EXPECT_FALSE((BarIsCallable<TypeWithBarFunction&, StructA&, const StructB&,
StructC>::value));
EXPECT_FALSE((BarIsCallable<TypeWithBarFunction, StructA, const StructB&,
StructC>::value));
}
// NOTE: Test of detail type_traits_internal::is_detected_convertible.
TEST(IsDetectedConvertibleTest, BasicUsage) {
EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, const StructB&,
StructC>::value));
EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, StructB&,
StructC>::value));
EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, StructB,
StructC>::value));
EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
StructA&, const StructB&, StructC>::value));
EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
StructA&, StructB&, StructC>::value));
EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
StructA&, StructB, StructC>::value));
EXPECT_FALSE(
(BarIsCallableConv<int, StructA&, const StructB&, StructC>::value));
EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunction&, StructA&,
const StructB&, StructC>::value));
EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunction, StructA, const StructB&,
StructC>::value));
EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType&,
StructA&, const StructB&, StructC>::value));
EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
StructA, const StructB&, StructC>::value));
}
TEST(VoidTTest, BasicUsage) {
StaticAssertTypeEq<void, absl::void_t<Dummy>>();
StaticAssertTypeEq<void, absl::void_t<Dummy, Dummy, Dummy>>();
}
TEST(ConjunctionTest, BasicBooleanLogic) {
EXPECT_TRUE(absl::conjunction<>::value);
EXPECT_TRUE(absl::conjunction<std::true_type>::value);
EXPECT_TRUE((absl::conjunction<std::true_type, std::true_type>::value));
EXPECT_FALSE((absl::conjunction<std::true_type, std::false_type>::value));
EXPECT_FALSE((absl::conjunction<std::false_type, std::true_type>::value));
EXPECT_FALSE((absl::conjunction<std::false_type, std::false_type>::value));
}
struct MyTrueType {
static constexpr bool value = true;
};
struct MyFalseType {
static constexpr bool value = false;
};
TEST(ConjunctionTest, ShortCircuiting) {
EXPECT_FALSE(
(absl::conjunction<std::true_type, std::false_type, Dummy>::value));
EXPECT_TRUE((std::is_base_of<MyFalseType,
absl::conjunction<std::true_type, MyFalseType,
std::false_type>>::value));
EXPECT_TRUE(
(std::is_base_of<MyTrueType,
absl::conjunction<std::true_type, MyTrueType>>::value));
}
TEST(DisjunctionTest, BasicBooleanLogic) {
EXPECT_FALSE(absl::disjunction<>::value);
EXPECT_FALSE(absl::disjunction<std::false_type>::value);
EXPECT_TRUE((absl::disjunction<std::true_type, std::true_type>::value));
EXPECT_TRUE((absl::disjunction<std::true_type, std::false_type>::value));
EXPECT_TRUE((absl::disjunction<std::false_type, std::true_type>::value));
EXPECT_FALSE((absl::disjunction<std::false_type, std::false_type>::value));
}
TEST(DisjunctionTest, ShortCircuiting) {
EXPECT_TRUE(
(absl::disjunction<std::false_type, std::true_type, Dummy>::value));
EXPECT_TRUE((
std::is_base_of<MyTrueType, absl::disjunction<std::false_type, MyTrueType,
std::true_type>>::value));
EXPECT_TRUE((
std::is_base_of<MyFalseType,
absl::disjunction<std::false_type, MyFalseType>>::value));
}
TEST(NegationTest, BasicBooleanLogic) {
EXPECT_FALSE(absl::negation<std::true_type>::value);
EXPECT_FALSE(absl::negation<MyTrueType>::value);
EXPECT_TRUE(absl::negation<std::false_type>::value);
EXPECT_TRUE(absl::negation<MyFalseType>::value);
}
// all member functions are trivial
class Trivial {
int n_;
};
struct TrivialDestructor {
~TrivialDestructor() = default;
};
struct NontrivialDestructor {
~NontrivialDestructor() {}
};
struct DeletedDestructor {
~DeletedDestructor() = delete;
};
class TrivialDefaultCtor {
public:
TrivialDefaultCtor() = default;
explicit TrivialDefaultCtor(int n) : n_(n) {}
private:
int n_;
};
class NontrivialDefaultCtor {
public:
NontrivialDefaultCtor() : n_(1) {}
private:
int n_;
};
class DeletedDefaultCtor {
public:
DeletedDefaultCtor() = delete;
explicit DeletedDefaultCtor(int n) : n_(n) {}
private:
int n_;
};
class TrivialMoveCtor {
public:
explicit TrivialMoveCtor(int n) : n_(n) {}
TrivialMoveCtor(TrivialMoveCtor&&) = default;
TrivialMoveCtor& operator=(const TrivialMoveCtor& t) {
n_ = t.n_;
return *this;
}
private:
int n_;
};
class NontrivialMoveCtor {
public:
explicit NontrivialMoveCtor(int n) : n_(n) {}
NontrivialMoveCtor(NontrivialMoveCtor&& t) noexcept : n_(t.n_) {}
NontrivialMoveCtor& operator=(const NontrivialMoveCtor&) = default;
private:
int n_;
};
class TrivialCopyCtor {
public:
explicit TrivialCopyCtor(int n) : n_(n) {}
TrivialCopyCtor(const TrivialCopyCtor&) = default;
TrivialCopyCtor& operator=(const TrivialCopyCtor& t) {
n_ = t.n_;
return *this;
}
private:
int n_;
};
class NontrivialCopyCtor {
public:
explicit NontrivialCopyCtor(int n) : n_(n) {}
NontrivialCopyCtor(const NontrivialCopyCtor& t) : n_(t.n_) {}
NontrivialCopyCtor& operator=(const NontrivialCopyCtor&) = default;
private:
int n_;
};
class DeletedCopyCtor {
public:
explicit DeletedCopyCtor(int n) : n_(n) {}
DeletedCopyCtor(const DeletedCopyCtor&) = delete;
DeletedCopyCtor& operator=(const DeletedCopyCtor&) = default;
private:
int n_;
};
class TrivialMoveAssign {
public:
explicit TrivialMoveAssign(int n) : n_(n) {}
TrivialMoveAssign(const TrivialMoveAssign& t) : n_(t.n_) {}
TrivialMoveAssign& operator=(TrivialMoveAssign&&) = default;
~TrivialMoveAssign() {} // can have nontrivial destructor
private:
int n_;
};
class NontrivialMoveAssign {
public:
explicit NontrivialMoveAssign(int n) : n_(n) {}
NontrivialMoveAssign(const NontrivialMoveAssign&) = default;
NontrivialMoveAssign& operator=(NontrivialMoveAssign&& t) noexcept {
n_ = t.n_;
return *this;
}
private:
int n_;
};
class TrivialCopyAssign {
public:
explicit TrivialCopyAssign(int n) : n_(n) {}
TrivialCopyAssign(const TrivialCopyAssign& t) : n_(t.n_) {}
TrivialCopyAssign& operator=(const TrivialCopyAssign& t) = default;
~TrivialCopyAssign() {} // can have nontrivial destructor
private:
int n_;
};
class NontrivialCopyAssign {
public:
explicit NontrivialCopyAssign(int n) : n_(n) {}
NontrivialCopyAssign(const NontrivialCopyAssign&) = default;
NontrivialCopyAssign& operator=(const NontrivialCopyAssign& t) {
n_ = t.n_;
return *this;
}
private:
int n_;
};
class DeletedCopyAssign {
public:
explicit DeletedCopyAssign(int n) : n_(n) {}
DeletedCopyAssign(const DeletedCopyAssign&) = default;
DeletedCopyAssign& operator=(const DeletedCopyAssign&) = delete;
private:
int n_;
};
struct MovableNonCopyable {
MovableNonCopyable() = default;
MovableNonCopyable(const MovableNonCopyable&) = delete;
MovableNonCopyable(MovableNonCopyable&&) = default;
MovableNonCopyable& operator=(const MovableNonCopyable&) = delete;
MovableNonCopyable& operator=(MovableNonCopyable&&) = default;
};
struct NonCopyableOrMovable {
NonCopyableOrMovable() = default;
virtual ~NonCopyableOrMovable() = default;
NonCopyableOrMovable(const NonCopyableOrMovable&) = delete;
NonCopyableOrMovable(NonCopyableOrMovable&&) = delete;
NonCopyableOrMovable& operator=(const NonCopyableOrMovable&) = delete;
NonCopyableOrMovable& operator=(NonCopyableOrMovable&&) = delete;
};
class Base {
public:
virtual ~Base() {}
};
TEST(TypeTraitsTest, TestIsFunction) {
struct Callable {
void operator()() {}
};
EXPECT_TRUE(absl::is_function<void()>::value);
EXPECT_TRUE(absl::is_function<void()&>::value);
EXPECT_TRUE(absl::is_function<void() const>::value);
EXPECT_TRUE(absl::is_function<void() noexcept>::value);
EXPECT_TRUE(absl::is_function<void(...) noexcept>::value);
EXPECT_FALSE(absl::is_function<void (*)()>::value);
EXPECT_FALSE(absl::is_function<void (&)()>::value);
EXPECT_FALSE(absl::is_function<int>::value);
EXPECT_FALSE(absl::is_function<Callable>::value);
}
TEST(TypeTraitsTest, TestRemoveCVRef) {
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int&>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int&&>::type, int>::value));
EXPECT_TRUE((
std::is_same<typename absl::remove_cvref<const int&>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int*>::type, int*>::value));
// Does not remove const in this case.
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type,
const int*>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int[2]>::type, int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&&)[2]>::type,
int[2]>::value));
}
#define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...) \
EXPECT_TRUE((std::is_same<typename std::trait_name<__VA_ARGS__>::type, \
absl::trait_name##_t<__VA_ARGS__>>::value))
TEST(TypeTraitsTest, TestRemoveCVAliases) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, const volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, const volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, const volatile int);
}
TEST(TypeTraitsTest, TestAddCVAliases) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, const volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, const volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, const volatile int);
}
TEST(TypeTraitsTest, TestReferenceAliases) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, int&&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, volatile int&&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, int&&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, volatile int&&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, int&&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, volatile int&&);
}
TEST(TypeTraitsTest, TestPointerAliases) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_pointer, int*);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_pointer, volatile int*);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_pointer, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_pointer, volatile int);
}
TEST(TypeTraitsTest, TestSignednessAliases) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, unsigned);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, volatile unsigned);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, unsigned);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, volatile unsigned);
}
TEST(TypeTraitsTest, TestExtentAliases) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[1][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[1][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[][1]);
}
TEST(TypeTraitsTest, TestDecay) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const volatile int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const volatile int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[1][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int());
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float)); // NOLINT
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(char, ...)); // NOLINT
}
struct TypeA {};
struct TypeB {};
struct TypeC {};
struct TypeD {};
template <typename T>
struct Wrap {};
enum class TypeEnum { A, B, C, D };
struct GetTypeT {
template <typename T,
absl::enable_if_t<std::is_same<T, TypeA>::value, int> = 0>
TypeEnum operator()(Wrap<T>) const {
return TypeEnum::A;
}
template <typename T,
absl::enable_if_t<std::is_same<T, TypeB>::value, int> = 0>
TypeEnum operator()(Wrap<T>) const {
return TypeEnum::B;
}
template <typename T,
absl::enable_if_t<std::is_same<T, TypeC>::value, int> = 0>
TypeEnum operator()(Wrap<T>) const {
return TypeEnum::C;
}
// NOTE: TypeD is intentionally not handled
} constexpr GetType = {};
TEST(TypeTraitsTest, TestEnableIf) {
EXPECT_EQ(TypeEnum::A, GetType(Wrap<TypeA>()));
EXPECT_EQ(TypeEnum::B, GetType(Wrap<TypeB>()));
EXPECT_EQ(TypeEnum::C, GetType(Wrap<TypeC>()));
}
TEST(TypeTraitsTest, TestConditional) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(conditional, true, int, char);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(conditional, false, int, char);
}
// TODO(calabrese) Check with specialized std::common_type
TEST(TypeTraitsTest, TestCommonType) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char, int);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char&);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char, int&);
}
TEST(TypeTraitsTest, TestUnderlyingType) {
enum class enum_char : char {};
enum class enum_long_long : long long {}; // NOLINT(runtime/int)
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(underlying_type, enum_char);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(underlying_type, enum_long_long);
}
struct GetTypeExtT {
template <typename T>
absl::result_of_t<const GetTypeT&(T)> operator()(T&& arg) const {
return GetType(std::forward<T>(arg));
}
TypeEnum operator()(Wrap<TypeD>) const { return TypeEnum::D; }
} constexpr GetTypeExt = {};
TEST(TypeTraitsTest, TestResultOf) {
EXPECT_EQ(TypeEnum::A, GetTypeExt(Wrap<TypeA>()));
EXPECT_EQ(TypeEnum::B, GetTypeExt(Wrap<TypeB>()));
EXPECT_EQ(TypeEnum::C, GetTypeExt(Wrap<TypeC>()));
EXPECT_EQ(TypeEnum::D, GetTypeExt(Wrap<TypeD>()));
}
namespace adl_namespace {
struct DeletedSwap {};
void swap(DeletedSwap&, DeletedSwap&) = delete;
struct SpecialNoexceptSwap {
SpecialNoexceptSwap(SpecialNoexceptSwap&&) {}
SpecialNoexceptSwap& operator=(SpecialNoexceptSwap&&) { return *this; }
~SpecialNoexceptSwap() = default;
};
void swap(SpecialNoexceptSwap&, SpecialNoexceptSwap&) noexcept {}
} // namespace adl_namespace
TEST(TypeTraitsTest, IsSwappable) {
using absl::type_traits_internal::IsSwappable;
using absl::type_traits_internal::StdSwapIsUnconstrained;
EXPECT_TRUE(IsSwappable<int>::value);
struct S {};
EXPECT_TRUE(IsSwappable<S>::value);
struct NoConstruct {
NoConstruct(NoConstruct&&) = delete;
NoConstruct& operator=(NoConstruct&&) { return *this; }
~NoConstruct() = default;
};
EXPECT_EQ(IsSwappable<NoConstruct>::value, StdSwapIsUnconstrained::value);
struct NoAssign {
NoAssign(NoAssign&&) {}
NoAssign& operator=(NoAssign&&) = delete;
~NoAssign() = default;
};
EXPECT_EQ(IsSwappable<NoAssign>::value, StdSwapIsUnconstrained::value);
EXPECT_FALSE(IsSwappable<adl_namespace::DeletedSwap>::value);
EXPECT_TRUE(IsSwappable<adl_namespace::SpecialNoexceptSwap>::value);
}
TEST(TypeTraitsTest, IsNothrowSwappable) {
using absl::type_traits_internal::IsNothrowSwappable;
using absl::type_traits_internal::StdSwapIsUnconstrained;
EXPECT_TRUE(IsNothrowSwappable<int>::value);
struct NonNoexceptMoves {
NonNoexceptMoves(NonNoexceptMoves&&) {}
NonNoexceptMoves& operator=(NonNoexceptMoves&&) { return *this; }
~NonNoexceptMoves() = default;
};
EXPECT_FALSE(IsNothrowSwappable<NonNoexceptMoves>::value);
struct NoConstruct {
NoConstruct(NoConstruct&&) = delete;
NoConstruct& operator=(NoConstruct&&) { return *this; }
~NoConstruct() = default;
};
EXPECT_FALSE(IsNothrowSwappable<NoConstruct>::value);
struct NoAssign {
NoAssign(NoAssign&&) {}
NoAssign& operator=(NoAssign&&) = delete;
~NoAssign() = default;
};
EXPECT_FALSE(IsNothrowSwappable<NoAssign>::value);
EXPECT_FALSE(IsNothrowSwappable<adl_namespace::DeletedSwap>::value);
EXPECT_TRUE(IsNothrowSwappable<adl_namespace::SpecialNoexceptSwap>::value);
}
TEST(TriviallyRelocatable, PrimitiveTypes) {
static_assert(absl::is_trivially_relocatable<int>::value, "");
static_assert(absl::is_trivially_relocatable<char>::value, "");
static_assert(absl::is_trivially_relocatable<void*>::value, "");
}
// User-defined types can be trivially relocatable as long as they don't have a
// user-provided move constructor or destructor.
TEST(TriviallyRelocatable, UserDefinedTriviallyRelocatable) {
struct S {
int x;
int y;
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided move constructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedMoveConstructor) {
struct S {
S(S&&) {} // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided copy constructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedCopyConstructor) {
struct S {
S(const S&) {} // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided copy assignment operator disqualifies a type from
// being trivially relocatable.
TEST(TriviallyRelocatable, UserProvidedCopyAssignment) {
struct S {
S(const S&) = default;
S& operator=(const S&) { // NOLINT(modernize-use-equals-default)
return *this;
}
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided move assignment operator disqualifies a type from
// being trivially relocatable.
TEST(TriviallyRelocatable, UserProvidedMoveAssignment) {
struct S {
S(S&&) = default;
S& operator=(S&&) { return *this; } // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// A user-provided destructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedDestructor) {
struct S {
~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
// TODO(b/275003464): remove the opt-out for Clang on Windows once
// __is_trivially_relocatable is used there again.
// TODO(b/324278148): remove the opt-out for Apple once
// __is_trivially_relocatable is fixed there.
// TODO(b/325479096): remove the opt-out for Clang once
// __is_trivially_relocatable is fixed there.
#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
(defined(__cpp_impl_trivially_relocatable) || \
(!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
// A type marked with the "trivial ABI" attribute is trivially relocatable even
// if it has user-provided special members.
TEST(TriviallyRelocatable, TrivialAbi) {
struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
S(S&&) {} // NOLINT(modernize-use-equals-default)
S(const S&) {} // NOLINT(modernize-use-equals-default)
S& operator=(S&&) { return *this; }
S& operator=(const S&) { return *this; }
~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
}
#endif
// TODO(b/275003464): remove the opt-out for Clang on Windows once
// __is_trivially_relocatable is used there again.
// TODO(b/324278148): remove the opt-out for Apple once
// __is_trivially_relocatable is fixed there.
#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && defined(__clang__) && \
!(defined(_WIN32) || defined(_WIN64)) && !defined(__APPLE__) && \
!defined(__NVCC__)
// A type marked with the "trivial ABI" attribute is trivially relocatable even
// if it has a user-provided copy constructor and a user-provided destructor.
TEST(TriviallyRelocatable, TrivialAbi_NoUserProvidedMove) {
struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
S(const S&) {} // NOLINT(modernize-use-equals-default)
~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
}
#endif
#ifdef ABSL_HAVE_CONSTANT_EVALUATED
constexpr int64_t NegateIfConstantEvaluated(int64_t i) {
if (absl::is_constant_evaluated()) {
return -i;
} else {
return i;
}
}
#endif // ABSL_HAVE_CONSTANT_EVALUATED
TEST(IsConstantEvaluated, is_constant_evaluated) {
#ifdef ABSL_HAVE_CONSTANT_EVALUATED
constexpr int64_t constant = NegateIfConstantEvaluated(42);
EXPECT_EQ(constant, -42);
int64_t now = absl::ToUnixSeconds(absl::Now());
int64_t not_constant = NegateIfConstantEvaluated(now);
EXPECT_EQ(not_constant, now);
static int64_t const_init = NegateIfConstantEvaluated(42);
EXPECT_EQ(const_init, -42);
#else
GTEST_SKIP() << "absl::is_constant_evaluated is not defined";
#endif // ABSL_HAVE_CONSTANT_EVALUATED
}
} // namespace