Skip to content

Commit

Permalink
Support TfHashAppend leveraging of std::hash for unspecialized types
Browse files Browse the repository at this point in the history
  • Loading branch information
nvmkuruc committed Nov 13, 2023
1 parent dcbc1ca commit 8cd68c1
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 43 deletions.
59 changes: 16 additions & 43 deletions pxr/base/tf/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ TfHashAppend(HashState &h, std::pair<T, U> const &p)

// Support std::vector. std::vector<bool> specialized below.
template <class HashState, class T>
inline std::enable_if_t<!std::is_same<std::remove_const_t<T>, bool>::value>
inline void
TfHashAppend(HashState &h, std::vector<T> const &vec)
{
static_assert(!std::is_same_v<std::remove_cv_t<T>, bool>,
"Unexpected usage of vector of 'bool'."
"Expected explicit overload.");
h.AppendContiguous(vec.data(), vec.size());
}

Expand Down Expand Up @@ -126,15 +129,6 @@ TfHashAppend(HashState& h, std::map<Key, Value, Compare> 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 <class HashState>
inline void
TfHashAppend(HashState& h, std::type_index const &type_index)
{
return h.Append(type_index.hash_code());
}

// Support for hashing std::string.
template <class HashState>
inline void
Expand All @@ -151,22 +145,6 @@ TfHashAppend(HashState &h, const T* ptr) {
return h.Append(reinterpret_cast<uintptr_t>(ptr));
}

// Support for hashing std::shared_ptr. When TfHash support for std::hash is
// enabled, this explicit specialization will no longer be necessary.
template <class HashState, class T>
inline void
TfHashAppend(HashState &h, const std::shared_ptr<T>& ptr) {
h.Append(std::hash<std::shared_ptr<T>>{}(ptr));
}

// Support for hashing std::unique_ptr. When TfHash support for std::hash is
// enabled, this explicit specialization will no longer be necessary.
template <class HashState, class T>
inline void
TfHashAppend(HashState &h, const std::unique_ptr<T>& ptr) {
h.Append(std::hash<std::unique_ptr<T>>{}(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
Expand Down Expand Up @@ -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<T>,
// 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 <class HashState, class T>
inline auto Tf_HashImpl(HashState &h, T &&obj, ...)
inline auto Tf_HashImpl(HashState &h, T &&obj, long)
-> decltype(std::hash<typename std::decay<T>::type>()(
std::forward<T>(obj)), void())
{
TfHashAppend(
h, std::hash<typename std::decay<T>::type>()(std::forward<T>(obj)));
}
*/

// hash_value, attempted second.
// hash_value, attempted last.
template <class HashState, class T>
inline auto Tf_HashImpl(HashState &h, T &&obj, long)
inline auto Tf_HashImpl(HashState &h, T &&obj, ...)
-> decltype(hash_value(std::forward<T>(obj)), void())
{
TfHashAppend(h, hash_value(std::forward<T>(obj)));
Expand Down Expand Up @@ -437,9 +410,8 @@ class Tf_HashState : public Tf_HashStateAPI<Tf_HashState>
/// 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<T> next, but std::hash<T> is not
/// SFINAE-friendly until c++17, and this code needs to support c++14.
/// not viable, TfHash tries to call std::hash<T>{}(). 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.
Expand Down Expand Up @@ -487,6 +459,7 @@ class Tf_HashState : public Tf_HashStateAPI<Tf_HashState>
/// \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
Expand Down
13 changes: 13 additions & 0 deletions pxr/base/tf/testenv/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
// commented for testing purposes, for now (6/2020).
//#include <boost/functional/hash.hpp>

#include <optional>
#include <set>
#include <string>
#include <variant>
#include <vector>

PXR_NAMESPACE_USING_DIRECTIVE
Expand Down Expand Up @@ -324,6 +326,17 @@ Test_TfHash()
// Validate support for std::unique_ptr
printf("hash(unique_ptr): %zu\n", h(std::make_unique<int>(7)));

// Validate support for std::optional
printf("hash(optional): %zu\n", h(std::make_optional<std::string>("xyz")));
TF_AXIOM(h(std::optional<std::string>("xyz")) ==
h(std::optional<std::string>("xyz")));

// Validate support for std::variant
printf("hash(variant): %zu\n",
h(std::variant<std::string, int, double>("abc")));
TF_AXIOM(h(std::variant<std::string, int, double>("abc")) ==
h(std::variant<std::string, int, double>("abc")));

TfHasher tfh;
//BoostHasher bh;

Expand Down

0 comments on commit 8cd68c1

Please sign in to comment.