将任何类的任何成员函数作为回调函数传递

4
我正在开发一个包含一些按钮的OpenGL菜单。我希望能够将一个动作(任何类的成员函数(具有固定签名)!)与按下按钮时执行的回调相关联。目前,我只能为一种类型的回调函数进行关联。我希望能够使用任何类的任何成员函数作为我的回调函数。
目前,我是这样做的:
#define BUTTONCALLBACK(Func) bind1st( mem_fun( &ClassICanSupport::Func ), this )

然后我可以创建一个这样的按钮:

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

回调函数的签名如下:
void callback(Button* source);

当我按下按钮时,可以执行我传递的回调函数。我尝试过boost::bind,但是我无法找到解决问题的方法。此外,我的所有类都派生自一个Object类,所以我考虑使用void*,然后通过一些typeid hack将其转换为正确的类,但我无法使其工作。最终,我总是无法完全消除回调函数的类类型(这对于在我的按钮类中保存函数指针是必要的),同时仍能调用该函数。您有任何想法如何解决这个问题吗?
4个回答

3
不要使用指针,使用boost::function与boost::bind(或C++0x中的std::function和std::bind)一起使用,例如:
// 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));

1
你应该使用一个 function<void(Button*)> 对象,这些对象是运行时多态的,并且可以与任何支持 void operator()(Button*) 的对象一起使用。在 Boost、TR1 和 C++0x 中都可以找到这样的对象,boost::bind 与这些对象搭配使用效果很好。

1

如果你不想使用Boost或者没有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();
}

在Ideone上的完整示例。


0
如果您想在不使用Boost库或新的C++特性的情况下解决问题,那么一个最佳选择就是由Danny Kalev和Herb Sutter讨论的"通用回调分发器"。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接