Event Dispatching & the GED Library

C/C++ Users Journal October, 2005

A generic library for event-driven development

By Bo Xu

Bo Xu is a software developer for Reuters and can be contacted at bo.xu.ca@gmail.com.

With event-driven programs, event dispatchers act as central control agents that poll for events from event sources, invoking event handlers as necessary. Generally, there are different types of event sources, each with its own mechanism for generating events:

In general, each application or application platform has its own way of dispatching events. While similar, these dispatchers aren't quite the same. Moreover, many existing event dispatchers are difficult (if not impossible) to extend at the event-source level. By design, Windows and X-Windows are UI oriented, making it awkward to plug in support of third-party messaging events. In many instances, a software-development organization keeps event-dispatching routines in a library that is shared by different projects across the organization. Supported event sources are then hard-coded in the library, making it impossible to add new event sources without modifying the library code. Finally, there appears to be a lack of generic event-dispatching mechanisms that might address some of these problems.

In this article, I present just such a generic event dispatcher (GED) library that:

The complete source code for the GED library is available at http://www.cuj.com/code/.

Top-Level Design

There are three kinds of objects: dispatchers, event sources, and events. The dispatcher class has the following functionality and characteristics:

How the dispatcher class interacts with event-source classes depends on whether the platform is multithreading. In nonmultithreading environments, the dispatcher must poll each event source so that event-source objects are visible to the dispatcher. In other words, an event-source object needs to register itself to the dispatcher. Nevertheless, with multithreading, it falls into a consumer-producer model. The dispatcher consumes fired events by calling event handlers regardless of their sources, while each event-source object produces (fires) events on its own thread. This makes event-source objects transparent to dispatchers. The design I implement in this article is based on a nonmultithreading environment, so the dispatcher maintains a queue for working with registered event-source objects polymorphously.

Listing 1 includes the classes EventDispatcher, EventSourceProxy, and EventProxy. The EventDispatcher class is a concrete Singleton class. The single object can only be accessed by calling the friend function theDispatcher() [1]. In the loop() method, the dispatcher calls the polling function of each registered event source, while in the pollEvent() method of an event source, the event-source object puts fired events (if any) to the dispatching queue. Afterwards, the dispatcher invokes the eventProcess() of each event in the dispatching queue in the order of priority.

The constructor of the EventSourceProxy class calls the eventSourceRegister() method of the dispatcher to register itself. This is a so-called "self registration" [2], which facilitates automatic runtime binding of an event source to the dispatcher.

The EventProxy class provides a set of common interfaces of an abstract event object. The eventProcess() method is the interface for the dispatcher. It has a timestamp data member and operator > for ordering event objects in the dispatcher's priority queue. An event object may self-destruct at the end of its lifetime. To enable this, the design only allows an event object to be created on the free storage (the heap) with the new operator. Consequently, I purposely put the virtual destructor in the protected access mode [1]. The member data, m_restore, indicates whether the object is self-destructive or not.

Concrete Event-Source Class And Concrete Event Classes

Given a concrete event source and events, say timer, should the timer event-source class be derived from EventSourceProxy and the timer event class from EventProxy directly? Before answering, note that there is something missing. The proxy classes are mainly interfaces to the dispatcher. They do not depict the relationship between event sources and events. For example, a timer event source can only fire timer events. On the other hand, a timer event can only be triggered by a timer event source. It is more challenging in C++ to specify relationships between objects than to describe the object itself. My method is to add another layer of abstraction to the class hierarchy. In Listing 2, I introduce two class templates: EventSource<T_EVENT> and Event<T_SRC> (for readability, I change in Listing 2 the formal name <T> to <T_EVENT> or <T_SRC> in its proper context). Any concrete event source class is to be derived from EventSource<T_EVENT> and any concrete event class from Event<T_SRC>.

For class template EventSource<T_EVENT>:

