Diff
Modified: trunk/Source/WTF/ChangeLog (264276 => 264277)
--- trunk/Source/WTF/ChangeLog 2020-07-12 08:16:13 UTC (rev 264276)
+++ trunk/Source/WTF/ChangeLog 2020-07-12 09:47:05 UTC (rev 264277)
@@ -1,3 +1,32 @@
+2020-07-11 Sam Weinig <[email protected]> and Yusuke Suzuki <[email protected]>
+
+ Relanding "Make hasher work with tuple-like classes"
+ https://bugs.webkit.org/show_bug.cgi?id=214224
+
+ Reviewed by Darin Adler.
+
+ The original patch broke the internal build. And reverting patch (r264274) broke the build too.
+ This patch relands r264270 with `constexpr` => `inline constexpr` template variable fix.
+
+ * wtf/Hasher.h:
+ (WTF::add):
+ (WTF::addTupleLikeHelper):
+ (WTF::TypeCheckHelper<decltype): Deleted.
+ (WTF::addTupleHelper): Deleted.
+ Add support for tuple-like classes. These are classes that:
+ - Have defined specializations of std::tuple_size<> and std::tuple_element<>.
+ - And have either a member function named get<> or there exists a function get<>()
+ that takes the class as argument that can be looked up via ADL.
+
+ To avoid abiguity when hashing std::array, the add() overload enabled for containers
+ with a begin member functions also now checks that the container is not tuple-like.
+ The std::pair overload is no longer needed as it will use the tuple-like one now.
+
+ * wtf/OptionSet.h:
+ * wtf/StdLibExtras.h:
+ Move is_type_complete_v<> from OptionSet.h to StdLibExtras.h and rename to
+ adhere to WebKit style as IsTypeComplete<>.
+
2020-07-11 Commit Queue <[email protected]>
Unreviewed, reverting r264270.
Modified: trunk/Source/WTF/wtf/Hasher.h (264276 => 264277)
--- trunk/Source/WTF/wtf/Hasher.h 2020-07-12 08:16:13 UTC (rev 264276)
+++ trunk/Source/WTF/wtf/Hasher.h 2020-07-12 09:47:05 UTC (rev 264277)
@@ -106,11 +106,10 @@
add(hasher, static_cast<std::underlying_type_t<Enumeration>>(value));
}
-template<typename> struct TypeCheckHelper { };
-template<typename, typename = void> struct HasBeginFunctionMember : std::false_type { };
-template<typename Container> struct HasBeginFunctionMember<Container, std::conditional_t<false, TypeCheckHelper<decltype(std::declval<Container>().begin())>, void>> : std::true_type { };
+template<typename, typename = void> inline constexpr bool HasBeginFunctionMember = false;
+template<typename T> inline constexpr bool HasBeginFunctionMember<T, std::void_t<decltype(std::declval<T>().begin())>> = true;
-template<typename Container> std::enable_if_t<HasBeginFunctionMember<Container>::value, void> add(Hasher& hasher, const Container& container)
+template<typename Container> std::enable_if_t<HasBeginFunctionMember<Container> && !IsTypeComplete<std::tuple_size<Container>>, void> add(Hasher& hasher, const Container& container)
{
for (const auto& value : container)
add(hasher, value);
@@ -126,20 +125,22 @@
addArgs(hasher, args...);
}
-template<typename Tuple, std::size_t ...i> void addTupleHelper(Hasher& hasher, const Tuple& values, std::index_sequence<i...>)
-{
- addArgs(hasher, std::get<i>(values)...);
-}
+template<typename, typename = void> inline constexpr bool HasGetFunctionMember = false;
+template<typename T> inline constexpr bool HasGetFunctionMember<T, std::void_t<decltype(std::declval<T>().template get<0>())>> = true;
-template<typename... Types> void add(Hasher& hasher, const std::tuple<Types...>& tuple)
+template<typename TupleLike, std::size_t ...I> void addTupleLikeHelper(Hasher& hasher, const TupleLike& tupleLike, std::index_sequence<I...>)
{
- addTupleHelper(hasher, tuple, std::make_index_sequence<std::tuple_size<std::tuple<Types...>>::value> { });
+ if constexpr (HasGetFunctionMember<TupleLike>)
+ addArgs(hasher, tupleLike.template get<I>()...);
+ else {
+ using std::get;
+ addArgs(hasher, get<I>(tupleLike)...);
+ }
}
-template<typename T1, typename T2> void add(Hasher& hasher, const std::pair<T1, T2>& pair)
+template<typename TupleLike> std::enable_if_t<IsTypeComplete<std::tuple_size<TupleLike>>, void> add(Hasher& hasher, const TupleLike& tuple)
{
- add(hasher, pair.first);
- add(hasher, pair.second);
+ addTupleLikeHelper(hasher, tuple, std::make_index_sequence<std::tuple_size<TupleLike>::value> { });
}
template<typename T> void add(Hasher& hasher, const Optional<T>& optional)
Modified: trunk/Source/WTF/wtf/OptionSet.h (264276 => 264277)
--- trunk/Source/WTF/wtf/OptionSet.h 2020-07-12 08:16:13 UTC (rev 264276)
+++ trunk/Source/WTF/wtf/OptionSet.h 2020-07-12 09:47:05 UTC (rev 264277)
@@ -32,18 +32,10 @@
#include <wtf/EnumTraits.h>
#include <wtf/MathExtras.h>
#include <wtf/Optional.h>
+#include <wtf/StdLibExtras.h>
namespace WTF {
-// Detecting in C++ whether a type is defined, part 3: SFINAE and incomplete types
-// <https://devblogs.microsoft.com/oldnewthing/20190710-00/?p=102678>
-template<typename, typename = void>
-constexpr bool is_type_complete_v = false;
-
-template<typename T>
-constexpr bool is_type_complete_v<T, std::void_t<decltype(sizeof(T))>> = true;
-
-
template<typename E> class OptionSet;
@@ -76,13 +68,13 @@
};
-template<typename E, std::enable_if_t<std::is_enum<E>::value && is_type_complete_v<EnumTraits<E>>>* = nullptr>
+template<typename E, std::enable_if_t<std::is_enum<E>::value && IsTypeComplete<EnumTraits<E>>>* = nullptr>
constexpr bool isValidOptionSetEnum(E e)
{
return OptionSetValueChecker<std::underlying_type_t<E>, typename EnumTraits<E>::values>::isValidOptionSetEnum(static_cast<std::underlying_type_t<E>>(e));
}
-template<typename E, std::enable_if_t<std::is_enum<E>::value && !is_type_complete_v<EnumTraits<E>>>* = nullptr>
+template<typename E, std::enable_if_t<std::is_enum<E>::value && !IsTypeComplete<EnumTraits<E>>>* = nullptr>
constexpr bool isValidOptionSetEnum(E e)
{
// FIXME: Remove once all OptionSet<> enums have EnumTraits<> defined.
@@ -90,7 +82,7 @@
}
-template<typename E, std::enable_if_t<std::is_enum<E>::value && is_type_complete_v<EnumTraits<E>>>* = nullptr>
+template<typename E, std::enable_if_t<std::is_enum<E>::value && IsTypeComplete<EnumTraits<E>>>* = nullptr>
constexpr typename OptionSet<E>::StorageType maskRawValue(typename OptionSet<E>::StorageType rawValue)
{
auto allValidBitsValue = OptionSetValueChecker<std::underlying_type_t<E>, typename EnumTraits<E>::values>::allValidBits();
@@ -97,7 +89,7 @@
return rawValue & allValidBitsValue;
}
-template<typename E, std::enable_if_t<std::is_enum<E>::value && !is_type_complete_v<EnumTraits<E>>>* = nullptr>
+template<typename E, std::enable_if_t<std::is_enum<E>::value && !IsTypeComplete<EnumTraits<E>>>* = nullptr>
constexpr typename OptionSet<E>::StorageType maskRawValue(typename OptionSet<E>::StorageType rawValue)
{
// FIXME: Remove once all OptionSet<> enums have EnumTraits<> defined.
Modified: trunk/Source/WTF/wtf/StdLibExtras.h (264276 => 264277)
--- trunk/Source/WTF/wtf/StdLibExtras.h 2020-07-12 08:16:13 UTC (rev 264276)
+++ trunk/Source/WTF/wtf/StdLibExtras.h 2020-07-12 09:47:05 UTC (rev 264277)
@@ -418,6 +418,11 @@
template <template <typename...> class Base, typename Derived>
struct IsBaseOfTemplate : public std::integral_constant<bool, Detail::IsBaseOfTemplateImpl<Base, Derived>::value> {};
+// Based on 'Detecting in C++ whether a type is defined, part 3: SFINAE and incomplete types'
+// <https://devblogs.microsoft.com/oldnewthing/20190710-00/?p=102678>
+template<typename, typename = void> inline constexpr bool IsTypeComplete = false;
+template<typename T> inline constexpr bool IsTypeComplete<T, std::void_t<decltype(sizeof(T))>> = true;
+
template <class T>
struct RemoveCVAndReference {
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type;
Modified: trunk/Tools/ChangeLog (264276 => 264277)
--- trunk/Tools/ChangeLog 2020-07-12 08:16:13 UTC (rev 264276)
+++ trunk/Tools/ChangeLog 2020-07-12 09:47:05 UTC (rev 264277)
@@ -1,3 +1,16 @@
+2020-07-11 Sam Weinig <[email protected]> and Yusuke Suzuki <[email protected]>
+
+ Relanding "Make hasher work with tuple-like classes"
+ https://bugs.webkit.org/show_bug.cgi?id=214224
+
+ Reviewed by Darin Adler.
+
+ * TestWebKitAPI/Tests/WTF/Hasher.cpp:
+ (TestWebKitAPI::get):
+ (TestWebKitAPI::HasherAddTupleLikeClass3::get const):
+ (TestWebKitAPI::TEST):
+ Update using new Color constructor for extended colors.
+
2020-07-12 Yusuke Suzuki <[email protected]>
Unreviewed, follow-up after r264210
Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/Hasher.cpp (264276 => 264277)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/Hasher.cpp 2020-07-12 08:16:13 UTC (rev 264276)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Hasher.cpp 2020-07-12 09:47:05 UTC (rev 264277)
@@ -208,8 +208,6 @@
EXPECT_EQ(1652352321U, computeHash(HasherAddCustom2 { }));
}
-#if 0 // FIXME: Add support for tuple-like classes.
-
struct HasherAddTupleLikeClass1 {
std::array<int, 4> array { { 1, 2, 3, 4 } };
template<size_t i> int get() const { return std::get<i>(array); }
@@ -219,34 +217,46 @@
std::array<int, 4> array { { 1, 2, 3, 4 } };
};
+template<size_t i> int get(const HasherAddTupleLikeClass2& object)
+{
+ return object.array[i];
}
+struct HasherAddTupleLikeClass3 {
+ int a = 1;
+ double b = 2;
+
+ template<size_t i> decltype(auto) get() const
+ {
+ if constexpr (!i)
+ return a;
+ if constexpr (i == 1)
+ return b;
+ }
+};
+
+}
+
namespace std {
-// FIXME: Documentation at cppreference.cpp says std::tuple_size is a struct, but it's a class in current macOS tools.
-// FIXME: It's inelegant to inject this into the std namespace. Is that really how a tuple-like class needs to be defined?
-// FIXME: This is so inconvenient that I am not sure it's something we want to do for lots of classes in WebKit.
-template<> class std::tuple_size<TestWebKitAPI::HasherAddTupleLikeClass1> : public std::integral_constant<size_t, std::tuple_size<decltype(TestWebKitAPI::HasherAddTupleLikeClass1::array)>::value> { };
+template<> class tuple_size<TestWebKitAPI::HasherAddTupleLikeClass1> : public integral_constant<size_t, 4> { };
+template<size_t I> class tuple_element<I, TestWebKitAPI::HasherAddTupleLikeClass1> { public: using type = int; };
-template<> class std::tuple_size<TestWebKitAPI::HasherAddTupleLikeClass2> : public std::integral_constant<size_t, std::tuple_size<decltype(TestWebKitAPI::HasherAddTupleLikeClass2::array)>::value> { };
+template<> class tuple_size<TestWebKitAPI::HasherAddTupleLikeClass2> : public integral_constant<size_t, 4> { };
+template<size_t I> class tuple_element<I, TestWebKitAPI::HasherAddTupleLikeClass2> { public: using type = int; };
+template<> class tuple_size<TestWebKitAPI::HasherAddTupleLikeClass3> : public integral_constant<size_t, 2> { };
+template<size_t I> class tuple_element<I, TestWebKitAPI::HasherAddTupleLikeClass3> { public: using type = decltype(declval<TestWebKitAPI::HasherAddTupleLikeClass3>().template get<I>()); };
+
}
namespace TestWebKitAPI {
-// FIXME: Is it OK for the get to be in the class's namespace and rely on argument-dependent lookup?
-// Or does this function template need to be moved into the std namespace like the tuple_size specialization?
-template<size_t i> int get(const HasherAddTupleLikeClass2& object)
-{
- return get<i>(object.array);
-}
-
TEST(WTF, Hasher_tupleLike)
{
EXPECT_EQ(1652352321U, computeHash(HasherAddTupleLikeClass1 { }));
EXPECT_EQ(1652352321U, computeHash(HasherAddTupleLikeClass2 { }));
+ EXPECT_EQ(3489601216U, computeHash(HasherAddTupleLikeClass3 { }));
}
-#endif
-
} // namespace TestWebKitAPI