tags:

views:

225

answers:

6

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)

+6  A: 

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);
Anders K.
http://en.wikipedia.org/wiki/Observer_pattern
Axel Gneiting
+9  A: 

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.

Artyom
+1 for using boost in a nice fashion
dark_charlie
+1, boost::bind is definitely the way to go for C++-only callback
Calvin1602
boost:bind is definitely the answer is was looking for. I probably will have to install boost though.
nacho4d
@nacho4d what compiler do you use? Many already have bind/function in their standard library, so no need for boost
Artyom
Currently I am using gcc in my iMac, but actually I creating a library that should compile in windows principally.
nacho4d
@nacho4d Generally gcc-4.x from mingw has it and MSVC2010 should have.
Artyom
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.
ejgottl
+3  A: 

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

Greg Sexton
+2  A: 

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

nonnb
Yes, I come from a Objective-C world and that is exactly what I want to implement.
nacho4d
+1  A: 

In C++, pointers to class methods are hardly used. The fact that you called in - it is delegates and their use is not recommended. Instead of them, you must use virtual functions and abstract classes. However, C++ would not have been so fond of me, if it not supported completely different concepts of programming. If you still want delegates, you should look towards "boost functional" (part of C + +0 x), it allows pointers to methods of classes regardless of the class name. Besides, in C++ Builder has type __closure - implementation of a delegate at the level of the compiler.

P.S. Sorry for bad English...

Daminian
+1  A: 

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.

Steve Jessop