Event System Changes

I guess it’s a bad sign when you start rewriting the rewrite, but I’m working on some event system changes now that should make the system much more flexible. In our current system, you register a shared_ptr to an “EventListener” to receive events of a particular type. That is ok, but it is a little ugly for a few reasons. Often a class wants to register itself, and getting a shared_ptr from *this is not pleasant. I ended up just having classes that want to register themselves have a separate listener class that they create so that I can easily get a shared_ptr to use (e.g. the IThreadedSubSystem and ThreadedSubSystemListener). That is pretty ugly. It can also be constraining because you either have to code up separate listeners for every event types you want, or do some kind of “if .. else if .. else if ..” to figure out what to do with the event that the listener receives. That is pretty ugly too!

I decided that it would make a lot more sense to just register a boost::function that takes a shared_ptr to the event. That way, you could bind a free standing function, or a member function, or whatever to receive events. Objects could register themselves to receive events without jumping through hoops. However, boost::function objects are not comparable to other boost::function objects, which makes it hard to store them in map.

I have seen boost::signals before. We actually talked about just pitching our EventSystem altogether and just giving each event type a static signal. Then if you care about that event type, you just connect to that signal. That could be nice, but I think having the event system take care of that is a little more clear and makes event queuing a little easier.

Anyway, what I am working on now is actually using boost::signals for the actual event dispatch, but still within the framework of our event system. That allows us to still use boost::functions (which is what drives the signal library), but it has a workaround for the lack of comparisons. When you register an event handler function with the EventSystem, it looks up and if necessary creates the signal. The event handler is connected to that signal and the EventSystem returns a signal “connection”. The user can then disconnect their handler by calling disconnect() on that “connection” object. That could be pretty unwieldy if you wanted to register for 10 event types and keep track of 10 different connections. As a result, I am also creating another class to manage those connections automatically. You just create an instance of this class and then call the “AddHandler” method with the event type and handler object. This manager class stores the event connection and takes care of disconnecting it when it is destroyed. Alternatively, you can call “RemoveHandler” and it will disconnect the connection. I’m not sure what I’m going to call it though. “EventHandlerManager”, “EventConnectionManager”? I know “Manager” isn’t very descriptive, but I can’t come up with anything better. It automates the management of event connections.

That allows us to still have the flexibility of using boost::functions, the actual event dispatch is handled by boost::signals, and as long as you use one of these connection manager objects (and dispose of it properly) the interface should be pretty clean for the user.

Let me know if you have comments, questions, or suggestions for that class name.

Edit 4/29/2009: We finished these changes a long time ago, so I’m removing old dead links.  We went with the name “EventConnectionManager” for the object that automatically hold event handler connections.  Since this post we have also abstracted the event hander registration and dispatching into a new class “EventDispatcher“.  The existing event system inherits from that, and the threaded subsystem class has a dispatcher as well.  The reason for that is so we can ensure thread-safe messages going to the new thread.  A threaded subsystem (e.g. “SoundSubSystem“) has all of it’s events from the main event system come through a single handler which takes care of the thread safety.  Then the message is placed in a queue for the thread to pick up and handle using it’s thead-specific dispatcher.  It’s a little complicated but made the threading for the sound system pretty easy.

Leave a Reply