cgul_event_router.h File Reference

Implements the router pattern. More...

#include "cgul_common.h"
#include "cgul_exception.h"
Include dependency graph for cgul_event_router.h:
This graph shows which files directly or indirectly include this file:

Typedefs

typedef typedefCGUL_BEGIN_C struct cgul_event_router * cgul_event_router_t
 
typedef struct cgul_event_router__listener * cgul_event_router__listener_t
 
typedef int(* cgul_event_router__callback_t) (const char *rendezvous, const void *event, const void *data)
 

Functions

CGUL_EXPORT cgul_event_router_t cgul_event_router__get_instance (cgul_exception_t *cex)
 
CGUL_EXPORT void cgul_event_router__delete (cgul_event_router_t er)
 
CGUL_EXPORT cgul_event_router__listener_t cgul_event_router__add_listener (cgul_exception_t *cex, cgul_event_router_t er, const char *rendezvous, cgul_event_router__callback_t cb, const void *data)
 
CGUL_EXPORT void cgul_event_router__remove_listener (cgul_exception_t *cex, cgul_event_router_t er, cgul_event_router__listener_t listener)
 
CGUL_EXPORT void cgul_event_router__send_event (cgul_exception_t *cex, cgul_event_router_t er, const char *rendezvous, const void *event)
 
CGUL_EXPORT void cgul_event_router__set_data (cgul_exception_t *cex, cgul_event_router_t er, cgul_event_router__listener_t listener, const void *data)
 
CGUL_EXPORT size_t cgul_event_router__get_listener_count (cgul_exception_t *cex, cgul_event_router_t er, const char *rendezvous)
 

Detailed Description

Experience shows that peer-to-peer communication between decoupled objects does not scale well. All but the smallest object-oriented programs must have some sort of centralized communications hub so that decoupled classes can come together to form a coherent program. The two patterns that commonly do this are the mediator and router patterns. This class implements the router pattern.

This particular implementation of the router pattern is specifically designed so that the router itself needs no knowledge of the events that are being passed. Thus, when you want to add new events or remove existing events, you can do so without having to edit or recompile this class. This works by the event sender and the event listener agreeing on a rendezvous string. When rendezvous strings match, an event sender will be able to broadcast to all objects listening for a particular event. Multiple listeners and multiple senders are allowed for any event. Events are received by listeners in the order in which the listeners were added.

If you have ever tried to design a peer-to-peer communication system for your objects, you will immediately recognize how much easier this is than trying to make sure every peer has a pointer to all the peers with which it communicates. The primary benefit is that your event listeners do not have to be passed a pointer to the event sender in order to register using a method like Java's Observable.addObserver() or .NET's Delegate. This means when you change your mind about which objects should send or receive events, you do not have to manually find every event sender or event listener involved in the change and alter the code accordingly which quickly becomes a maintenance nightmare.

On the other hand, with the approach provided by this class, your event senders and event listeners just pass in a string to the cgul_event_router. If you want to add or remove new event senders or listeners, all changes are local to the class to which you are adding or removing; you do not have to go traipsing through your code looking for peer objects.

Another advantage of this approach is that all objects are capable of sending and receiving any event, and you get this flexibility without either having to clutter your inheritance tree or build this functionality explicitly into each object.

The use of rendezvous strings makes maintenance much easier, but it places a burden on the programmer to make sure the strings are unique and that both the event sender and the event listener agree on what those strings are. If you want compiler support for detecting these errors, I suggest you resort to the Motif trick whereby you keep a central list of const strings that you assign to variables that have the same name as the string. If you accidentally reuse a string, the compiler will complain that its variable has already been defined. If you accidentally misspell the string at the point where it is used, the compiler will complain that it cannot resolve the symbol. This fixes most of the known problems that arise when using arbitrary strings as rendezvous points.

Note
The short story for how to use this class with Managed C++ .NET classes is that you need to use the .NET GCHandle class to convert your tracking handles to and from magic cookies that map to void*. For an example of this see the following file which is part of the cgul source distribution:
    tests/main_event_router_cxx_dot_net.cpp
Note
This class is not thread-safe by design. If you want to use this class from multiple threads, you must provide external synchronization.
Note
This class is reentrant by design. In practice, this means it is safe for a listener to remove itself (or to add and remove other listeners) in the same code that listens for events. It also means that event listeners can recursively send events.
Author
Paul Serice

Typedef Documentation

§ cgul_event_router_t

typedef typedefCGUL_BEGIN_C struct cgul_event_router* cgul_event_router_t

Opaque pointer to a cgul_event_router instance.

§ cgul_event_router__listener_t

typedef struct cgul_event_router__listener* cgul_event_router__listener_t

Opaque pointer a cgul_event_router__listener instance. An object of this type is passed back to the user by cgul_event_router__add_listener(). It can be used to remove a listener or change the data passed to a listener.

§ cgul_event_router__callback_t

