Patrick Uhlmann
Patrick Uhlmann

Reputation: 41

Pass any member function of any class as a Callback function

I'm working on a OpenGL menu which contains some buttons. I want to be able to associate an action (member function (with a fixed signature) of any class!) to a button which gets executed when the button is pressed. I can do it right now but only for one type. I want to be able to use any member function of any class for my callback.

Right now I'm doing it like this:

#define BUTTONCALLBACK(Func) bind1st( mem_fun( &ClassICanSupport::Func ), this )

I can then create a button like this:

Button* b = new Button("Bla", BUTTONCALLBACK(functionIWanttoCall));

The Callback function has the following signature:

void callback(Button* source);

When I press the button I can execute the callback function which I passed.

I had a look at boost::bind but I couldn't really find a way to tackle the problem. Furthermore all my classes are derived from a class Object so I thought about a void* which I could convert to the right class with some typeid hack but I was unable to get it working. At the end I always had the problem that I couldn't completly eliminate the class type of the callback function (which would be necessary to save the function pointer in my button class) and still being able to call the function.

Do you have any idea how to tackle this problem?

Upvotes: 4

Views: 2225

Answers (4)

SridharKritha
SridharKritha

Reputation: 9701

If you want a solution to your problem without using Boost library / without using new C++ features then one of the best choice is Generic Callbacks Dispatcher discussed by Danny Kalev / Herb Sutter.

http://www.gotw.ca/gotw/083.htm

Upvotes: 0

Xeo
Xeo

Reputation: 131887

Well, the easiest way would be with virtual functions, if you don't want to pull in Boost or don't have access to C++0x.

#include <iostream>

// fwd declare
class Button;

class BtnCallbackBase{
public:
  virtual void operator()(Button*) = 0;
};

template<class C>
class BtnCallback : public BtnCallbackBase{
private:
  typedef void (C::*callback_func)(Button*);

  C* _object;
  callback_func _onclick;

public:
  BtnCallback(C* obj, callback_func func)
    : _object(obj)
    , _onclick(func)
  {}

  virtual void operator()(Button* btn){
    (_object->*_onclick)(btn);
  }
};


class Button{
public:
  Button()
    : _onclick(0)
  {}

  void Click(){
    if(_onclick != 0)
      (*_onclick)(this);
  }

  template<class C>
  void RegisterCallback(C* obj, void (C::*func)(Button*)){
    // cleanup old callback, deleting null pointer is a noop
    delete _onclick;
    _onclick = new BtnCallback<C>(obj,func);
  }

  ~Button(){
    delete _onclick;
  }

private:
  BtnCallbackBase* _onclick;
};

class MyClass{
public:
  void ExampleCallback(Button* btn){
    std::cout << "Callback works!\n";
  }
};

int main(){
  Button btn;
  MyClass test;

  btn.RegisterCallback(&test, &MyClass::ExampleCallback);

  btn.Click();
}

Full example on Ideone.

Upvotes: 1

Puppy
Puppy

Reputation: 147036

You should use a function<void(Button*)> object. These are run-time polymorphic and can be used with any object that supports void operator()(Button*). You can find one in Boost, TR1 and C++0x. boost::bind works well with these objects.

Upvotes: 1

Cat Plus Plus
Cat Plus Plus

Reputation: 129984

Don't use pointers, use boost::function together with boost::bind (or std::function and std::bind if C++0x), something like

// in Button class (or whatever signature you need)
Button(const std::string&, boost::function<void(Button*)> callback) // ...
// you can then use callback as a function

// in calling code
Button *b = new Button("..", boost::bind(&Class::func, this));

Upvotes: 3

Related Questions