Class methods listed in the next paragraphs are prepended with a little ball. The color of this ball indicates whether or not you are expected to override that method in a derived class:
Must be overridden. | |
May be overridden. | |
Should not be overridden. | |
Must never be overridden. |
The library defines five classes, in libcw/events.h:
class busy_interface_ct; template<class EVENTTYPE> class event_request_base_tct; template<class EVENTREQUESTBASE> class event_request_queue_tct; template<class EVENTREQUESTBASE, class EVENTREQUESTQUEUE = event_request_queue_tct<EVENTREQUESTBASE> > class event_server_tct; template<class EVENTREQUESTBASE, class EVENTREQUESTQUEUE = event_request_queue_tct<EVENTREQUESTBASE> > class event_data_server_tct;
There are in total seven nested (template) classes, three defined by busy_interface_ct, two by event_server_tct and two by event_data_server_tct. However, these nested classes are of no concern to the user and will not be mentioned any further.
Either event_server_tct or event_data_server_tct is the actual event server. They are identical except that the latter supports event request data. All other characteristics that distinguish different event servers are defined in the two template parameter classes that are passed to it: EVENTREQUESTBASE and EVENTREQUESTQUEUE.
EVENTREQUESTQUEUE determines the characteristics of the request queue: the type of queue and how the requests are stored and retrieved. class event_request_queue_tct is a default class which uses a deque to store the event requests and which handles all requests (in the order in which they were requested) every time its trigger() method is called. Most custom event servers will need to write a different EVENTREQUESTQUEUE class.
EVENTREQUESTBASE must be derived from event_request_base_tct. It determines the characteristics of the requests themselves. In particular, it defines data that needs to be stored in the request object itself (which is passed with the requests). This data can for instance be used use by the trigger call to determine which requests need handling.
This class represents a busy interface. It is only a default class however and the user may define their own busy interface, or use this one as a base class.
An event client can have zero or more busy interface objects, one of which it can pass to the event server when requesting events from it.
A busy interface class must define the following public methods:
bool is_busy(void) const |
|
Return true when the interface is busy, false otherwise. | |
|
|
void set_busy(void) |
|
Increment the busy depth counter: Marks the interface busy. | |
|
|
void unset_busy(void) |
|
Decrement the busy depth counter: The object becomes not busy when the unset_busy method is called as often as the set_busy method was called before. | |
|
|
template<class EVENTREQUESTBASE> void queue(EVENTREQUESTBASE* event_request, typename EVENTREQUESTBASE::event_type_ct const& event_type) |
|
Queue the event until the object becomes not busy, then call event_request->rehandle(event_type). This method should also take care to call the pair event_request->add_reference() and event_request->release(), see below. |
When an event occurs for a client that provided a busy interface, then the is_busy() method is called; when this method returns false then first the set_busy() method is called, then the call back function of the client is called and finally the unset_busy() method is called. If is_busy() returned true however, then the queue() method is called.
This class is only needed when you design a new EventServer class. It is an abstract class, used as base class for event request objects, and needs to be passed to the event server as the EVENTREQUESTBASE template parameter.
You will need to derive your own EventRequestBase class from this class when you want to use specific event request data, see below.
Class event_request_base_tct defines the following public methods:
void add_reference(void) |
|
Increments the reference counter. | |
|
|
void release(void) |
|
Decrements the reference counter counter: The object is deleted when the release method is called once more often than the add_reference method was called. |
Usually, add_reference() and release() will be called as a pair as part of the queuing mechanism of a busy interface class. The method release() is always called after a request is handle()-ed.
Further more, the following type is defined:
event_type_ct |
|
Defined as typedef EVENTTYPE event_type_ct; where EVENTTYPE is the template parameter defining the event type of this server. |
virtual void handle(EVENTTYPE const& event_type) = 0 |
|
Passes event_type to the client by calling its call back function, or queues the event when the client is busy. This method takes care of calling set_busy() / unset_busy(). | |
|
|
virtual void rehandle(EVENTTYPE const& event_type) = 0 |
|
Passes event_type to the client by calling its call back function. This method is only called when the client just became not busy but still is marked as busy! So no extra calls to set_busy() and unset_busy() are performed. |
You should not override or call these two functions.
This is the default EVENTREQUESTQUEUE template parameter of event_server_tct and event_data_server_tct.  It stores requests in a STL deque. All requests are handled, in the same order as that they were added, when the trigger() method is called.
Click here for information on what a custom EVENTREQUESTQUEUE must provide.
Class event_request_queue_tct has one public method:
void trigger(typename EVENTREQUESTBASE::event_type_ct const& event_type); |
|
Calls handle(event_type) for all queued request objects in the same order as the requests were added. |
This class defines an interface for requesting events without request data, by means of four overloaded operator() template methods:
template<class EVENTCLIENT> void operator()(EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&)) |
|
Request to call back function ec.em(event_type). Does not use a busy interface or a cookie. | |
|
|
template<class EVENTCLIENT, class BUSYINTERFACE> void operator()(EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&), BUSYINTERFACE& bi) |
|
Request to call back function ec.em(event_type). Use busy interface bi. Does not use a cookie. | |
|
|
template<class EVENTCLIENT, class REQUESTCOOKIE> void operator()(EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&, REQUESTCOOKIE), REQUESTCOOKIE cookie) |
|
Request to call back function ec.em(event_type, cookie). Does not use a busy interface. | |
|
|
template<class EVENTCLIENT, class REQUESTCOOKIE, class BUSYINTERFACE> void operator()(EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&, REQUESTCOOKIE), REQUESTCOOKIE cookie, BUSYINTERFACE& bi) |
|
Request to call back function ec.em(event_type, cookie). Use busy interface bi. |
EVENTCLIENT, REQUESTCOOKIE and BUSYINTERFACE can be any arbitrary class type, although BUSYINTERFACE must provide the necessary methods of course. In general, the provided busy_interface_ct will be sufficient.
The REQUESTCOOKIE object is copied into the request object and returned with em(), the call back function, when the request is being handled.
This class defines an interface for requesting events with request data, by means of four overloaded operator() template methods. It is completely identical with class event_server_tct above, except that the first parameter of the operator() methods is of type EVENTREQUESTBASE::event_request_data_ct and will be passed as initialization data to the constructor of EVENTREQUESTBASE.
template<class EVENTCLIENT> void operator()(typename EVENTREQUESTBASE::event_request_data_ct rd, EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&)); |
|
Pass request data rd with request to call back function ec.em(event_type). Does not use a busy interface or a cookie. | |
|
|
template<class EVENTCLIENT, class BUSYINTERFACE> void operator()(typename EVENTREQUESTBASE::event_request_data_ct rd, EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&), BUSYINTERFACE& bi); |
|
Pass request data rd with request to call back function ec.em(event_type). Use busy interface bi. Does not use a cookie. | |
|
|
template<class EVENTCLIENT, class REQUESTCOOKIE> void operator()(typename EVENTREQUESTBASE::event_request_data_ct rd, EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&, REQUESTCOOKIE), REQUESTCOOKIE cookie); |
|
Pass request data rd with request to call back function ec.em(event_type, cookie). Does not use a busy interface. | |
|
|
template<class EVENTCLIENT, class REQUESTCOOKIE, class BUSYINTERFACE> void operator()(typename EVENTREQUESTBASE::event_request_data_ct rd, EVENTCLIENT& ec, void (EVENTCLIENT::*em)(event_type_ct const&, REQUESTCOOKIE), REQUESTCOOKIE cookie, BUSYINTERFACE& bi) |
|
Pass request data rd with request to call back function ec.em(event_type, cookie). Use busy interface bi. |
Note the difference between event_request_data_ct and REQUESTCOOKIE: The first is a fixed type determined by the developer of the event server. REQUESTCOOKIE is arbitrary and can differ from request to request. The Cookie object is returned to the caller, while the request data is not; and vica versa, the Cookie object is not available to the event server code while the request data can be used in (for example) the trigger method.
Defining a new Event Server involves the definition of, at most, five classes:
class EventData class EventType class EventRequestBase class RequestQueue class EventServer
Libcw makes no distinction between EventData and EventType: Only EventType is used. EventData merely contains data and operators that work on that data, while EventType must be an unique type, devoted to this specific EventServer. EventType can be derived from EventData allowing to use EventData also for other purposes (like other event servers).
This class is specific for this EventServer, hence the use of the word type. However, you will mostly consider it to be data, and that is correct because it should be derived from EventData. EventType is the type that is passed from the event server to the event client, as parameter of the call back function. While the type can be any arbitrary object, it should be noted that it is passed as a const reference. This means that you can't change it until you copied it. It might be obvious why a reference is passed, and not a copy, while that is faster. The reason it has to be const is that the same object is passed to every client that requested the event.
For every request made by a client, a request object needs to be stored in a queue. The created objects are of different types however: Different clients, different call back functions, different busy interfaces if any and different cookies if any. Therefore we can only store a pointer in the queue: A pointer to the base class of the request object. EventRequestBase is this base class, allowing you to control the objects available in the request queue.
The EventRequestBase must be derived from event_request_base_tct.
Request data
It is possible to add extra attributes to the EventRequestBase: You can add one object which will be initialized with data provided by the client, when the client issues its request. In order to do so, you need to define the type event_request_data_ct inside your EventRequestBase class and provide a constructor that takes that type as single parameter.
Example:
class MyEventType { /* ... */ }; class MyEventRequestBase: public event_request_base_tct<MyEventType> { typedef struct timeval event_request_data_ct; private: struct timeval t; public: MyEventRequestBase(struct timeval a) : t(a) { } };
In order to let the event server create request objects with this constructor, one must use event_data_server_tct. Class event_server_tct will call the default constructor of EventRequestBase.
This object is a base class for the event server. It must store requests and call the handle() method of the requests when they are triggered (optionally deleting requests after that by calling their release() method).
When the object is destructed, it must call the release() method of all requests that are still stored.
An EventRequestQueue class must define one protected method:
void add_request(EventRequestBase*); |
|
Store the request pointer. |
Example:
class MyEventRequestQueue { typedef priority_queue<MyEventRequestBase*, list<MyEventRequestBase*>, ptr_greater> event_requests_ct; private: event_requests_ct event_requests; ~MyEventRequestQueue(); public: struct again* it_happened(int hard); /* (arbitrary) trigger method */ protected: void add_request(MyEventRequestBase* er) { event_requests.push(er); } }; MyEventRequestQueue::~MyEventRequestQueue() { while (!event_requests.empty()) { event_requests.top()->release(); event_requests.pop(); } }
Here ptr_greater is a function object that sorts the priority queue.
For a working example of an event server that uses a priority queue, see libcw/timing.h.
EventServer is basically just a typedef. Depending on whether or not you use request data, one defines this type as:
typedef event_server_tct<EventRequestBase, EventRequestQueue> EventServer;
or as
typedef event_data_server_tct<EventRequestBase, EventRequestQueue> EventServer;