How do I implement a callback in C++?

29,514

Solution 1

The best solution, use boost::function with boost::bind, or if your compiler supports tr1/c++0x use std::tr1::function and std::tr1::bind.

So it becomes as simple as:

boost::function<void()> callback;
Target myTarget;
callback=boost::bind(&Target::doSomething,&myTarget);

callback(); // calls the function

And your set callback becomes:

class MyClassWithCallback{
public:
  void setCallback(boost::function<void()> const &cb)
  {
     callback_ = cb;
  }
  void call_it() { callback_(); }
private:
  boost::function<void()> callback_;
};

Otherwise you need to implement some abstract class

struct callback { 
 virtual void call() = 0;
 virtual ~callback() {}
};

struct TargetCallback {
 virtual void call() { ((*self).*member)()); }
 void (Target::*member)();
 Target *self;
 TargetCallback(void (Target::*m)(),Target *p) : 
       member(m),
       self(p)
 {}
};

And then use:

myCaller.setCallback(new TargetCallback(&Target::doSomething,&myTarget));

When your class get modified into:

class MyClassWithCallback{
public:
  void setCallback(callback *cb)
  {
     callback_.reset(cb);
  }
  void call_it() { callback_->call(); }
private:
  std::auto_ptr<callback> callback_;
};

And of course if the function you want to call does not change you may just implement some interface, i.e. derive Target from some abstract class with this call.

Solution 2

One trick is to use interfaces instead, that way you don't need specifically to know the class in your 'MyClassWithCallback', if the object passed in implements the interface.

e.g. (pseudo code)

struct myinterface
{
  void doSomething()=0;
};

class Target : public myinterface { ..implement doSomething... };

and

myinterface *targetObj; 
void setCallback(myinterface *myObj){
    targetObj = myObj;
};

doing the callback

targetObj->doSomething();

setting it up:

Target myTarget;
MyClassWithCallback myCaller;
myCaller.setCallback(myTarget);

Solution 3

The Observer design pattern seems to be what you're looking for.

Solution 4

You have a few basic options:

1) Specify what class the callback is going to use, so that the object pointer and member function pointer types are known, and can be used in the caller. The class might have several member functions with the same signature, which you can choose between, but your options are quite limited.

One thing that you've done wrong in your code is that member function pointers and free function pointers in C++ are not the same, and are not compatible types. Your callback registration function takes a function pointer, but you're trying to pass it a member function pointer. Not allowed. Furthermore, the type of the "this" object is part of the type of a member function pointer, so there's no such thing in C++ as "a pointer to any member function which takes an integer and returns void". It has to be, "a pointer to any member function of Target which takes an integer and returns void". Hence the limited options.

2) Define a pure virtual function in an interface class. Any class which wants to receive the callback therefore can inherit from the interface class. Thanks to multiple inheritance, this doesn't interfere with the rest of your class hierarchy. This is almost exactly the same as defining an Interface in Java.

3) Use a non-member function for the callback. The for each class which wants to use it, you write a little stub free function which takes the object pointer and calls the right member function on it. So in your case you'd have:

dosomething_stub(void *obj, int a) {
    ((Target *)obj)->doSomething(a);
}

4) Use templates:

template<typename CB> class MyClassWithCallback {
    CB *callback;
 public:
    void setCallback(CB &cb) { callback = &cb; }
    void callCallback(int a) {
        callback(a);
    }
};

class Target {
    void operator()(int a) { /* do something; */ }
};

int main() {
    Target t;
    MyClassWithCallback<T> caller;
    caller.setCallback(t);
}

Whether you can use templates depends whether your ClassWithCallback is part of some big old framework - if so then it might not be possible (to be precise: might require some more tricks, such as a template class which inherits from a non-template class having a virtual member function), because you can't necessarily instantiate the entire framework once for each callback recipient.

Solution 5

Also, look at the Observer Pattern and signals and slots . This extends to multiple subscribers.

Share:
29,514
nacho4d
Author by

nacho4d

C/C++/Objective-C/C#/Javascript/shell-script/Japanese/Go/Spanish/English I love this site. So Helpful! https://twitter.com/nacho4d http://nacho4d-nacho4d.blogspot.com/

Updated on August 02, 2020

Comments

  • nacho4d
    nacho4d almost 4 years

    I want to implement a class in c++ that has a callback.

    So I think I need a method that has 2 arguments:

    • the target object. (let's say *myObj)
    • the pointer to a member function of the target object. (so i can do *myObj->memberFunc(); )

    The conditions are:

    • myObj can be from any class.

    • the member function that is gonna be the callback function is non-static.

    I've been reading about this but it seems like I need to know the class of myObj before hand. But I am not sure how to do it. How can I handle this? Is this possible in C++?

    This is something I have in mind but is surely incorrect.

    class MyClassWithCallback{
    public
        void *targetObj;
        void (*callback)(int number);
        void setCallback(void *myObj, void(*callbackPtr)(int number)){
            targetObj = myObj;
            callback = callbackPtr;
        };
        void callCallback(int a){
            (myObj)->ptr(a);
        };
    };
    class Target{
    public
        int res;
        void doSomething(int a){//so something here. This is gonna be the callback function};        
    };
    
    int main(){
        Target myTarget;
        MyClassWithCallback myCaller;
        myCaller.setCallback((void *)&myTarget, &doSomething);
    

    }

    I appreciate any help.

    Thank you.

    UPDATE Most of you said Observing and Delegation, well that's i exactly what i am looking for, I am kind of a Objective-C/Cocoa minded guy. My current implementation is using interfaces with virtual functions. Is just I thought it would be "smarter" to just pass the object and a member function pointer (like boost!) instead of defining an Interface. But It seems that everybody agrees that Interfaces are the easiest way right? Boost seems to be a good idea, (assuming is installed)

    • Artyom
      Artyom almost 14 years
      Observer pattern requires changes in "observed" class interface, sometimes it is much simpler and clearer use function and bind instead of interfaces. Also note, if you using recent gcc, you already have function and bind so no need of boost. Using function/bind is functional programing patters while using interfaces is more using "standard" design pattern that has different meaning
  • nacho4d
    nacho4d almost 14 years
    Yes, I come from a Objective-C world and that is exactly what I want to implement.
  • Said algharibi
    Said algharibi almost 14 years
    +1, boost::bind is definitely the way to go for C++-only callback
  • nacho4d
    nacho4d almost 14 years
    boost:bind is definitely the answer is was looking for. I probably will have to install boost though.
  • Artyom
    Artyom almost 14 years
    @nacho4d what compiler do you use? Many already have bind/function in their standard library, so no need for boost
  • nacho4d
    nacho4d almost 14 years
    Currently I am using gcc in my iMac, but actually I creating a library that should compile in windows principally.
  • Artyom
    Artyom almost 14 years
    @nacho4d Generally gcc-4.x from mingw has it and MSVC2010 should have.
  • ejgottl
    ejgottl almost 14 years
    In g++ 4.5 and msvc2010 you can use a lambda expression instead of bind. Your code will not be compatible with older compilers though.
  • Marchy
    Marchy about 11 years
    +1 for showing an approach for building your own callback (without using an external library). How would this look if it was templated?