Переглянути джерело

type_traits: tuple-like type support for is_equality_comparable[_v]

Michele Caini 4 роки тому
батько
коміт
66b89d170f
2 змінених файлів з 51 додано та 29 видалено
  1. 43 28
      src/entt/core/type_traits.hpp
  2. 8 1
      test/entt/core/type_traits.cpp

+ 43 - 28
src/entt/core/type_traits.hpp

@@ -446,6 +446,14 @@ struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}
 template<typename Type>
 inline constexpr bool is_complete_v = is_complete<Type>::value;
 
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
 /**
  * @cond TURN_OFF_DOXYGEN
  * Internal details not to be documented.
@@ -454,10 +462,10 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
 namespace internal {
 
 template<typename, typename = void>
-struct is_iterator: std::false_type {};
+struct has_iterator_category: std::false_type {};
 
 template<typename Type>
-struct is_iterator<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
 
 } // namespace internal
 
@@ -466,18 +474,10 @@ struct is_iterator<Type, std::void_t<typename std::iterator_traits<Type>::iterat
  * @endcond
  */
 
-/**
- * @brief Provides the member constant `value` to true if a given type is an
- * iterator, false otherwise.
- * @tparam Type The type to test.
- */
-template<typename Type, typename = void>
-struct is_iterator: std::false_type {};
-
 /*! @copydoc is_iterator */
 template<typename Type>
 struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
-    : internal::is_iterator<Type> {};
+    : internal::has_iterator_category<Type> {};
 
 /**
  * @brief Helper variable template.
@@ -548,6 +548,14 @@ struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::tr
 template<typename Type>
 inline constexpr bool is_transparent_v = is_transparent<Type>::value;
 
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
 /**
  * @cond TURN_OFF_DOXYGEN
  * Internal details not to be documented.
@@ -555,30 +563,40 @@ inline constexpr bool is_transparent_v = is_transparent<Type>::value;
 
 namespace internal {
 
-template<typename>
-[[nodiscard]] constexpr bool is_equality_comparable(...) {
-    return false;
-}
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
 
 template<typename Type>
-[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval<Type>() == std::declval<Type>()) {
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+    return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
     return true;
 }
 
 template<typename Type>
-[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), std::declval<Type>() == std::declval<Type>()) {
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
     if constexpr(is_iterator_v<Type>) {
         return true;
     } else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
-        return is_equality_comparable<Type>(choice<0>);
+        return maybe_equality_comparable<Type>(choice<0>);
     } else {
-        return is_equality_comparable<typename Type::value_type>(choice<2>);
+        return is_equality_comparable<typename Type::value_type>::value;
     }
 }
 
 template<typename Type>
-[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval<typename Type::mapped_type>(), std::declval<Type>() == std::declval<Type>()) {
-    return is_equality_comparable<typename Type::key_type>(choice<2>) && is_equality_comparable<typename Type::mapped_type>(choice<2>);
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+    if constexpr(has_tuple_size_value<Type>::value) {
+        return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+    } else {
+        return maybe_equality_comparable<Type>(choice<1>);
+    }
 }
 
 } // namespace internal
@@ -588,13 +606,10 @@ template<typename Type>
  * @endcond
  */
 
-/**
- * @brief Provides the member constant `value` to true if a given type is
- * equality comparable, false otherwise.
- * @tparam Type The type to test.
- */
-template<typename Type, typename = void>
-struct is_equality_comparable: std::bool_constant<internal::is_equality_comparable<Type>(choice<2>)> {};
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+    : std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
 
 /**
  * @brief Helper variable template.

+ 8 - 1
test/entt/core/type_traits.cpp

@@ -105,18 +105,24 @@ TEST(TypeTraits, ValueList) {
 
 TEST(TypeTraits, IsEqualityComparable) {
     static_assert(entt::is_equality_comparable_v<int>);
+    static_assert(entt::is_equality_comparable_v<const int>);
     static_assert(entt::is_equality_comparable_v<std::vector<int>>);
     static_assert(entt::is_equality_comparable_v<std::vector<std::vector<int>>>);
     static_assert(entt::is_equality_comparable_v<std::unordered_map<int, int>>);
     static_assert(entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, char>>>);
+    static_assert(entt::is_equality_comparable_v<std::pair<const int, int>>);
+    static_assert(entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, char>>>);
     static_assert(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>);
     static_assert(entt::is_equality_comparable_v<nlohmann_json_like>);
 
     static_assert(!entt::is_equality_comparable_v<not_comparable>);
+    static_assert(!entt::is_equality_comparable_v<const not_comparable>);
     static_assert(!entt::is_equality_comparable_v<std::vector<not_comparable>>);
     static_assert(!entt::is_equality_comparable_v<std::vector<std::vector<not_comparable>>>);
     static_assert(!entt::is_equality_comparable_v<std::unordered_map<int, not_comparable>>);
     static_assert(!entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, not_comparable>>>);
+    static_assert(!entt::is_equality_comparable_v<std::pair<const int, not_comparable>>);
+    static_assert(!entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, not_comparable>>>);
     static_assert(!entt::is_equality_comparable_v<void>);
 }
 
@@ -136,10 +142,11 @@ TEST(TypeTraits, IsComplete) {
 
 TEST(TypeTraits, IsIterator) {
     static_assert(!entt::is_iterator_v<void>);
-    static_assert(!entt::is_iterator_v<void *>);
     static_assert(!entt::is_iterator_v<int>);
 
+    static_assert(!entt::is_iterator_v<void *>);
     static_assert(entt::is_iterator_v<int *>);
+
     static_assert(entt::is_iterator_v<std::vector<int>::iterator>);
     static_assert(entt::is_iterator_v<std::vector<int>::const_iterator>);
     static_assert(entt::is_iterator_v<std::vector<int>::reverse_iterator>);