Browse Source

type_traits: improve is_equality_comparable[_v]

Michele Caini 2 years ago
parent
commit
273035ff49
2 changed files with 33 additions and 22 deletions
  1. 31 22
      src/entt/core/type_traits.hpp
  2. 2 0
      test/entt/core/type_traits.cpp

+ 31 - 22
src/entt/core/type_traits.hpp

@@ -3,6 +3,7 @@
 
 
 #include <cstddef>
 #include <cstddef>
 #include <iterator>
 #include <iterator>
+#include <optional>
 #include <tuple>
 #include <tuple>
 #include <type_traits>
 #include <type_traits>
 #include <utility>
 #include <utility>
@@ -747,8 +748,12 @@ inline constexpr bool is_transparent_v = is_transparent<Type>::value;
  * equality comparable, false otherwise.
  * equality comparable, false otherwise.
  * @tparam Type The type to test.
  * @tparam Type The type to test.
  */
  */
-template<typename Type, typename = void>
-struct is_equality_comparable: std::false_type {};
+template<typename Type>
+struct is_equality_comparable;
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
 
 
 /**
 /**
  * @cond TURN_OFF_DOXYGEN
  * @cond TURN_OFF_DOXYGEN
@@ -763,52 +768,56 @@ struct has_tuple_size_value: std::false_type {};
 template<typename Type>
 template<typename Type>
 struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
 struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
 
 
+template<typename, typename = void>
+struct has_value_type: std::false_type {};
+
+template<typename Type>
+struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {};
+
 template<typename Type, std::size_t... Index>
 template<typename Type, std::size_t... Index>
 [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
 [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
     return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
     return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
 }
 }
 
 
 template<typename>
 template<typename>
-[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
-    return true;
-}
-
-template<typename 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 maybe_equality_comparable<Type>(choice<0>);
-    } else {
-        return is_equality_comparable<typename Type::value_type>::value;
-    }
+[[nodiscard]] constexpr bool maybe_equality_comparable(char) {
+    return false;
 }
 }
 
 
 template<typename Type>
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
     if constexpr(has_tuple_size_value<Type>::value) {
     if constexpr(has_tuple_size_value<Type>::value) {
         return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
         return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+    } else if constexpr(has_value_type<Type>::value) {
+        if constexpr(is_iterator_v<Type> || std::is_same_v<typename Type::value_type, Type>) {
+            return true;
+        } else {
+            return is_equality_comparable<typename Type::value_type>::value;
+        }
     } else {
     } else {
-        return maybe_equality_comparable<Type>(choice<1>);
+        return true;
     }
     }
 }
 }
 
 
 } // namespace internal
 } // namespace internal
 
 
+template<typename Type>
+struct is_equality_comparable: std::bool_constant<internal::maybe_equality_comparable<Type>(0)> {};
+
 /**
 /**
  * Internal details not to be documented.
  * Internal details not to be documented.
  * @endcond
  * @endcond
  */
  */
 
 
-/*! @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>)> {};
-
 /*! @copydoc is_equality_comparable */
 /*! @copydoc is_equality_comparable */
 template<typename Type, auto N>
 template<typename Type, auto N>
 struct is_equality_comparable<Type[N]>: std::false_type {};
 struct is_equality_comparable<Type[N]>: std::false_type {};
 
 
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+// std::optional has a non sfinae-friendly comparison operator :(
+struct is_equality_comparable<std::optional<Type>>: is_equality_comparable<typename std::optional<Type>::value_type> {};
+
 /**
 /**
  * @brief Helper variable template.
  * @brief Helper variable template.
  * @tparam Type The type to test.
  * @tparam Type The type to test.

+ 2 - 0
test/entt/core/type_traits.cpp

@@ -212,6 +212,7 @@ TEST(IsEqualityComparable, Functionalities) {
     ASSERT_TRUE((entt::is_equality_comparable_v<std::pair<const int, int>>));
     ASSERT_TRUE((entt::is_equality_comparable_v<std::pair<const int, int>>));
     ASSERT_TRUE((entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, char>>>));
     ASSERT_TRUE((entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, char>>>));
     ASSERT_TRUE(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>);
     ASSERT_TRUE(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>);
+    ASSERT_TRUE((entt::is_equality_comparable_v<std::optional<int>>));
     ASSERT_TRUE(entt::is_equality_comparable_v<nlohmann_json_like>);
     ASSERT_TRUE(entt::is_equality_comparable_v<nlohmann_json_like>);
 
 
     ASSERT_FALSE(entt::is_equality_comparable_v<int[3u]>);
     ASSERT_FALSE(entt::is_equality_comparable_v<int[3u]>);
@@ -223,6 +224,7 @@ TEST(IsEqualityComparable, Functionalities) {
     ASSERT_FALSE((entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, not_comparable>>>));
     ASSERT_FALSE((entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, not_comparable>>>));
     ASSERT_FALSE((entt::is_equality_comparable_v<std::pair<const int, not_comparable>>));
     ASSERT_FALSE((entt::is_equality_comparable_v<std::pair<const int, not_comparable>>));
     ASSERT_FALSE((entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, not_comparable>>>));
     ASSERT_FALSE((entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, not_comparable>>>));
+    ASSERT_FALSE((entt::is_equality_comparable_v<std::optional<not_comparable>>));
     ASSERT_FALSE(entt::is_equality_comparable_v<void>);
     ASSERT_FALSE(entt::is_equality_comparable_v<void>);
 }
 }