From 21dbdb9b47ae01957f2e8bb7fc460e3624acf35a Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 2 Feb 2022 13:14:51 -0800 Subject: [PATCH] Add Portability section to AsciiDoc --- doc/hash/portability.adoc | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/doc/hash/portability.adoc b/doc/hash/portability.adoc index 0f86e87..a552b8e 100644 --- a/doc/hash/portability.adoc +++ b/doc/hash/portability.adoc @@ -2,3 +2,81 @@ = Portability :idprefix: portability_ + +xref:#ref_hash[boost::hash] is written to be as portable as possible, but unfortunately, several older compilers don't support argument dependent lookup (ADL) - the mechanism used for customisation. On those compilers custom overloads for `hash_value` needs to be declared in the `boost` namespace. + +On a strictly standards compliant compiler, an overload defined in the `boost` namespace won't be found when xref:#ref_hash[boost::hash] is instantiated, so for these compilers the overload should only be declared in the same namespace as the class. + +Let's say we have a simple custom type: + +[list,subs="+quotes,+macros"] +---- +namespace foo +{ + template + class custom_type + { + T value; + public: + custom_type(T x) : value(x) {} + + friend std::size_t hash_value(custom_type x) + { + xref:#ref_hash[boost::hash] hasher; + return hasher(x.value); + } + }; +} +---- + +On a compliant compiler, when `hash_value` is called for this type, it will look at the namespace inside the type and find `hash_value` but on a compiler which doesn't support ADL `hash_value` won't be found. To make things worse, some compilers which do support ADL won't find a friend class defined inside the class. + +So first move the member function out of the class: + +[listing,subs="+quotes,+macros"] +---- +namespace foo +{ + template + class custom_type + { + T value; + public: + custom_type(T x) : value(x) {} + + std::size_t hash(custom_type x) + { + xref:#ref_hash[boost::hash] hasher; + return hasher(value); + } + }; + + template + inline std::size_t hash_value(custom_type x) + { + return x.hash(); + } +} +---- + +Unfortunately, I couldn't declare `hash_value` as a friend, as some compilers don't support template friends, so instead I declared a member function to calculate the hash, and called it from `hash_value`. + +For compilers which don't support ADL, `hash_value` needs to be defined in the `boost` namespace: + +[listing] +---- +#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +namespace boost +#else +namespace foo +#endif +{ + template + std::size_t hash_value(foo::custom_type x) + { + return x.hash(); + } +} +---- + +Full code for this example is at link:../../examples/portable.cpp[/libs/container_hash/examples/portable.cpp^].