Search code examples
c++functioncallbackmember

C++ callback using class member


I know this has been asked so many times, and because of that it's difficult to dig through the cruft and find a simple example of what works.

I've got this, it's simple and it works for MyClass...

#include <iostream>
using std::cout;
using std::endl;

class MyClass
{
    public:
        MyClass();
        static void Callback(MyClass* instance, int x);
    private:
        int private_x;
};

class EventHandler
{
    public:
        void addHandler(MyClass* owner)
        {
            cout << "Handler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner,1);
        }
};

EventHandler* handler;

MyClass::MyClass()
{
    private_x = 5;
    handler->addHandler(this);
}

void MyClass::Callback(MyClass* instance, int x)
{
    cout << x + instance->private_x << endl;
}

int main(int argc, char** argv)
{
    handler = new EventHandler();
    MyClass* myClass = new MyClass();
}

class YourClass
{
    public:
        YourClass();
        static void Callback(YourClass* instance, int x);
};

How can that be rewritten so EventHandler::addHandler() will work with both MyClass and YourClass. I'm sorry but it's just the way my brain works, I need to see a simple example of what works before I can comprehend why/how it works. If you've got a favorite way to make this work now's the time to show it off, please markup that code and post it back.

[edit]

It was answered but the answer was deleted before I could give the checkmark. The answer in my case was a templated function. Changed addHandler to this...

class EventHandler
{
    public:
        template<typename T>
        void addHandler(T* owner)
        {
            cout << "Handler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner,1);
        }
};

Solution

  • Instead of having static methods and passing around a pointer to the class instance, you could use functionality in the new C++11 standard: std::function and std::bind:

    #include <functional>
    class EventHandler
    {
        public:
            void addHandler(std::function<void(int)> callback)
            {
                cout << "Handler added..." << endl;
                // Let's pretend an event just occured
                callback(1);
            }
    };
    

    The addHandler method now accepts a std::function argument, and this "function object" have no return value and takes an integer as argument.

    To bind it to a specific function, you use std::bind:

    class MyClass
    {
        public:
            MyClass();
    
            // Note: No longer marked `static`, and only takes the actual argument
            void Callback(int x);
        private:
            int private_x;
    };
    
    MyClass::MyClass()
    {
        using namespace std::placeholders; // for `_1`
    
        private_x = 5;
        handler->addHandler(std::bind(&MyClass::Callback, this, _1));
    }
    
    void MyClass::Callback(int x)
    {
        // No longer needs an explicit `instance` argument,
        // as `this` is set up properly
        cout << x + private_x << endl;
    }
    

    You need to use std::bind when adding the handler, as you explicitly needs to specify the otherwise implicit this pointer as an argument. If you have a free-standing function, you don't have to use std::bind:

    void freeStandingCallback(int x)
    {
        // ...
    }
    
    int main()
    {
        // ...
        handler->addHandler(freeStandingCallback);
    }
    

    Having the event handler use std::function objects, also makes it possible to use the new C++11 lambda functions:

    handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });