0

我有一个 A 类,我打算将它放入共享库中,因为它与设备驱动程序交互。

我有一个 B 类,将来可能是 C、D、E...,它将使用共享库使用 A 类。

我想要在 A 类中设置回调函数的能力,以便当特定事件发生时,B、C、D、E 类的非静态成员函数应该由 A 类调用。

我在 google 上搜索了 C++ 中的回调函数,但发现 C 风格的回调定义不支持非静态成员函数。

可以使用函数指针来完成吗?

请为不违反 OOPS 概念的 C++ 回调提供一些建议。

我还发现了一个名为“Boost”的库,它提供了类似的功能,但如果可能的话,我想避免额外库的开销。回调函数推荐使用 Boost 吗?

编辑: B、C、D、E 不会共享任何层次结构,它们将是完全独立的类。但是它们都有A类的对象。A类也有一个公共函数来设置回调函数。

4

2 回答 2

4

一个选择,如果你真的想避免多态函数包装器几乎不重要的开销,是使这些函数静态并让它们采用“用户数据”void*参数,指向函数是成员的类的适当实例的。在静态函数内部,您然后转换回适当的类型:

#include <iostream>

struct A{
  typedef void (*callback_type)(void*, int);
  callback_type callback;
  void* user_data;
  void set_callback(callback_type cb, void* ud){
     callback = cb; user_data = ud;
  }
  void invoke(){ callback(user_data, 42); }
};

struct B{
  static void cb_foo(void* vself, int data){
    B* self = static_cast<B*>(vself);
    self->foo(data);
  }
  void foo(int data){ std::cout << data * 2 << "\n"; }
};

struct C{
  static void cb_bar(void* vself, int data){
    C* self = static_cast<C*>(vself);
    self->bar(data);
  }
  void bar(int data){ std::cout << data / 2 << "\n"; }
};

int main(){
  A a;
  B b;
  a.set_callback(&B::cb_foo, &b);
  a.invoke();
  C c;
  a.set_callback(&C::cb_bar, &c);
  a.invoke();
}

Ideone 上的实时示例。

不过,我个人建议使用std::function,因为上述内容在可以接受为回调的方面受到严重限制。std::function是一个多态函数包装器,这意味着它可以采用普通函数指针、成员函数指针甚至函子(函数对象)并以相同的方式调用它们。与std::bind允许您将参数绑定到函数的 一起,您可以轻松地回调成员函数。Boost 也提供了它们(Boost.FunctionBoost.Bind)。

#include <iostream>
#include <functional> // C++11
//#include <boost/function.hpp>
//#include <boost/bind.hpp>

struct A{
  std::function<void(int)> callback;
  void invoke(){ callback(42); }
};

struct B{
  void foo(int data){ std::cout << data * 2 << "\n"; }
};

struct C{
  void bar(int data){ std::cout << data / 2 << "\n"; }
};

int main(){
  using namespace std::placeholders; // namespace for argument placeholders for std::bind
                                     // not needed for Boost.Bind
  A a;
  B b;
  a.callback = std::bind(&B::foo, &b, _1);
  a.invoke();
  C c;
  a.callback = std::bind(&C::bar, &c, _1);
  a.invoke();
};

Ideone 上的实时示例。

基本上std::bind会自动完成您在第一个版本中必须手动执行的操作,它保存对象指针并调用其上的成员函数。但是,它不是通过void*指针来执行此操作的,而是std::bind为每个不同的对象指针返回不同的绑定器类型。这就是你需要的原因std::function,因为它不在乎你传递了什么。

于 2012-07-17T12:59:55.247 回答
0

假设 {B, C, D, E} 共享一些层次结构,因此您不需要为每个类编写新版本,使回调函数静态但添加一个附加参数,它是对 {B, C, D 的引用, E} 回调中涉及的实例。这样,一旦您在函数的上下文中,您将能够在相关对象上调用非静态函数/操作。

如果 {B, C, D, E} 或将来出现的任何东西的类层次结构不同,并且您找不到共同的起点,您可能需要求助于更通用的东西,例如 void 指针,尽管这使得很难知道可以在对象上调用哪些功能。

于 2012-07-17T12:36:09.613 回答