106

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);
        }
};
jww
  • 97,681
  • 90
  • 411
  • 885
BentFX
  • 2,746
  • 5
  • 25
  • 30
  • 4
    Who posted the templated function example? You got the checkmark, but you deleted your answer while I was testing. It did exactly what I needed. A simple function template got lost in the stew of all the other info I was reading. Your answer added as edit to question. – BentFX Jan 07 '13 at 03:42
  • I think it was JaredC. You may need to hunt him down =P – WhozCraig Jan 07 '13 at 03:43

6 Answers6

200

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'; });
BentFX
  • 2,746
  • 5
  • 25
  • 30
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 4
    Thanks Joachim! This example does a lot to demystify std::function and std::bind. I'll certainly use it in the future! [edit] I still don't get lambda at all :) – BentFX Jan 07 '13 at 03:58
  • The templated class does just what I need, but the static functions and instance pointers don't make for nice reading. :) The more I look at this the better I understand it, and who knows, it just may be the future. – BentFX Jan 07 '13 at 04:20
  • I edited the answer to add `#include ` and also for those like me absolutely new to this, you need a `-std=c++11` in the g++ command line to make this work. But it works and I like it. Thanks again. – BentFX Jan 07 '13 at 04:46
  • 3
    I folded this into my larger project(about 6,000 lines. That's big for me.) It uses vectors of button definitions with differing callbacks and parameters then feeds that to wxWidgets, so objects can manage their own buttons in the wxFrame. This simplified things a lot! I can't say it enough, the internet contains far too much technicality and opinion, and not enough simple examples. – BentFX Jan 07 '13 at 05:39
  • With every bind, must an unbind be called? Or what will happen when the bound class gets released from memory and the callback gets called? – user819640 Jan 03 '15 at 18:25
  • 1
    @user819640 There is no "unbind", instead `std::bind` just returns an (unspecified) object, and when you're done with it you can just let it go out of scope. If the bound object is destructed and you try to call the function you will get [*undefined behavior*](http://en.wikipedia.org/wiki/Undefined_behavior). – Some programmer dude Jan 03 '15 at 18:41
  • 2
    `handler->addHandler()`, means that somewhere you create an object of `EventHandler`? Good answer btw, +1. – gsamaras Aug 07 '15 at 10:03
  • It's not clear if `handler` is to be passed into the `MyClass` constructor or is to be instantiated inside the `MyClass` constructor; and, if it's to be instantiated, how a reference to it is to be held. – Jefferson Hudson Apr 13 '17 at 19:33
  • @JeffersonHudson (and others) If you read the question the `handler` pointer is defined as a global variable. – Some programmer dude Apr 14 '17 at 00:18
  • please dont explain placeholder lik if it was a standard obvious paradigm for anyone – Phil Mar 18 '18 at 13:12
  • thanks - but could you change _1 to std::placeholders::_1 in the example ? it was not immediately obvious what this new syntax was to me – Goblinhack Jul 21 '18 at 18:26
  • 2
    Note that you need the number of placeholders to match the number of arguments, so if there are two arguments in the callback you need to use `...., _1, _2)` and so on. – Den-Jason Jun 17 '20 at 12:50
  • Is it possible with this example to implement also `removeHandler()`? I don't think an event trigger is useful if you can't ask it to stop telling you about its events. – adigostin Aug 16 '20 at 14:51
  • @adigostin That really depends on how you store the handlers. Typically one would have some sort of container for the event-handlers (the `std::function` objects) so they cant be invoked at a later time, and if the container have a way to remove elements then it's possible to implement a `removeHandler` function. – Some programmer dude Aug 16 '20 at 16:27
  • I just spent 4 days trying to figure this out. Thank you so very much. – pennyrave Dec 23 '20 at 18:31
8

Here's a concise version that works with class method callbacks and with regular function callbacks. In this example, to show how parameters are handled, the callback function takes two parameters: bool and int.

class Caller {
  template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int))
  {
    using namespace std::placeholders; 
    callbacks_.emplace_back(std::bind(mf, object, _1, _2));
  }
  void addCallback(void(* const fun)(bool,int)) 
  {
    callbacks_.emplace_back(fun);
  }
  void callCallbacks(bool firstval, int secondval) 
  {
    for (const auto& cb : callbacks_)
      cb(firstval, secondval);
  }
private:
  std::vector<std::function<void(bool,int)>> callbacks_;
}

class Callee {
  void MyFunction(bool,int);
}

//then, somewhere in Callee, to add the callback, given a pointer to Caller `ptr`

