c++templatesboostboost-unitsboost-functional

boost::hash for tuple containing boost units


I want to use a tbb::concurrent_hash_map with a key of std::tuple<A...> where A... will include boost unit types. The HashCompare struct I'm using at the moment looks like this:

template<typename K>
struct HashCompare {
    static size_t hash( const K& key )
    {
        boost::hash<K> hasher;
        return hasher(key);
    }
    static bool equal( const K& key1, const K& key2 ) {return key1 == key2;}
};

For all non boost unit types I tried this worked nicely but not so with boost units. I know that it is possible to extend the boost::hash function with custom types but I seem unable to do that. As i have quite a lot of units I wanted to do this with a template of the following form:

std::size_t hash_value(T const& t){
    boost::hash<double> hasher;
    return hasher(t.value());
}

Putting this function in the boost namespace or in the namespace where the units are defined didn't work.

How is it possible to extend the boost hashing function to custom types or write a template for the HashCompare::hash function that takes only boost units?


Solution

  • Indeed Boost Units does not have hash support. You can add it:

    template <typename... T>
    size_t hash_value(boost::units::quantity<T...> const& q) {
        using boost::hash_value; // enable ADL
        return hash_value(q.value());
    }
    

    Smallest demo:

    Live On Coliru

    #include <boost/functional/hash.hpp>
    #include <boost/units/unit.hpp>
    #include <boost/units/systems/si.hpp>
    
    #include <boost/units/io.hpp>
    #include <iostream>
    
    using boost::hash_value;
    
    template <typename... T>
    size_t hash_value(boost::units::quantity<T...> const& q) {
        using boost::hash_value; // enable ADL
        return hash_value(q.value());
    }
    
    int main() {
        using namespace boost::units::si;
        auto l  = 23.0*meter;
        auto dt = 2.0*second;
    
        std::cout << (l) << "\n";
        std::cout << (dt) << "\n";
        std::cout << (l/dt) << "\n";
    
        std::cout << std::hex << std::showbase;
        std::cout << hash_value(l) << "\n";
        std::cout << hash_value(dt) << "\n";
        std::cout << hash_value(l/dt) << "\n";
    }
    

    Prints

    23 m
    2 s
    11.5 m s^-1
    0x4037000000000000
    0x4000000000000000
    0x4027000000000000
    

    Using with boost::hash in generic code

    Make sure the overload is accessible on the point of instantiation. Enable argument dependent lookup (ADL):

    Live On Coliru

    #include <boost/units/unit.hpp>
    #include <boost/units/systems/si.hpp>
    
    #include <boost/units/io.hpp>
    #include <iostream>
    #include <boost/functional/hash.hpp>
    
    namespace boost::units {
        template <typename... T>
        size_t hash_value(quantity<T...> const& q) {
            using boost::hash_value; // enable ADL
            return hash_value(q.value());
        }
    }
    
    namespace detail {
        template<typename K>
            struct HashCompare {
                static size_t hash( const K& key )
                {
                    boost::hash<K> hasher;
                    return hasher(key);
                }
                static bool equal( const K& key1, const K& key2 ) {return key1 == key2;}
            };
    
        template <typename T>
        size_t test_mycompare(T const& v) {
            return HashCompare<T>::hash(v);
        }
    }
    
    int main() {
        using namespace boost::units::si;
        using boost::hash_value;
    
        auto l  = 23.0*meter;
        auto dt = 2.0*second;
    
        std::cout << (l) << "\n";
        std::cout << (dt) << "\n";
        std::cout << (l/dt) << "\n";
    
        std::cout << std::hex << std::showbase;
        std::cout << hash_value(l) << "\n";
        std::cout << hash_value(dt) << "\n";
        std::cout << hash_value(l/dt) << "\n";
    
        std::cout << detail::test_mycompare(l) << "\n";
        std::cout << detail::test_mycompare(dt) << "\n";
        std::cout << detail::test_mycompare(l/dt) << "\n";
    }
    

    Prints

    23 m
    2 s
    11.5 m s^-1
    0x4037000000000000
    0x4000000000000000
    0x4027000000000000
    0x4037000000000000
    0x4000000000000000
    0x4027000000000000