1

I am registering callbacks to a hash map in a class to handle various 'events', so I am using std::functionto store them in the hash map I want the callbacks to be bound to the class so they can access some common methods, so I am using std::bind to accomplish this. What I don't understand is why the first example works, but when I try to refactor the std::bind call to the bindEvent function, compilation fails.

So this works:

void MyClass::bindEvent(const QString event, std::function<QJsonObject(const QString &, const QJsonObject &)> handler)
{
   mHandlers.insert(event.toLower(), handler);
}


void MyClass::registerAll()
{

   this->bindEvent(QStringLiteral("do_something"), std::bind(&MyClass::doSomething, this, _1, _2));
   this->bindEvent(QStringLiteral("do_something_else"), std::bind(&MyClass::doSomethingElse, this, _1, _2));
}

This fails:

void MyClass::bindEvent(const QString event, std::function<QJsonObject(const QString &, const QJsonObject &)> handler)
{
   mHandlers.insert(event.toLower(), std::bind(handler, this, _1, _2));
}


void MyClass::registerAll()
{

   this->bindEvent(QStringLiteral("do_something"), &MyClass::doSomething);
   this->bindEvent(QStringLiteral("do_something_else"), &MyClass::doSomethingElse);
}

The error message:

../src/MyClass.cpp: In member function ‘virtual bool MyClass::registerAll()’:
../src/MyClass.cpp:124:388: error: no matching function for call to ‘MyClass::bindEvent(QString, QJsonObject (MyClass::*)(const QString&, const QJsonObject&))’
    this->bindEvent(QStringLiteral("do_something"), &MyClass::doSomething);
                                                                                                                                                                                                                                                                                                                                                                                                    ^
../src/MyClass.cpp:124:388: note: candidate is:
../src/MyClass.cpp:109:6: note: void MyClass::bindEvent(QString, std::function<QJsonObject(const QString&, const QJsonObject&)>)
 void MyClass::bindEvent(const QString event, std::function<QJsonObject(const QString &, const QJsonObject &)> handler)
      ^
../src/MyClass.cpp:109:6: note:   no known conversion for argument 2 from ‘QJsonObject (MyClass::*)(const QString&, const QJsonObject&)’ to ‘std::function<QJsonObject(const QString&, const QJsonObject&)>’

The hash itself is declared as

QHash<const QString, std::function<QJsonObject(const QString &, const QJsonObject &)> > mHandlers;
jramm
  • 6,415
  • 4
  • 34
  • 73

2 Answers2

2

The second attempt fails because a member function needs to be invoked on an object. When you pass a pointer-to-member-function there is no object to invoke the function on so it can't be converted to a std::function.

You can either stick to what you are doing now, or you can accept a pointer-to-member-function in bindEvent.

void MyClass::bindEvent(const QString event, QJsonObject (MyClass::*handler)(const QString &, const QJsonObject &))
{
   mHandlers.insert(event.toLower(), std::bind(handler, this, _1, _2));
}

Another thing to point out here is that it is since a while back recommended to use lambdas over std::bind. From my understanding it gives the compiler a better chance to optimize the code.

Personally I also find it more readable.

void MyClass::bindEvent(const QString event, QJsonObject (MyClass::*handler)(const QString &, const QJsonObject &))
{
   mHandlers.insert(event.toLower(), [&, handler](const QString& s, const QJsonObject& o){
      this->*handler(s, o);
   });
}
super
  • 12,335
  • 2
  • 19
  • 29
  • With using lambdas, the 'type' in the hash map just becomes a generic pointer-to-function, does it not?: `QHash mHandlers;` – jramm Apr 06 '18 at 06:45
  • @jramm The type is still a `std::function` as defined by the map. It's not quite a pointer-to-function since it also holds a reference to the instance on which to invoke the function. It's a function object with state, even if the state never changes in this case. – super Apr 06 '18 at 06:53
  • Ah. So my hash can remain declared as `QHash > mHandlers;` – jramm Apr 06 '18 at 06:57
1

I think the issue might be that it isn't a function, but a function contained in a class or namespace. You need to specify this in the signature i think.

alternatively using lambdas could lead to a workaround. I can't help any further since you didn't provide a minimal working example. When creating that you might find what went wrong or enable people to find your mistake.

Gladaed
  • 211
  • 1
  • 8