
[/ Copyright 2005-2006 Daniel James.
 / Distributed under the Boost Software License, Version 1.0. (See accompanying
 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ]

[section:portability Portability]

[def __boost_hash__ [classref boost::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`
need 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 __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:

    namespace foo
    {
        template <class T>
        class custom_type
        {
            T value;
        public:
            custom_type(T x) : value(x) {}

            friend std::size_t hash_value(custom_type x)
            {
                __boost_hash__<int> 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:

    namespace foo
    {
        template <class T>
        class custom_type
        {
            T value;
        public:
            custom_type(T x) : value(x) {}

            std::size_t hash(custom_type x)
            {
                __boost_hash__<T> hasher;
                return hasher(value);
            }
        };

        template <class T>
        inline std::size_t hash_value(custom_type<T> 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:

    #ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
    namespace boost
    #else
    namespace foo
    #endif
    {
        template <class T>
        std::size_t hash_value(foo::custom_type<T> x)
        {
            return x.hash();
        }
    }

Full code for this example is at
[@../../libs/functional/hash/examples/portable.cpp /libs/functional/hash/examples/portable.cpp].

[h2 Other Issues]

On Visual C++ versions 6.5 and 7.0, `hash_value` isn't overloaded for built in
arrays. __boost_hash__, [funcref boost::hash_combine] and [funcref boost::hash_range] all use a workaround to
support built in arrays so this shouldn't be a problem in most cases.

On Visual C++ versions 6.5 and 7.0, function pointers aren't currently supported.

When using GCC on Solaris, `boost::hash_value(long double)` treats
`long double`s as `double`s - so the hash function doesn't take into account the
full range of values.

[endsect]