With the class template Event<T_SRC>, any specification of Event<T_SRC> is a derivation of EventProxy that has an interface with the dispatcher and its own event-source class. Also, the template parameter T_SRC has to be a concrete event-source class derived from EventSource<T_EVENT>. The static member, theEventSource, of type T_SRC, binds all objects of a concrete event class to the event source T_SRC.

It seems that there is a looping reference between a concrete event class and the corresponding concrete event-source class. For example, for the timer event, you have the class TimeEvent and class TimeEventSource. TimeEvent is derived from Event<TimeEventSource>, and TimeEventSource is based on EventSource<TimeEvent>. They reference each other in their declarations! Is this a problem? Actually, it isn't. Using forward declaration and observing that in class TimeEventSource only pointers of TimeEvent are directly referred, the compiler yields what is intended.

The following classes complete the GED library:

To declare and define concrete event classes and event source classes, first specify the class template Event<T_SRC> with the concrete event-source class and specify the class template EventSrc<T_EVENT> with the concrete event class. Then, derive the concrete event class from the specification of Event<T_SRC>, and the concrete event-source class from the specification of EventSrc<T_EVENT>, respectively.

A Timer Event Example

The timer event illustrates how a concrete event class, along with its source class, is derived and implemented (Listing 3). Each object has its own callback routine, which is called by the method eventProcess(). This callback routine seems to be kind of common and it would be defined in some base class. However, it has to be specific to the concrete class because the callback routine always has its own particular signature (type of an argument, number of arguments) appropriate to the concrete class.

In the method TimeEvent::activateEvent(), the statement theEventSource.addEvent(this) is mandatory for the purpose of putting this event object onto the polling queue maintained by its event-source class, TimeEventSrc. However, this common behavior cannot be specified in a base class, for the polling queue contains pointers of objects of type TimeEvent, not their base type. By doing this, you avoid a possible downcast.

Another common behavior pattern is found in the method eventProcess(). As shown in the TimerEvent class, regardless of what concrete event class it is, this method always does the same routine—check if there is callback specified. If so, call it; check if this object is to be restored. If so, restore it; otherwise, do self deletion. Again, since the callback is only known by the concrete event class, the implementation of eventProcess() cannot be put in a base class.

A Windows Event Example

In a Windows application, dispatching events via the GED library turns out to be just an extension. The classes WinEvent and WinEventSource (Listing 4) are derived from Event<WinEventSource> and EventSrc<WinEvent>, respectively. As the design is on a single-threaded platform, the Windows PeekMessage() API call is used instead of another commonly used API call, GetMessage(). This is because the former is polling for Windows events while the latter is block-waiting. In eventProcess(), a Windows event is relayed to the Windows standard event-dispatching API calls, TranslateMessage() and DispatchMessage().

It is worth noting that:

The GED library makes Windows standard event dispatching a secondary level of dispatching, so it is necessary to make some changes in the main code. Normally, the following code is found in the main function (namely, WinMain):

 ... //initialization
while (GetMessage(&msg, NULL, 0, 0)) 
{	if (!TranslateAccelerator(msg.hwnd,
		       hAccelTable, &msg)) 
	{	TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

However, this code is substituted with:

 ... //initialization
WinEvent *wEvent = new WinEvent
		       (hAccelTable);
theDispatcher().Loop();
delete wEvent;

With this change, Windows applications can support other event sources (such as third-party messaging events). The companion source code (http://www.cuj.com/code/) shows how the timer event source described here can be added to a typical Windows application.

Conclusion

To implement a concrete event class and its companion event source class with the generic event dispatcher (GED) library, the process is as follows:

The main function in Listing 5 shows the usage of the timer event class. Note that the object timeTerm is self-destructive and so there is no need to delete it explicitly.

In the accompanying source code, the implementation of the signal event class and its event-source class is available for interested readers. The source code has been built and tested on Windows XP with Visual Studio 7 (VC++ 7) and on Solaris with Sun CC 6.

References

  1. [1] Meyers, Scott. Effective C++, Addison-Wesley, 1996.
  2. [2] Beveridge, Jim. "Self-Registering Objects in C++," Dr. Dobb's Journal, August 1998.
CUJ