Add std::variant, std::tuple, std::array, std::reference_wrapper, and reference/const/volatile specializations for IsOwner and IsView Fixes: #1960 PiperOrigin-RevId: 953386746 Change-Id: If5184a5b5201964f3f5d8aeb48675a546afe26a9
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 78f7c86..6ca2dc8 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h
@@ -35,12 +35,15 @@ #ifndef ABSL_META_TYPE_TRAITS_H_ #define ABSL_META_TYPE_TRAITS_H_ +#include <array> #include <cstddef> #include <functional> #include <string> #include <string_view> +#include <tuple> #include <type_traits> #include <utility> +#include <variant> #include <vector> #include "absl/base/attributes.h" @@ -564,15 +567,32 @@ template <typename T> struct IsOwner : IsOwnerImpl<T> {}; +template <typename T> +struct IsOwner<T&> : std::false_type {}; + +template <typename T> +struct IsOwner<T&&> : std::false_type {}; + +template <typename T> +struct IsOwner<const T> : IsOwner<T> {}; + +template <typename T> +struct IsOwner<volatile T> : IsOwner<T> {}; + +template <typename T> +struct IsOwner<const volatile T> : IsOwner<T> {}; + +template <typename T> +struct IsOwner<std::reference_wrapper<T>> : std::false_type {}; + +template <typename T, std::size_t N> +struct IsOwner<std::array<T, N>> + : std::conditional_t<N != 0, IsOwner<T>, std::false_type> {}; + // This allows incomplete types to be used for associative containers, and also // expands the set of types we can handle to include std::pair. template <typename T1, typename T2> -struct IsOwner<std::pair<T1, T2>> - : std::bool_constant< - std::conditional_t<std::is_reference_v<T1>, std::false_type, - IsOwner<std::remove_cv_t<T1>>>::value && - std::conditional_t<std::is_reference_v<T2>, std::false_type, - IsOwner<std::remove_cv_t<T2>>>::value> {}; +struct IsOwner<std::pair<T1, T2>> : IsOwner<std::tuple<T1, T2>> {}; template <typename T, typename Traits, typename Alloc> struct IsOwner<std::basic_string<T, Traits, Alloc>> : std::true_type {}; @@ -580,6 +600,17 @@ template <typename T, typename Alloc> struct IsOwner<std::vector<T, Alloc>> : std::true_type {}; +template <typename... T> +struct IsOwner<std::tuple<T...>> + : std::bool_constant<(sizeof...(T) > 0) && + // Uses a C++17 fold expression where '...' unpacks the + // parameter pack T, and 'true &&' provides the base + // case for the logical AND operation across all types. + (true && ... && IsOwner<T>::value)> {}; + +template <typename... T> +struct IsOwner<std::variant<T...>> : IsOwner<std::tuple<T...>> {}; + // 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. @@ -607,16 +638,47 @@ struct IsView : std::bool_constant<std::is_pointer_v<T> || IsViewImpl<T>::value> {}; +template <typename T> +struct IsView<T&> : std::true_type {}; + +template <typename T> +struct IsView<T&&> : std::true_type {}; + +template <typename T> +struct IsView<const T> : IsView<T> {}; + +template <typename T> +struct IsView<volatile T> : IsView<T> {}; + +template <typename T> +struct IsView<const volatile T> : IsView<T> {}; + +template <typename T> +struct IsView<std::reference_wrapper<T>> : std::true_type {}; + +template <typename T, std::size_t N> +struct IsView<std::array<T, N>> + : std::conditional_t<N != 0, IsView<T>, std::false_type> {}; + // This allows incomplete types to be used for associative containers, and also // expands the set of types we can handle to include std::pair. template <typename T1, typename T2> -struct IsView<std::pair<T1, T2>> - : std::bool_constant<IsView<std::remove_cv_t<T1>>::value && - IsView<std::remove_cv_t<T2>>::value> {}; +struct IsView<std::pair<T1, T2>> : IsView<std::tuple<T1, T2>> {}; template <typename Char, typename Traits> struct IsView<std::basic_string_view<Char, Traits>> : std::true_type {}; +template <typename... T> +struct IsView<std::tuple<T...>> + : std::bool_constant<(sizeof...(T) > 0) && + // Uses a C++17 fold expression where '...' unpacks the + // parameter pack T, and 'true &&' provides the base + // case for the logical AND operation across all types. + (true && ... && IsView<T>::value)> {}; + +template <typename... T> +struct IsView<std::variant<T...>> : IsView<std::tuple<T...>> {}; + #ifdef __cpp_lib_span template <typename T> struct IsView<std::span<T>> : std::true_type {};
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index 9734fc1..3675a0f 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc
@@ -14,11 +14,15 @@ #include "absl/meta/type_traits.h" +#include <array> #include <cstdint> +#include <functional> #include <string> #include <string_view> +#include <tuple> #include <type_traits> #include <utility> +#include <variant> #include <vector> #include "gtest/gtest.h" @@ -32,6 +36,11 @@ using ::testing::StaticAssertTypeEq; template <typename T> +using IsViewAndNotOwner = + std::conjunction<absl::type_traits_internal::IsView<T>, + std::negation<absl::type_traits_internal::IsOwner<T>>>; + +template <typename T> using IsOwnerAndNotView = std::conjunction<absl::type_traits_internal::IsOwner<T>, std::negation<absl::type_traits_internal::IsView<T>>>; @@ -45,9 +54,9 @@ "string is an owner, not a view"); static_assert(IsOwnerAndNotView<std::wstring>::value, "wstring is an owner, not a view"); -static_assert(!IsOwnerAndNotView<std::string_view>::value, +static_assert(IsViewAndNotOwner<std::string_view>::value, "string_view is a view, not an owner"); -static_assert(!IsOwnerAndNotView<std::wstring_view>::value, +static_assert(IsViewAndNotOwner<std::wstring_view>::value, "wstring_view is a view, not an owner"); template <class T, class U> @@ -340,6 +349,104 @@ static_assert(!absl::is_trivially_relocatable<S>::value, ""); } +TEST(Ownership, References) { + static_assert(IsViewAndNotOwner<std::string&>::value, + "std::string& is a view, not an owner"); + static_assert(IsViewAndNotOwner<int&>::value, "int& is a view, not an owner"); + + static_assert(IsViewAndNotOwner<std::string&&>::value, + "std::string&& is a view, not an owner"); + static_assert(IsViewAndNotOwner<int&&>::value, + "int&& is a view, not an owner"); + + static_assert(IsViewAndNotOwner<std::reference_wrapper<std::string>>::value, + "std::reference_wrapper<std::string> is a view, not an owner"); + static_assert(IsViewAndNotOwner<std::reference_wrapper<int>>::value, + "std::reference_wrapper<int> is a view, not an owner"); +} + +TEST(Ownership, CVQualifiers) { + static_assert(IsOwnerAndNotView<const std::string>::value, + "const std::string is an owner, not a view"); + static_assert(IsViewAndNotOwner<const std::string_view>::value, + "const std::string_view is a view, not an owner"); + + static_assert(IsOwnerAndNotView<volatile std::string>::value, + "volatile std::string is an owner, not a view"); + static_assert(IsViewAndNotOwner<volatile std::string_view>::value, + "volatile std::string_view is a view, not an owner"); + + static_assert(IsOwnerAndNotView<const volatile std::string>::value, + "const volatile std::string is an owner, not a view"); + static_assert(IsViewAndNotOwner<const volatile std::string_view>::value, + "const volatile std::string_view is a view, not an owner"); +} + +TEST(Ownership, Array) { + static_assert(!IsOwnerAndNotView<std::array<std::string, 0>>::value, + "empty array is not an owner"); + static_assert(!IsViewAndNotOwner<std::array<std::string, 0>>::value, + "empty array is not a view"); + + static_assert(!IsOwnerAndNotView<std::array<std::string_view, 0>>::value, + "empty array is not an owner"); + static_assert(!IsViewAndNotOwner<std::array<std::string_view, 0>>::value, + "empty array is not a view"); + + static_assert(IsOwnerAndNotView<std::array<std::string, 5>>::value, + "array of owners is an owner"); + static_assert(IsViewAndNotOwner<std::array<std::string_view, 5>>::value, + "array of views is a view"); +} + +TEST(Ownership, Variant) { + static_assert(!IsOwnerAndNotView<std::variant<>>::value, + "empty variant is not an owner"); + static_assert(!IsViewAndNotOwner<std::variant<>>::value, + "empty variant is not a view"); + + static_assert(IsOwnerAndNotView<std::variant<std::string, std::vector<char>, + std::vector<int>>>::value, + "aggregate of owners is an owner"); + static_assert(IsViewAndNotOwner< + std::variant<std::wstring_view, std::string_view>>::value, + "aggregate of views is a view"); + + static_assert( + !IsOwnerAndNotView<std::variant<const char*, std::string>>::value, + "variant of mixed-ownership types is not considered an owner"); + static_assert( + !IsViewAndNotOwner<std::variant<const char*, std::string>>::value, + "variant of mixed-ownership types is not considered a view"); + + static_assert( + IsViewAndNotOwner<std::variant<std::reference_wrapper<const int>>>::value, + "variant of reference is considered a view"); +} + +TEST(Ownership, Tuple) { + static_assert(!IsOwnerAndNotView<std::tuple<>>::value, + "empty tuple is not an owner"); + static_assert(!IsViewAndNotOwner<std::tuple<>>::value, + "empty tuple is not a view"); + + static_assert( + IsOwnerAndNotView< + std::tuple<std::string, std::vector<char>, std::vector<int>>>::value, + "aggregate of owners is an owner"); + static_assert( + IsViewAndNotOwner<std::tuple<std::wstring_view, std::string_view>>::value, + "aggregate of views is a view"); + + static_assert(!IsOwnerAndNotView<std::tuple<const char*, std::string>>::value, + "tuple of mixed-ownership types is not considered an owner"); + static_assert(!IsViewAndNotOwner<std::tuple<const char*, std::string>>::value, + "tuple of mixed-ownership types is not considered a view"); + + static_assert(IsViewAndNotOwner<std::tuple<const int&>>::value, + "tuple of reference is considered a view"); +} + #ifdef ABSL_HAVE_CONSTANT_EVALUATED constexpr int64_t NegateIfConstantEvaluated(int64_t i) {