Add hasher specializations for enums, pairs and tuples
authorAlexander Shaposhnikov <alexshap@fb.com>
Wed, 29 Jul 2015 03:35:46 +0000 (20:35 -0700)
committerfacebook-github-bot-1 <folly-bot@fb.com>
Wed, 29 Jul 2015 04:22:15 +0000 (21:22 -0700)
Summary: This diff adds partial specializations of folly::hasher for enum types, std::pair and std::tuple.
We also restrict the specialization for folly::Range to POD value types.

Reviewed By: @ot, @ddrcoder

Differential Revision: D2285554

folly/Hash.h
folly/Range.h
folly/test/HashTest.cpp

index 566bad05afed814ec77e4698711d250ff518f643..f40f4ec27d620512239a7d85fa6f7cac83600863 100644 (file)
@@ -23,6 +23,7 @@
 #include <utility>
 #include <tuple>
 
+#include <folly/ApplyTuple.h>
 #include <folly/SpookyHashV1.h>
 #include <folly/SpookyHashV2.h>
 
@@ -335,7 +336,7 @@ inline uint32_t hsieh_hash32_str(const std::string& str) {
 
 } // namespace hash
 
-template<class Key>
+template<class Key, class Enable = void>
 struct hasher;
 
 struct Hash {
@@ -343,6 +344,11 @@ struct Hash {
   size_t operator()(const T& v) const {
     return hasher<T>()(v);
   }
+
+  template <class T, class... Ts>
+  size_t operator()(const T& t, const Ts&... ts) const {
+    return hash::hash_128_to_64((*this)(t), (*this)(ts...));
+  }
 };
 
 template<> struct hasher<int32_t> {
@@ -369,6 +375,27 @@ template<> struct hasher<uint64_t> {
   }
 };
 
+template <class T>
+struct hasher<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
+  size_t operator()(T key) const {
+    return Hash()(static_cast<typename std::underlying_type<T>::type>(key));
+  }
+};
+
+template <class T1, class T2>
+struct hasher<std::pair<T1, T2>> {
+  size_t operator()(const std::pair<T1, T2>& key) const {
+    return Hash()(key.first, key.second);
+  }
+};
+
+template <typename... Ts>
+struct hasher<std::tuple<Ts...>> {
+  size_t operator() (const std::tuple<Ts...>& key) const {
+    return applyTuple(Hash(), key);
+  }
+};
+
 // recursion
 template <size_t index, typename... Ts>
 struct TupleHasher {
index c34d5cfe601efd766d7c43b3181422a02eae4686..1088f4662c5506fcfcbe9347432c32c18c85c66d 100644 (file)
@@ -1133,10 +1133,12 @@ inline size_t qfind_first_of(const Range<const unsigned char*>& haystack,
                                      StringPiece(needles));
 }
 
-template<class Key>
+template<class Key, class Enable>
 struct hasher;
 
-template <class T> struct hasher<folly::Range<T*>> {
+template <class T>
+struct hasher<folly::Range<T*>,
+              typename std::enable_if<std::is_pod<T>::value, void>::type> {
   size_t operator()(folly::Range<T*> r) const {
     return hash::SpookyHashV2::Hash64(r.begin(), r.size() * sizeof(T), 0);
   }
index 435352b7ea92e6cc04197fd04983f2696cfd0dc4..bbae4f6f29fcb07e9820141e2955ded2484b1f94 100644 (file)
@@ -238,6 +238,46 @@ TEST(Hash, std_tuple) {
   EXPECT_EQ("bar", m[t]);
 }
 
+TEST(Hash, enum_type) {
+  const auto hash = folly::Hash();
+
+  enum class Enum32 : int32_t { Foo, Bar };
+  EXPECT_EQ(hash(static_cast<int32_t>(Enum32::Foo)), hash(Enum32::Foo));
+  EXPECT_EQ(hash(static_cast<int32_t>(Enum32::Bar)), hash(Enum32::Bar));
+  EXPECT_NE(hash(Enum32::Foo), hash(Enum32::Bar));
+
+  std::unordered_map<Enum32, std::string, folly::Hash> m32;
+  m32[Enum32::Foo] = "foo";
+  EXPECT_EQ("foo", m32[Enum32::Foo]);
+
+  enum class Enum64 : int64_t { Foo, Bar };
+  EXPECT_EQ(hash(static_cast<int64_t>(Enum64::Foo)), hash(Enum64::Foo));
+  EXPECT_EQ(hash(static_cast<int64_t>(Enum64::Bar)), hash(Enum64::Bar));
+  EXPECT_NE(hash(Enum64::Foo), hash(Enum64::Bar));
+
+  std::unordered_map<Enum64, std::string, folly::Hash> m64;
+  m64[Enum64::Foo] = "foo";
+  EXPECT_EQ("foo", m64[Enum64::Foo]);
+}
+
+TEST(Hash, pair_folly_hash) {
+  typedef std::pair<int64_t, int32_t> pair2;
+  pair2 p(42, 1);
+
+  std::unordered_map<pair2, std::string, folly::Hash> m;
+  m[p] = "bar";
+  EXPECT_EQ("bar", m[p]);
+}
+
+TEST(Hash, tuple_folly_hash) {
+  typedef std::tuple<int64_t, int32_t, int32_t> tuple3;
+  tuple3 t(42, 1, 1);
+
+  std::unordered_map<tuple3, std::string, folly::Hash> m;
+  m[t] = "bar";
+  EXPECT_EQ("bar", m[t]);
+}
+
 namespace {
 template <class T>
 size_t hash_vector(const std::vector<T>& v) {