C++ Callbacks? Should I use Member Func Pointers/Delegates/Events?
I am entering a realm that is new to me, but basically I need to implement callbacks in C++. I am designing a toolkit for myself to use to simplify my life. Basically it is a .dll plugin that will be exposing a lot of functions to my other .dll plugins.
One of these functions is HookEvent(const char *event_name, void *callback) which will allow me to hook different events that get fired. Here would be an example...
Example_Plugin1.dll does HookEvent("player_spawn", &Plugin1::Event_PlayerSpawn); Example_Plugin2.dll does HookEvent("player_spawn", &Plugin2::Event_PlayerSpawn);
I need to figure out the best (and preferably easiest) method of setting up a callbacks system that will work well for this. I have been reading up on C++ callbacks for a few hours now, and found quite a few different approaches.
I assume the easiest thing to do would be make a template, and use typedef bool (ClassName::*EventHookCallback)(IGameEvent, bool); After that, I am a bit foggy.
I also read that Delegates or a .NET style events system are other possible approaches. I am already somewhat confused, so I don't want to confuse myself more, but figured it was worth asking.
Here is a link to the C++ .NET style events system I was reading about. http://cratonica.wordpress.com/2010/02/19/implementing-c-net-events-in-c/
So what do you guys suggest? Any tips as far as implementing it would be mos开发者_运维问答t appreciated.
If you want generalized event firing Boost.Signals2 might be applicable.
The Boost.Signals2 library is an implementation of a managed signals and slots system. Signals represent callbacks with multiple targets, and are also called publishers or events in similar systems. Signals are connected to some set of slots, which are callback receivers (also called event targets or subscribers), which are called when the signal is "emitted."
Even if you don't need this level of flexibility you should be able to simplify the function binding in your code using Boost.Bind, or the C++0x equivalents.
EDIT: There's an excellent discussion from Herb Sutter of the issues you could face here. You could use this for guidance if you decide you don't need the full Boost feature set, and so roll your own.
How about using Qt Signal and Slot? It does what callbacks do but without the messiness of making anything not part of your callback parameters global.
Boost.Signals
would be my choice, combined with things like boost::bind
and Boost.Function
.
I would use an abstract base class as a plugin interface. (And in fact, I have used a pattern like the one below before.)
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();
Plugin:
#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(); }
This way, your plugin classes can easily have members, and C++'s virtual call mechanism takes care of giving you a good this
pointer in EventCallback
.
It may be tempting to make a virtual method per event type, say just make Event_PlayerSpawn
and similar methods virtual. But then whenever you want to add an event type, if this means changing class PluginIfc
, your old compiled plugins are no longer compatible. So it's safer to use a string event identifier (for extensibility) and have the main callback sort events off to more specific methods.
The major drawback here (as compared to a signal-slot type implementation) is that all callbacks must take the same set of arguments. But your question sounded like that would be adequate. And it's often possible to work within that limitation by making sure the set of arguments is very flexible, using strings to be parsed or Any
-style objects.
Sounds like you might be interested in how to build your own plugin framework. The problems you'll encounter are likely the same. Have a look at this nice Dr Dobbs article Building Your Own Plugin Framework.
Hope this helps!
Implementing your own callback system is non-trivial.
My understanding is that your aim is to map event types to specific callback functions.
E.g. if "player_spawn" event is risen the &Plugin1::Event_PlayerSpawn will be called.
So what you should do is the following:
1) Define all the events of interest. Make them as generic as possible. They can
encapsulate any information you need
2) Create a Registrar. I.e. a class that all modules register their interest for specific
methods. E.g. Registrar.register(player_spawn,this,Event_PlayerSpawn);
3) Registrar has a queue of all subscribers.
4) You can also have a uniform interface for the modules. I.e. all module implement a specific function but based on event's data can do different things
5) When an event occurs, all the subscribers interested for the specific event get notified by calling the appropriate function
6)Subscriber can de-register when ever is need
Hope this helps.
精彩评论