Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building an unordered map with tuples as keys

Tags:

In a C++ program with Boost, I am trying to build an unordered map whose keys are tuples of doubles:

typedef boost::tuples::tuple<double, double, double, double> Edge; typedef boost::unordered_map< Edge, int > EdgeMap; 

Initializing the map completes OK, however, when I try to populate it with keys and values

EdgeMap map; Edge key (0.0, 0.1, 1.1, 1.1); map[key] = 1; 

I encounter the following error message:

/usr/include/boost/functional/hash/extensions.hpp:176: error: no matching function for call to ‘hash_value(const boost::tuples::tuple<double, double, double, double, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>&)’ 

I presume this is because I need to specify a hash function for the tuple keys. How can I do that?

EDIT:

Following the suggestions below, I wrote the following implementation:

#include <boost/tuple/tuple.hpp> #include <boost/unordered_map.hpp>  typedef boost::tuples::tuple<double, double, double, double> Edge;  struct ihash     : std::unary_function<Edge, std::size_t> {     std::size_t operator()(Edge const& e) const     {         std::size_t seed = 0;         boost::hash_combine( seed, e.get<0>() );         boost::hash_combine( seed, e.get<1>() );         boost::hash_combine( seed, e.get<2>() );         boost::hash_combine( seed, e.get<3>() );         return seed;     } };  struct iequal_to     : std::binary_function<Edge, Edge, bool> {     bool operator()(Edge const& x, Edge const& y) const     {         return ( x.get<0>()==y.get<0>() &&                  x.get<1>()==y.get<1>() &&                  x.get<2>()==y.get<2>() &&                  x.get<3>()==y.get<3>());     } };  typedef boost::unordered_map< Edge, int, ihash, iequal_to > EdgeMap;  int main() {      EdgeMap map;     Edge key (0.0, 0.1, 1.1, 1.1);     map[key] = 1;      return 0; } 

Is it possible to shorten it?

like image 809
D R Avatar asked Aug 31 '10 18:08

D R


People also ask

Can unordered map have pair as key?

Unordered Map does not contain a hash function for a pair like it has for int, string, etc, So if we want to hash a pair then we have to explicitly provide it with a hash function that can hash a pair. unordered_map can takes upto 5 arguments: Key : Type of key values. Value : Type of value to be stored against the key.

Is an unordered set of tuples?

An unordered set of tuples is an unordered set in which each of the elements is a tuple. Note that by default an unordered set doesn't have the functionality of tuples. In simple words, one cannot declare an unordered set of tuples directly in C++. One have to pass a Hash function as an argument to the unordered set.

Are C++ tuples hashable?

The answer is no, can't be done for all tuples, since a tuple is a variadic template type.

How do you implement an unordered map?

Internally unordered_map is implemented using Hash Table, the key provided to map are hashed into indices of a hash table that is why the performance of data structure depends on hash function a lot but on an average, the cost of search, insert and delete from the hash table is O(1).


2 Answers

Actually, you could perfectly define a general hash function for boost::tuple. The only requirement is that it lives within the same namespace so that it is picked up by ADL.

I am actually surprised that they did not already write one.

namespace boost { namespace tuples {    namespace detail {      template <class Tuple, size_t Index = length<Tuple>::value - 1>     struct HashValueImpl     {       static void apply(size_t& seed, Tuple const& tuple)       {         HashValueImpl<Tuple, Index-1>::apply(seed, tuple);         boost::hash_combine(seed, tuple.get<Index>());       }     };      template <class Tuple>     struct HashValueImpl<Tuple,0>     {       static void apply(size_t& seed, Tuple const& tuple)       {         boost::hash_combine(seed, tuple.get<0>());       }     };   } // namespace detail    template <class Tuple>   size_t hash_value(Tuple const& tuple)   {     size_t seed = 0;     detail::HashValueImpl<Tuple>::apply(seed, tuple);     return seed;   }  } } 

Note: I have only proved it correct, I haven't tested it.

like image 165
Matthieu M. Avatar answered Sep 30 '22 22:09

Matthieu M.


You need a bit of front matter. Because of the underlying implementation of boost::tuples::tuple, make Edge a structure to allow the overloads to resolve correctly. Otherwise, you'll get no matches for

  • boost::hash_value(const Edge &)
  • operator==(const Edge &, const Edge &)

Code below:

struct Edge {   Edge(double x1, double x2, double x3, double x4)     : tuple(x1,x2,x3,x4) {}   boost::tuples::tuple<double, double, double, double> tuple; };  // XXX: less than ideal implementation! bool operator==(const Edge &a, const Edge &b) {   return a.tuple.get<0>() == b.tuple.get<0>() &&          a.tuple.get<1>() == b.tuple.get<1>() &&          a.tuple.get<2>() == b.tuple.get<2>() &&          a.tuple.get<3>() == b.tuple.get<3>(); }  // XXX: me too! std::size_t hash_value(const Edge &e) {   std::size_t seed = 0;   boost::hash_combine(seed, e.tuple.get<0>());   boost::hash_combine(seed, e.tuple.get<1>());   boost::hash_combine(seed, e.tuple.get<2>());   boost::hash_combine(seed, e.tuple.get<3>());   return seed; }  typedef boost::unordered_map< Edge, int > EdgeMap; 
like image 23
Greg Bacon Avatar answered Sep 30 '22 22:09

Greg Bacon