1

I'm trying to form a std::tr1::unordered_map where the key type is a struct that includes a callback function, for which I'm using std::tr1::function. I'm running into two problems: 1) the function object does not appear to be equality comparable, as the Boost.Function documentation indicates; 2) I don't see how to implement the hash function, since I can't get a regular function pointer (or something else I could use for hashing) from the function object.

Here's the example code:

#include <boost/functional/hash.hpp>
#include <boost/tr1/functional.hpp>
#include <boost/tr1/unordered_map.hpp>
#include <iostream>

int f(int) {}
typedef std::tr1::function<int(int)> callback;

struct Record
{
  callback func;
  // More members...

  // Requirements for unordered_map key.
  friend bool operator==(Record const & lhs, Record const & rhs)
    { return lhs.func == rhs.func; } // error: ambiguous
  friend std::size_t hash_value(Record const & arg)
    { return boost::hash<void *>(arg.func.get()); } // error: no member get()
};

int main()
{
  std::tr1::unordered_map<Record, int> map;
  Record a = {f};
  map[a] = 0;

  return 0;
}

Here's some detail on the first error :

test.cpp: In function bool operator==(const Record&, const Record&):
test.cpp:16: error: ambiguous overload for operator== in lhs->Record::func == rhs->Record::func
test.cpp:16: note: candidates are: operator==(void (boost::function1<int, int>::dummy::*)(), void (boost::function1<int, int>::dummy::*)()) <built-in>
<root>/boost/function/function_template.hpp:1024: note:                 void boost::operator==(const boost::function1<R, T0>&, const boost::function1<R, T0>&) [with R = int, T0 = int]

For the second error, obviously there's no function<...>::get member, but what should I use instead?

I'm using Boost version 1.42 and g++ 4.2.2. Thanks for any help.

Update

The answer to the posted question is "you can't." tr1::function objects are hashable (e.g., using boost::hash), but are not equality comparable. If you want to use a function in the hash key, rethink the approach or find a workaround.

AndyJost
  • 1,085
  • 1
  • 10
  • 18
  • You could always decide to not include `func` in the calculation of the hash. That may not be ideal, but it's certainly valid. And if you expect the other members of `Record` to typically vary, it might not have any practical impact as well. – Jon Dec 08 '11 at 01:40
  • That's a great idea, in fact. I could capture what makes the records equal or not in a separate field "id" and just hash that. Having operator== work for functions would certainly be more elegant, but it doesn't look possible. – AndyJost Dec 08 '11 at 18:45
  • In case anyone else was confused, there are two sections in the [Boost.Function documentation](http://www.boost.org/libs/function) that discuss comparison. Under "Frequently Asked Questions," it explains why function object cannot be compared. Under "Comparing Boost.Function function objects" it describes how to compare function objects. Since the second page was updated more recently, I assumed that the FAQs were just out of date. Actually, both are correct. Function wrappers can be compared against anything storable in the wrapper BUT NOT ANOTHER WRAPPER. – AndyJost Dec 08 '11 at 18:56

3 Answers3

2

It would seem that TR1 specifically requires that

template<class Function2> bool operator==(const function<Function2>&);
template<class Function2> bool operator!=(const function<Function2>&);

remain undefined (3.7.2.6), so at the very least you'd have to find another way to get equality. Furthermore, I'm not finding any reference to a get() member method in the paper either.

matthias
  • 2,419
  • 1
  • 18
  • 27
  • The use of get() was supposed to convey the gist of what I'm trying to do; there is no member get in tr1::function. Taking a fresh look today, this particular problem became clear, though. I just didn't read the documentation for boost::hash carefully enough, and tried to call it like a function. It's a type. I'll add an answer for that. – AndyJost Dec 08 '11 at 18:31
  • Thanks @matthias, you are exactly right. There are no operator== or operator!= functions that compare two tr1::function objects (although a tr1::function can be compared against any object A the could be stored in the function). – AndyJost Dec 08 '11 at 19:02
2

I can answer my own question as regards to the hash_value. This is the correct way to call boost::hash with a tr1::function:

friend std::size_t hash_value(Record const & arg)
{
  boost::hash<callback> hasher;
  return hasher(arg.func);
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
AndyJost
  • 1,085
  • 1
  • 10
  • 18
0

There are some ideas using function::target discussed here and here. You may also want to consider the Boost.Signals library as it's designed to support registration of callbacks.

Community
  • 1
  • 1
Andrew Durward
  • 3,771
  • 1
  • 19
  • 30
  • Thanks for the info. This seems to confirm that the comparison I'm trying to make is going to be ugly if I try to make the function a part of the hash value. – AndyJost Dec 08 '11 at 18:41