我正在进入一个对我来说很新的领域,但基本上我需要在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/
那么你们有什么建议?关于实施它的任何提示都将非常受欢迎。
答案 0 :(得分:3)
如果您希望广播事件触发Boost.Signals2可能适用。
Boost.Signals2库是一个 管理信号的实现 和插槽系统。信号代表 具有多个目标的回调,以及 也称为出版商或活动 在类似的系统中。信号是 连接到一些插槽,其中 是回调接收器(也称为 事件目标或订阅者),其中 在信号发出时被调用 “发射”。
即使您不需要这种级别的灵活性,您也应该能够使用Boost.Bind或C ++ 0x等效来简化代码中的函数绑定。
编辑: Herb Sutter对您可能遇到的问题here进行了很好的讨论。如果您决定不需要完整的Boost功能集,可以使用它作为指导,并自行滚动。
答案 1 :(得分:1)
如何使用Qt信号和插槽?它做了回调所做的事情,但没有让任何不属于你的回调参数全局的混乱。
答案 2 :(得分:1)
Boost.Signals
将是我的选择,与boost::bind
和Boost.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)订户可以在需要时取消注册
希望这会有所帮助。