简体   繁体   中英

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?

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));

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.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM