Gaffer Docs

EventFactory

The creation of events can be nicely abstracted with the Factory Pattern. Since we'll be serializing and deserializing events, an EventFactory can serve us well in these tasks. For a great treatment of Factories in C++, I highly recommend Modern C++ Design, which devotes an entire chapter to the subject.

EventFactory Class

typedef Event* (*CreateEventCallback_t)();

class EventFactory
{
public:
    explicit EventFactory();
    ~EventFactory();

    // registers the event create callback
    bool registerEvent(EventType_t type, CreateEventCallback_t callback);
    
    bool isEventRegistered(EventType_t type);

    EventType_t typeFromName(const char * name);

    // execute create func that has been registered in the factory for
    // specified EventType_t
    Event* createEvent(EventType_t type);

private:
    void registerEventName(const char * name, EventType_t type);

    typedef std::vector<CreateEventCallback_t> Callbacks_t;
    typedef std::vector<Callbacks_t*> CallbacksVector_t;

    Callbacks_t * findCallbacks(EventType_t type);

    // Declare a map that will facilitate finding event types for a
    // given event name.  This will enable defining GUI events in text
    // files and identifying which EventType_t those names correspond
    // to at runtime.
    typedef std::map<std::string, EventType_t> EventNameMap_t;
    EventNameMap_t eventNameMap_;

    // our callbacks to create events when deserializing
    CallbacksVector_t callbacksVector_;
};

To register an event you must pass in a callback that returns an Event*. Typically this is a static method provided on the event class.

registerEvent Method

bool EventFactory::registerEvent(EventType_t type, CreateEventCallback_t callback)
{
    // Extract event id from the event type (low 24 bits)
    boost::uint32_t eventid = eventID(type);

    // Verify no events have broken our range rules
    if (eventid > MAX_EVENT_ID)
        throw GA_EXCEPTION_2("During registration, event type out of accepted range (0-%d): %x",
                             MAX_EVENT_ID, type);

    Callbacks_t * callbacks = findCallbacks(type);

    (*callbacks)[eventid] = callback;

    return true;
}

An Event Type is a 32 bit unsigned value. The high order byte is the client ID. Gaffer reserves client ID 0, but any other value is available. If there are a lot of Gaffer clients in the future we'll have to come up with some kind of organization.

The low order 24 bits are the event id. Only values less than 1024 are currently supported. The findCallbacks method simply returns the table of callbacks for the client ID specified by the Event Type.

EventFactory::Callbacks_t * EventFactory::findCallbacks(EventType_t type)
{
    // Extract the client id from the event type (high 8 bits)
    boost::uint32_t clientid = clientID(type);

    Callbacks_t* callbacks = callbacksVector_[clientid];

    if (callbacks == 0)
    {
        callbacks = new Callbacks_t(MAX_EVENT_ID+1, 0L);
        callbacksVector_[clientid] = callbacks;
    }
    
    return callbacks;
}

Once we have the callbacks lookup table, we can store the callback for this event id.

createEvent Method

Event* EventFactory::createEvent(EventType_t type)
{
    // Extract event id from the event type (low 24 bits)
    boost::uint32_t eventid = eventID(type);

    // Verify it's a valid event type
    if (eventid > MAX_EVENT_ID)
        throw GA_EXCEPTION_2("During deserialization, event type out of accepted range (0-%d): %x",
                             MAX_EVENT_ID, type);

    Callbacks_t * callbacks = findCallbacks(type);

    // Verify type has been registered
    if ((*callbacks)[eventid] == 0)
        throw GA_EXCEPTION_1("Event type was not registered: %x", type);

    // call create callback associated for event type
    return (*callbacks)[eventid]();

}

STANDARD_REGISTRATION Macro

If your event is a StandardEvent or one of the DataEventX varieties (all events should you create should be), you can use the STANDARD_REGISTRATION macro to simplify registration.

#define STANDARD_REGISTRATION(T) \
    namespace { \
    bool registered_ ## T = EventFactoryS::inst().registerEvent(T::TYPE, \
                                                                T::createEvent); \
    }

}

Usage is very simple, just pass in your event class.

STANDARD_REGISTRATION(LoadActor);

With this macro, you can place calls of this kind in one of your .cpp files and your events will get registered at program load time. You won't have to register them during an initializaiton method or constructor. This is preferrable since it's simpler and guarantees all events get registered as soon as possible. Any EventType conflicts that may exist in the system will throw an exception right away at program start time.