Types Don't Know # - Howard Hinnant - CppCon 2014
Types Don't Know # - Howard Hinnant - CppCon 2014
Howard Hinnant
Ripple Labs
CppCon 2014
Overview
Overview
std::size_t!
fnv1a (void const* key, std::size_t len)!
{!
std::size_t h = 14695981039346656037u;!
unsigned char const* p = static_cast<unsigned char const*>(key);!
unsigned char const* const e = p + len;!
for (; p < e; ++p)!
h = (h ^ *p) * 1099511628211u;!
return h;!
}
Example Class
Hash with FNV-1A
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
// ...!
!
!
!
!
!
!
!
!
!
};!
Example Class
Hash with FNV-1A
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
// ...!
! std::size_t
! hash_code() const
! {
! std::size_t k1 = fnv1a(firstName_.data(), firstName_.size());
! std::size_t k2 = fnv1a(lastName_.data(), lastName_.size());
! std::size_t k3 = fnv1a(&age_, sizeof(age_));
!
!
!
};!
Example Class
Hash with FNV-1A
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
Ok, but our algorithm is still
public:! “polluted” by the combine step…
// ...!
! std::size_t
! hash_code() const
! {
! std::size_t k1 = fnv1a(firstName_.data(), firstName_.size());
! std::size_t k2 = fnv1a(lastName_.data(), lastName_.size());
! std::size_t k3 = fnv1a(&age_, sizeof(age_));
! return hash_combine(k1, k2, k3); // what algorithm is this?!
! }
!
};!
Anatomy Of A Hash Function
Anatomy Of A Hash Function
std::size_t!
fnv1a(void const* key, std::size_t len) noexcept!
{!
! std::size_t h = 14695981039346656037u;
! unsigned char const* p = static_cast<unsigned char const*>(key);!
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
! return h;
}
Example Hash Algorithm
FNV-1A
{
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
! return h;
Consume bytes into
internal state
}
Example Hash Algorithm
FNV-1A
{
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
! return h;
Consume bytes into
internal state
}
{
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
! return h;
Consume bytes into
internal state
}
std::size_t!
fnv1a(void const* key, std::size_t len) noexcept!
{!
! std::size_t h = 14695981039346656037u;
! unsigned char const* p = static_cast<unsigned char const*>(key);!
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
! return h;
}
Example Hash Algorithm
FNV-1A
class fnv1a!
{!
! std::size_t h = 14695981039346656037u;
public:!
using result_type = std::size_t;!
!
void!
operator()(void const* key, std::size_t len) noexcept!
{!
! unsigned char const* p = static_cast<unsigned char const*>(key);!
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
}!
!
explicit!
operator result_type() noexcept!
{!
! return h;
}!
};
Example Hash Algorithm
FNV-1A
class fnv1a! Initialize internal state
{!
! std::size_t h = 14695981039346656037u;
public:!
using result_type = std::size_t;! Consume bytes into
!
void! internal state
operator()(void const* key, std::size_t len) noexcept!
{!
! unsigned char const* p = static_cast<unsigned char const*>(key);!
! unsigned char const* const e = p + len;!
! for (; p < e; ++p)!
! h = (h ^ *p) * 1099511628211u;
}!
!
explicit!
operator result_type() noexcept!
{!
! return h;
}!
}; Finalize internal state to size_t
Example Class
Hash with FNV-1A
Customer
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
// ...!
!
!
!
!
!
!
!
!
!
};!
Example Class
Hash with FNV-1A
Customer
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
// ...!
! std::size_t
! hash_code() const
! {
! fnv1a h;
!
!
!
!
!
};!
Example Class
Hash with FNV-1A
Customer
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
// ...!
! std::size_t
! hash_code() const
! {
! fnv1a h;
! h(firstName_.data(), firstName_.size());
! h(lastName_.data(), lastName_.size());
! h(&age_, sizeof(age_));
!
!
};!
Example Class
Hash with FNV-1A
Customer
class Customer!
{!
std::string firstName_;!
Now we are using a “pure”
std::string lastName_;!
int age_;!
FNV-1A algorithm for the
public:! entire data structure!
// ...!
! std::size_t
! hash_code() const
! {
! fnv1a h;
! h(firstName_.data(), firstName_.size());
! h(lastName_.data(), lastName_.size());
! h(&age_, sizeof(age_));
! return static_cast<std::size_t>(h); // No more hash_combine!
! }
};!
Example Class
Hash with FNV-1A
Customer
class Customer!
{!
std::string firstName_;!
Now we are using a “pure”
std::string lastName_;!
int age_;!
FNV-1A algorithm for the
public:! entire data structure!
// ...!
! std::size_t
! hash_code() const
! {
! fnv1a h;
! h(firstName_.data(), firstName_.size());
! h(lastName_.data(), lastName_.size());
! h(&age_, sizeof(age_));
! return static_cast<std::size_t>(h); // No more hash_combine!
! }
};!
class Sale!
{!
Customer customer_;!
Product product_;!
Date date_;!
public:!
!
!
!
!
!
!
!
!
!
};
Combining Types
class Sale!
{!
Customer customer_;!
Product product_;!
Date date_;!
public:!
! std::size_t
! hash_code() const
! {
! std::size_t h1 = customer_.hash_code();
! std::size_t h2 = product_.hash_code();
! std::size_t h3 = date_.hash_code();
!
!
!
};
Combining Types
class Sale!
{!
Customer customer_;!
Product product_;!
Date date_;!
public:!
! std::size_t
! hash_code() const
! {
! std::size_t h1 = customer_.hash_code();
! std::size_t h2 = product_.hash_code();
! std::size_t h3 = date_.hash_code();
! // hash_combine is back!!!
! return hash_combine(h1, h2, h3);
! }
};
Combining Types
How do we use FNV-1A
class Sale!
{!
for the entire Sale class?
Customer customer_;!
Product product_;!
Date date_;!
public:!
! std::size_t
! hash_code() const
! {
! std::size_t h1 = customer_.hash_code();
! std::size_t h2 = product_.hash_code();
! std::size_t h3 = date_.hash_code();
! // hash_combine is back!!!
! return hash_combine(h1, h2, h3);
! }
};
hash_append
Looking back at the Customer
Customer
class Customer! class, let’s solve this problem!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
// ...!
! std::size_t!
! hash_code() const!
! {!
! fnv1a h;!
!! h(c.firstName_.data(), c.firstName_.size());!
!! h(c.lastName_.data(), c.lastName_.size());!
!! h(&c.age_, sizeof(c.age_));
! return static_cast<std::size_t>(h);!
! }!
};!
hash_append
Let some other piece of code
Customer
class Customer! construct and finalize fnv1a.
{!
std::string firstName_;!
std::string lastName_;! Customer only appends to
int age_;!
public:! the state of fnv1a.
// ...!
! friend!
! void!
! hash_append(fnv1a& h, const Customer& c)!
! {!
!! h(c.firstName_.data(), c.firstName_.size());!
!! h(c.lastName_.data(), c.lastName_.size());!
!! h(&c.age_, sizeof(c.age_));
! }!
!
};!
hash_append
class Sale!
{!
Customer customer_;!
Product product_;!
Date date_;!
public:!
!
!
!
!
!
!
!
!
};
hash_append
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
!
! friend!
! void!
! hash_append(fnv1a& h, const Customer& c)!
! {!
! h(c.firstName_.data(), c.firstName_.size());!
! h(c.lastName_.data(), c.lastName_.size());!
! h(&c.age_, sizeof(c.age_));!
! }!
};
hash_append
Primitive and std-defined types can
be given hash_append overloads.
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
!
! friend!
! void!
! hash_append(fnv1a& h, const Customer& c)!
! {!
! hash_append(h, c.firstName_);!
! hash_append(h, c.lastName_);! Simplify!
! hash_append(h, c.age_);!
! }!
};
hash_append
If all Hash Algorithms follow the same interface
as given for fnv1a, then hash_append can be
templated on the algorithm.
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
! template <class HashAlgorithm>!
! friend!
! void!
! hash_append(HashAlgorithm& h, const Customer& c)!
! {!
! hash_append(h, c.firstName_);!
! hash_append(h, c.lastName_);!
! hash_append(h, c.age_);!
! }!
};
hash_append
If all Hash Algorithms follow the same interface
as given for fnv1a, then hash_append can be
templated on the algorithm.
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
! template <class HashAlgorithm>!
! friend!
! void!
! hash_append(HashAlgorithm& h, const Customer& c)!
! {!
! hash_append(h, c.firstName_);!
! hash_append(h , c.lastName_);!
! hash_append(h, c.age_);!
! }!
};
hash_append
One can easily create a variadic
version of hash_append.
class Customer!
{!
std::string firstName_;!
std::string lastName_;!
int age_;!
public:!
! template <class HashAlgorithm>!
! friend!
! void!
! hash_append(HashAlgorithm& h, const Customer& c)!
! {!
! hash_append(h, c.firstName_ , c.lastName_ , c.age_ );!
! }
!
!
};
hash_append
For primitive types that are contiguously hashable
one can just send their bytes to the hash algorithm
in hash_append.
template <class HashAlgorithm>!
void!
hash_append(HashAlgorithm& h, int i)!
{!
h(&i, sizeof(i));!
}
HashAlgorithm h;
To use hash_append
Append to the Hash Algorithm
HashAlgorithm h;
hash_append(h, t);
To use hash_append
Finalize the Hash Algorithm
HashAlgorithm h;
hash_append(h, t);
return static_cast<result_type>(h);
To use hash_append
template <class HashAlgorithm = fnv1a>!
struct uhash!
{!
using result_type = typename HashAlgorithm::result_type;!
!
template <class T>!
result_type!
operator()(T const& t) const noexcept!
{!
! HashAlgorithm h;
! hash_append(h, t);
! return static_cast<result_type>(h);
}!
};!
HashAlgorithm h{get_seed()};
To seed a HashAlgorithm
Append to the Hash Algorithm
HashAlgorithm h{get_seed()};
hash_append(h, t);
To seed a HashAlgorithm
Finalize the Hash Algorithm
HashAlgorithm h{get_seed()};
hash_append(h, t);
return static_cast<result_type>(h);
To seed a HashAlgorithm
template <class HashAlgorithm = SipHash>!
struct seeded_hash!
{!
using result_type = typename HashAlgorithm::result_type;!
!
template <class T>!
result_type!
operator()(T const& t) const noexcept!
{!
! HashAlgorithm h{get_seed()};
! hash_append(h, t);
! return static_cast<result_type>(h);
}!
private:!
result_type get_seed();!
};
https://github.com/HowardHinnant/hash_append
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3980.html