Implements the router pattern. More...
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) |
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.
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
typedef typedefCGUL_BEGIN_C struct cgul_event_router* cgul_event_router_t |
Opaque pointer to a cgul_event_router
instance.
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.
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.
[in] | rendezvous | rendezvous string associated with this event |
[in] | event | event your function has been sent |
[in] | data | client data that was passed in when the listener was added with cgul_event_router__add_listener() . |
event
to other listeners 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.
[in,out] | cex | c-style exception |
Referenced by cgul_event_router_cxx::get_instance().
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.
[in] | er | event router |
Referenced by cgul_event_router_cxx::delete_instance().
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.
[in,out] | cex | c-style exception |
[in] | er | event router |
[in] | rendezvous | rendezvous string |
[in] | cb | callback function |
[in] | data | opaque client data to pass back to cb |
Referenced by cgul_event_router_cxx::add_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
.
[in] | cex | c-style exception |
[in] | er | event router |
[in] | listener | listener |
Referenced by cgul_event_router_cxx::remove_listener().
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.
[in,out] | cex | c-style exception |
[in] | er | event router |
[in] | rendezvous | rendezvous string |
[in] | event | event to send |
Referenced by cgul_event_router_cxx::send_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 | ||
) |
This method sets the client data that is passed back to listener
when it is sent an event.
[in] | cex | c-style exception |
[in] | er | event router |
[in] | listener | listener |
[in] | data | client data |
Referenced by cgul_event_router_cxx::set_data().
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.
[in] | cex | c-style exception |
[in] | er | event router |
[in] | rendezvous | rendezvous string |
Referenced by cgul_event_router_cxx::get_listener_count().