I am registering callbacks to a hash map in a class to handle various 'events', so I am using std::function
to 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;
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);
});
}