============================== How-To make C++ types hashable ============================== Making a C++ type hashable for use as a key in e.g. ``std::unordered_map`` is simple; all it requires is that an overload of ``std::hash`` exists for that type. We provide a simple macro to help with this, which delegates to a member function of that type. This is mostly a matter of preference, but it allows to keep the definition of how to hash a type closer to the type itself. .. sourcecode:: cpp :linenos: :dedent: :caption: Make type hashable #include struct my_type { inline size_t hash() const { // Calculate hash value } }; LIBERATE_MAKE_HASHABLE(my_type); .. note:: The macro defines an overload in the ``std`` namespace, and therefore **must** be used in the global scope, outside of any of the namespaces you define. This could mean that you need to prefix the namespace to its argument, e.g. use ``LIBERATE_MAKE_HASHABLE(foo::my_type)``. The more complex part usually lies in how to calculate a hash value for a complex type in the first place. Hash functions should be relatively simple, and not produce too many collisions. The library provides one such function :cpp:func:`liberate::cpp::hash_combine`, which adds a value to a pre-existing seed value in a way that lets you repeat this several times. .. sourcecode:: cpp :linenos: :dedent: :caption: Make type hashable struct my_type { int foo; char bar; inline size_t hash() const { using namespace liberate::cpp; size_t result = 0; hash_combine(result, std::hash()(foo)); hash_combine(result, std::hash()(bar)); return result; } }; Because the above is somewhat tedious, the header also defines a function :cpp:func:`liberate::cpp::multi_hash` which does the above recursively for a variable number of arguments, and even deduces the type of the arguments. You can rewrite the above like this: .. sourcecode:: cpp :linenos: :dedent: :caption: Use multi_hash struct my_type { int foo; char bar; inline size_t hash() const { return liberate::cpp::multi_hash(foo, bar); } }; Finally, it sometimes is necessary to calculate a hash over a range, such as for a ``std::string``, or a list of values. That is also made relatively easy. .. sourcecode:: cpp :linenos: :dedent: :caption: Use range_hash std::string tmp{"Hello, world!"}; auto hash = liberate::cpp::range_hash(tmp.begin(), tmp.end()); You can, for example, now :cpp:func:`liberate::cpp::hash_combine` this with other values.