Search code examples
c++functional-programmingstd-functionstdbind

C++ binding callbacks to a class


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;

Solution

  • 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);
       });
    }