typedef int(* cgul_event_router__callback_t) (const char *rendezvous, const void *event, const void *data)

In order to register to listen to events, you must implement a function that is of type. The function must return true after each call in order for event to be sent to the remaining listeners.

Parameters
[in]rendezvousrendezvous string associated with this event
[in]eventevent your function has been sent
[in]dataclient data that was passed in when the listener was added with cgul_event_router__add_listener().
Returns
whether to continue sending event to other listeners

Function Documentation

§ cgul_event_router__get_instance()

CGUL_EXPORT cgul_event_router_t cgul_event_router__get_instance ( cgul_exception_t cex)

Return the single instance of this class. Because cgul_event_router is a singleton, the caller is generally not responsible for cleaning up by calling cgul_event_router__delete(). If memory cannot be allocated, NULL is returned, and an exception is thrown.

Parameters
[in,out]cexc-style exception
Returns
single instance of this class.

Referenced by cgul_event_router_cxx::get_instance().

§ cgul_event_router__delete()

CGUL_EXPORT void cgul_event_router__delete ( cgul_event_router_t  er)

This method frees all internally allocated resources. In a typical program, it should only be called once (if at all) immediately before the program exists.

Parameters
[in]erevent router

Referenced by cgul_event_router_cxx::delete_instance().

§ cgul_event_router__add_listener()

CGUL_EXPORT cgul_event_router__listener_t cgul_event_router__add_listener ( cgul_exception_t cex,
cgul_event_router_t  er,
const char *  rendezvous,
cgul_event_router__callback_t  cb,
const void *  data 
)

This method adds cb to listen for the event specified by the rendezvous string rendezvous. A copy of rendezvous is made so the client does not have to keep rendezvous in scope.

Events will be sent to the listeners in the order in which the listeners were added.

The "all" rendezvous string is special; if you use it, your listener will receive all events without having to explicitly register for each one. Furthermore, the "all" event listeners will receive the events before the normal event listeners. This makes the output of a logger that is attached to the "all" event sane when your code recursively sends events.

The cb callback can be added more than once per rendezvous string. This makes it possible to use this class with C++ code when using a single private "static" method to forward the callback to a member method for more than one object of the C++ class. When using this tactic, each C++ object has be to registered individually, and this class makes it possible to do so.

cb should return true if it wants the event to be sent to all remaining listeners. If cb returns false, the event will not be sent to any function that was added after cb.

This class is reentrant so it is safe to recursively add or remove listeners or even send events from cb.

If an error occurs, NULL will be returned, and an exception will be thrown.

Parameters
[in,out]cexc-style exception
[in]erevent router
[in]rendezvousrendezvous string
[in]cbcallback function
[in]dataopaque client data to pass back to cb
Returns
listener

Referenced by cgul_event_router_cxx::add_listener().

§ cgul_event_router__remove_listener()

CGUL_EXPORT void cgul_event_router__remove_listener ( cgul_exception_t cex,
cgul_event_router_t  er,
cgul_event_router__listener_t  listener 
)

Remove listener. After calling this method, listener is invalid and should not be used again by the client.

This class is reentrant so it is safe to recursively add or remove listeners or even send events from the callback associated with listener.

Parameters
[in]cexc-style exception
[in]erevent router
[in]listenerlistener

Referenced by cgul_event_router_cxx::remove_listener().

§ cgul_event_router__send_event()

CGUL_EXPORT void cgul_event_router__send_event ( cgul_exception_t cex,
cgul_event_router_t  er,
const char *  rendezvous,
const void *  event 
)

Send event to all listeners registered for events associated with the rendezvous string. Events will be sent to the listeners in the order in which the listeners were added. This class is reentrant so it is safe to call this method from the callbacks associated with listeners thereby recursively sending events. If an error occurs, an exception will be thrown.

Parameters
[in,out]cexc-style exception
[in]erevent router
[in]rendezvousrendezvous string
[in]eventevent to send

Referenced by cgul_event_router_cxx::send_event().

§ cgul_event_router__set_data()

CGUL_EXPORT void cgul_event_router__set_data ( cgul_exception_t cex,
cgul_event_router_t  er,
cgul_event_router__listener_t  listener,
const void *  data 
)

This method sets the client data that is passed back to listener when it is sent an event.

Parameters
[in]cexc-style exception
[in]erevent router
[in]listenerlistener
[in]dataclient data

Referenced by cgul_event_router_cxx::set_data().

§ cgul_event_router__get_listener_count()

CGUL_EXPORT size_t cgul_event_router__get_listener_count ( cgul_exception_t cex,
cgul_event_router_t  er,
const char *  rendezvous 
)

Get the count of listeners currently registered for the event associated with the rendezvous string.

Parameters
[in]cexc-style exception
[in]erevent router
[in]rendezvousrendezvous string
Returns
listener count

Referenced by cgul_event_router_cxx::get_listener_count().