C ++回调?我应该使用会员功能指针/代表/活动吗?

时间:2010-11-24 16:06:32

标签: c++ events delegates callback handler

我正在进入一个对我来说很新的领域,但基本上我需要在C ++中实现回调。我正在为自己设计一个工具包来简化我的生活。基本上它是一个.dll插件,它会向我的其他.dll插件公开很多功能。

其中一个函数是HookEvent(const char * event_name,void * callback),它允许我挂钩被触发的不同事件。这是一个例子......

Example_Plugin1.dll执行HookEvent(“player_spawn”,& Plugin1 :: Event_PlayerSpawn); Example_Plugin2.dll执行HookEvent(“player_spawn”,& Plugin2 :: Event_PlayerSpawn);

我需要弄清楚设置一个适用于此的回调系统的最佳(最好是最简单)方法。我已经阅读了几个小时的C ++回调,并发现了不同的方法。

我认为最简单的事情就是制作一个模板,并使用typedef bool(ClassName :: * EventHookCallback)(IGameEvent,bool);在那之后,我有点模糊。

我还读到,Delegates或.NET风格的事件系统是其他可能的方法。我已经有点困惑了,所以我不想让自己更加困惑,但认为值得一提。

这是我正在阅读的C ++ .NET样式事件系统的链接。 http://cratonica.wordpress.com/2010/02/19/implementing-c-net-events-in-c/

那么你们有什么建议?关于实施它的任何提示都将非常受欢迎。

6 个答案:

答案 0 :(得分:3)

如果您希望广播事件触发Boost.Signals2可能适用。

  

Boost.Signals2库是一个   管理信号的实现   和插槽系统。信号代表   具有多个目标的回调,以及   也称为出版商或活动   在类似的系统中。信号是   连接到一些插槽,其中   是回调接收器(也称为   事件目标或订阅者),其中   在信号发出时被调用   “发射”。

即使您不需要这种级别的灵活性,您也应该能够使用Boost.Bind或C ++ 0x等效来简化代码中的函数绑定。

编辑: Herb Sutter对您可能遇到的问题here进行了很好的讨论。如果您决定不需要完整的Boost功能集,可以使用它作为指导,并自行滚动。

答案 1 :(得分:1)

如何使用Qt信号和插槽?它做了回调所做的事情,但没有让任何不属于你的回调参数全局的混乱。

答案 2 :(得分:1)

Boost.Signals将是我的选择,与boost::bindBoost.Function等内容相结合。

答案 3 :(得分:1)

我会使用抽象基类作为插件接口。 (事实上​​,我之前使用过类似下面的模式。)

Library,PluginIfc.h:

class PluginIfc {
public:
  virtual ~PluginIfc() = 0;
  virtual bool EventCallback(const char* event_name, IGameEvent, bool) = 0;     
};

// For Windows, add dllexport/dllimport magic to this declaration.
// This is the only symbol you will look up from the plugin and invoke.
extern "C" PluginIfc* GetPlugin();

插件:

#include <PluginIfc.h>
class Plugin1 : public PluginIfc {
public:
  virtual bool EventCallback(const char* event_name, IGameEvent, bool);
  Plugin1& get() { return the_plugin_obj; }

  bool Event_PlayerSpawn(IGameEvent, bool);
  // ...
private:
  std::vector<std::string> _some_member;

  static Plugin1 the_plugin_obj; // constructed when plugin loaded
};

Plugin1 Plugin1::the_plugin_obj;
PluginIfc* GetPlugin() { return &Plugin1::get(); }

这样,您的插件类可以轻松拥有成员,C ++的虚拟调用机制负责在this中为您提供良好的EventCallback指针。

每个事件类型创建一个虚拟方法可能很诱人,比如make Event_PlayerSpawn和类似的方法是虚拟的。但是,只要您想添加事件类型,如果这意味着更改类PluginIfc,则旧的编译插件将不再兼容。因此,使用字符串事件标识符(用于扩展性)更安全,并将主要的回调排序事件放在更具体的方法上。

这里的主要缺点(与信号槽类型实现相比)是所有回调必须采用相同的参数集。但你的问题听起来似乎已经足够了。并且通常可以通过确保参数集非常灵活,使用要解析的字符串或Any - 样式对象来在该限制内工作。

答案 4 :(得分:0)

听起来您可能对如何构建自己的插件框架感兴趣。你遇到的问题可能是一样的。看看这个很好的Dr Dobbs文章Building Your Own Plugin Framework

希望这有帮助!

答案 5 :(得分:0)

实现自己的回调系统并非易事 我的理解是你的目标是将事件类型映射到特定的回调函数 例如。如果“player_spawn”事件上升,将调用&amp; Plugin1 :: Event_PlayerSpawn。
所以你应该做的是以下几点:
1)定义所有感兴趣的事件。使它们尽可能通用。他们能 封装您需要的任何信息 2)创建注册商。即所有模块注册其特定兴趣的类 方法。例如。 Registrar.register(player_spawn,this,Event_PlayerSpawn);
3)注册商拥有所有订阅者的队列 4)您还可以为模块提供统一的界面。即所有模块都实现了一个特定的功能,但基于事件的数据可以做不同的事情 5)当事件发生时,所有对特定事件感兴趣的订阅者都会通过调用相应的函数得到通知 6)订户可以在需要时取消注册 希望这会有所帮助。