From 5c837f6111a2af2a0ac67c4471321063895868e6 Mon Sep 17 00:00:00 2001 From: Matt Kuruc Date: Fri, 3 Nov 2023 15:08:20 -0700 Subject: [PATCH] Support `TfHashAppend` leveraging of `std::hash` for unspecialized types --- pxr/base/tf/hash.h | 59 ++++++++++-------------------------- pxr/base/tf/testenv/hash.cpp | 12 ++++++++ 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/pxr/base/tf/hash.h b/pxr/base/tf/hash.h index a0b655fd43..6734f44ddc 100644 --- a/pxr/base/tf/hash.h +++ b/pxr/base/tf/hash.h @@ -92,9 +92,12 @@ TfHashAppend(HashState &h, std::pair const &p) // Support std::vector. std::vector specialized below. template -inline std::enable_if_t, bool>::value> +inline void TfHashAppend(HashState &h, std::vector const &vec) { + static_assert(!std::is_same_v, bool>, + "Unexpected usage of vector of 'bool'." + "Expected explicit overload."); h.AppendContiguous(vec.data(), vec.size()); } @@ -126,15 +129,6 @@ TfHashAppend(HashState& h, std::map const &elements) h.AppendRange(std::begin(elements), std::end(elements)); } -// Support std::type_index. When TfHash support for std::hash is enabled, -// this explicit specialization will no longer be necessary. -template -inline void -TfHashAppend(HashState& h, std::type_index const &type_index) -{ - return h.Append(type_index.hash_code()); -} - // Support for hashing std::string. template inline void @@ -151,22 +145,6 @@ TfHashAppend(HashState &h, const T* ptr) { return h.Append(reinterpret_cast(ptr)); } -// Support for hashing std::shared_ptr. When TfHash support for std::hash is -// enabled, this explicit specialization will no longer be necessary. -template -inline void -TfHashAppend(HashState &h, const std::shared_ptr& ptr) { - h.Append(std::hash>{}(ptr)); -} - -// Support for hashing std::unique_ptr. When TfHash support for std::hash is -// enabled, this explicit specialization will no longer be necessary. -template -inline void -TfHashAppend(HashState &h, const std::unique_ptr& ptr) { - h.Append(std::hash>{}(ptr)); -} - // We refuse to hash [const] char *. You're almost certainly trying to hash the // pointed-to string and this will not do that (it will hash the pointer // itself). To hash a c-style null terminated string, you can use @@ -206,29 +184,24 @@ inline void TfHashAppend(HashState &h, TfCStrHashWrapper hcstr) } // Implementation detail: dispatch based on hash capability: Try TfHashAppend -// first, otherwise try hash_value. We'd like to otherwise try std::hash, -// but std::hash<> is not SFINAE-friendly until c++17 and this code needs to -// support c++14 currently. We rely on a combination of expression SFINAE and -// establishing preferred order by passing a 0 constant and having the overloads -// take int (highest priority), long (next priority) and '...' (lowest -// priority). - -// std::hash version, attempted last. Consider adding when we move to -// C++17 or newer. -/* +// first, otherwise try std::hash, followed by hash_value. We rely on a +// combination of expression SFINAE and establishing preferred order by passing +// a 0 constant and having the overloads take int (highest priority), long +// (next priority) and '...' (lowest priority). + +// std::hash version, attempted second. template -inline auto Tf_HashImpl(HashState &h, T &&obj, ...) +inline auto Tf_HashImpl(HashState &h, T &&obj, long) -> decltype(std::hash::type>()( std::forward(obj)), void()) { TfHashAppend( h, std::hash::type>()(std::forward(obj))); } -*/ -// hash_value, attempted second. +// hash_value, attempted last. template -inline auto Tf_HashImpl(HashState &h, T &&obj, long) +inline auto Tf_HashImpl(HashState &h, T &&obj, ...) -> decltype(hash_value(std::forward(obj)), void()) { TfHashAppend(h, hash_value(std::forward(obj))); @@ -437,9 +410,8 @@ class Tf_HashState : public Tf_HashStateAPI /// some STL types and types in Tf. TfHash uses three methods to attempt to /// hash a passed object. First, TfHash tries to call TfHashAppend() on its /// argument. This is the primary customization point for TfHash. If that is -/// not viable, TfHash makes an unqualified call to hash_value(). We would like -/// TfHash to try to use std::hash next, but std::hash is not -/// SFINAE-friendly until c++17, and this code needs to support c++14. +/// not viable, TfHash tries to call std::hash{}(). Lastly, TfHash makes an +/// unqualified call to hash_value. /// /// The best way to add TfHash support for user-defined types is to provide a /// function overload like the following. @@ -487,6 +459,7 @@ class Tf_HashState : public Tf_HashStateAPI /// \li TfEnum /// \li const void* /// \li types that provide overloads for TfHashAppend +/// \li types that provide overloads for std::hash /// \li types that provide overloads for hash_value /// /// The \c TfHash class can be used to instantiate a \c TfHashMap with \c string diff --git a/pxr/base/tf/testenv/hash.cpp b/pxr/base/tf/testenv/hash.cpp index ebb084ede9..93a5338bc5 100644 --- a/pxr/base/tf/testenv/hash.cpp +++ b/pxr/base/tf/testenv/hash.cpp @@ -31,6 +31,7 @@ #include #include +#include #include PXR_NAMESPACE_USING_DIRECTIVE @@ -303,6 +304,17 @@ Test_TfHash() // Validate support for std::unique_ptr printf("hash(unique_ptr): %zu\n", h(std::make_unique(7))); + // Validate support for std::optional + printf("hash(optional): %zu\n", h(std::make_optional("xyz"))); + TF_AXIOM(h(std::optional("xyz")) == + h(std::optional("xyz"))); + + // Validate support for std::variant + printf("hash(variant): %zu\n", + h(std::variant("abc"))); + TF_AXIOM(h(std::variant("abc")) == + h(std::variant("abc"))); + TfHasher tfh; _TestStatsOne(tfh, "TfHash");