ptr->addCallback(this, &Callee::MyFunction);

//or to add a call back to a regular function
ptr->addCallback(&MyRegularFunction);

This restricts the C++11-specific code to the addCallback method and private data in class Caller. To me, at least, this minimizes the chance of making mistakes when implementing it.

Note that with C++20's bind_front you can simplify add_callback for class member functions to:

      template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int))
      {
        callbacks_.emplace_back(std::bind_front(mf, object));
      }
rsjaffe
  • 5,600
  • 7
  • 27
  • 39
4

What you want to do is to make an interface which handles this code and all your classes implement the interface.

class IEventListener{
public:
   void OnEvent(int x) = 0;  // renamed Callback to OnEvent removed the instance, you can add it back if you want.
};


class MyClass :public IEventListener
{
    ...
    void OnEvent(int x); //typically such a function is NOT static. This wont work if it is static.
};

class YourClass :public IEventListener
{

Note that for this to work the "Callback" function is non static which i believe is an improvement. If you want it to be static, you need to do it as JaredC suggests with templates.

Karthik T
  • 31,456
  • 5
  • 68
  • 87
4

A complete working example from the code above.... for C++11:

#include <stdlib.h>
#include <stdio.h>
#include <functional>

#if __cplusplus <= 199711L
  #error This file needs at least a C++11 compliant compiler, try using:
  #error    $ g++ -std=c++11 ..
#endif

using namespace std;

class EventHandler {
    public:
        void addHandler(std::function<void(int)> callback) {
            printf("\nHandler added...");
            // Let's pretend an event just occured
            callback(1);
        }
};


class MyClass
{
    public:
        MyClass(int);
        // Note: No longer marked `static`, and only takes the actual argument
        void Callback(int x);

    private:
        EventHandler *pHandler;
        int private_x;
};

MyClass::MyClass(int value) {
    using namespace std::placeholders; // for `_1`

    pHandler = new EventHandler();
    private_x = value;
    pHandler->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
    printf("\nResult:%d\n\n", (x+private_x));
}

// Main method
int main(int argc, char const *argv[]) {

    printf("\nCompiler:%ld\n", __cplusplus);
    new MyClass(5);
    return 0;
}


// where $1 is your .cpp file name... this is the command used:
// g++ -std=c++11 -Wall -o $1 $1.cpp
// chmod 700 $1
// ./$1

Output should be:

Compiler:201103

Handler added...
Result:6
Craig D
  • 351
  • 2
  • 7
1

MyClass and YourClass could both be derived from SomeonesClass which has an abstract (virtual) Callback method. Your addHandler would accept objects of type SomeonesClass and MyClass and YourClass can override Callback to provide their specific implementation of callback behavior.

s.bandara
  • 5,636
  • 1
  • 21
  • 36
  • For what I'm doing I toyed with this idea. But because of the number of widely different classes that that would be using my handler I didn't see it as an option. – BentFX Jan 07 '13 at 04:03
0

If you have callbacks with different parameters you can use templates as follows:
// compile with: g++ -std=c++11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbacksApp

#include <functional>     // c++11

#include <iostream>        // due to: cout


using std::cout;
using std::endl;

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

class OtherClass
{
    public:
        OtherClass();
        static void Callback(OtherClass* instance, std::string str);
    private:
        std::string private_str;
};

class EventHandler
{

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

};

MyClass::MyClass()
{
    EventHandler* handler;
    private_x = 4;
    handler->addHandler(this, private_x);
}

OtherClass::OtherClass()
{
    EventHandler* handler;
    private_str = "moh ";
    handler->addHandler(this, private_str );
}

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

void OtherClass::Callback(OtherClass* instance, std::string private_str)
{
    cout << " OtherClass::Callback(OtherClass* instance, std::string private_str) ==> " 
         << " Hello " << instance->private_str << endl;
}

int main(int argc, char** argv)
{
    EventHandler* handler;
    handler = new EventHandler();
    MyClass* myClass = new MyClass();
    OtherClass* myOtherClass = new OtherClass();
}
mohDady
  • 21
  • 3
  • 1
    Can you please explain what you did to solve the OP's problem? Is it really necessary to include the complete code of the OP? The OP wanted his code to work with his `YourClass`. You seem to have removed that class and added a different `OtherClass`. Moreover, the question has already a well received answer. In how far is your solution better so that it is worth posting? – honk Oct 09 '14 at 16:03
  • I did not say My posting is a better solution. I showed how to use "OtherClass" in a template way. – mohDady Oct 09 '14 at 17:51