2

I am planning an event driven game engine. The basic idea is that instead of having everything talking to everything, everything would talk to the event system, which would relay the messages to their recipients, without coupling the recipients to the notifiers or the other way around.

  • Objects register themselves to the event system. A notification ID and a pointer to a callback function are passed as parameters of each registration command.
  • Objects add notifications to the event system. The ID of the notification is passed as a parameter of each notification. The notifications are added to a queue holding all pending notifications.
  • Additionally, the event system supports scheduled notifications. The notification ID and the future time of execution are passed as parameters of each notification. The scheduled notifications are then stored in a data structure ("schedule") holding the scheduled notifications in order of the future time of execution.
  • An invoker object commands the event system to process all queued notifications. The event system fetches the notifications in order, and calls the callback function of each object that has registered itself with the same ID as the current notification.
  • An invoker object commands the event system to process one scheduled notification. The oldest notification is fetched from the schedule, and the callbacks of all objects registered with the same notification ID are called.

.

class Registration
{
public:
    void callback(void){ callback_(); }
    void setCallback((*callback)(void));
    void addToEventSystem(int ID, EventSystem &eventSystem);
private:
    void (*callback_)(void);
};

class EventSystem
{
public:
    void register(int ID, Registration* registration);
    void unRegister(int ID, Registration* registration);
    void addNotificationToQueue(int ID);
    void addNotificationToSchedule(int ID, int notificationTime);
    void processQueuedNotifications(void);
    void processNextScheduled(void);
    int getCurrentTime(void);
private:
    //placeholder types
    <list> notificationQueue;
    <binaryheap> notificationSchedule;
};

//------------Use:------------------
class ReceiverObject
{
public:
    void doStuff(void);
    void initialize(void){
        keyPressRegistration.setCallback(doStuff);
        //multiple registrations with different ID:s to same eventsystem possible
        keyPressRegistration.addToEventSystem(1234,eventSystem);
        keyPressRegistration.addToEventSystem(42,eventSystem);};
private:
    Registration keyPressRegistration;
};

int main()
{
    ReceiverObject receiverObject;
    EventSystem eventSystem;
    receiverObject.initialize();
    eventSystem.addNotificationToQueue(1234);
    eventSystem.processQueuedNotifications();
}

However I am not entirely satisfied with this solution, mostly because the system does not allow easy passing of parameters to recipients, and I am skeptical about callbacks to member functions, is it good design practice? What about the method/class/variable names I have come up with? Constructive criticism, guidance and alternative approaches to the problem are welcome.

2
  • 2
    How about passing a std::function instead of a plain function pointer for your callbacks? Commented Jul 30, 2013 at 12:09
  • Indeed those look promising. I just have no experience nor knowledge about c++11, but I'll give the new standard a look Commented Jul 30, 2013 at 12:25

3 Answers 3

6

It is not strictly related to your question, but speaking about design, I'd avoid using a global notification system 'for everything' as I've seen bad consequences in the past. You'll just tend to use a heavy event system in places where one object would just call some method on another object.

Specialised templatatised systems work better, i.e. the systems that allow you to control objects' lifespan and that are designed to handle events of particular types and known parameters in them.

Any case, you'll find it hard to solve problems such as pending events wating for delivery to already-killed recipients.

Sign up to request clarification or add additional context in comments.

Comments

1

I personally would have a interface class of EventHandler, that you can register, which will have a virtual action function (and perhaps a function for the event system to tell the class that it's being unregistered). This completely avoids callbacks, and the actual implementation of the EventHandler will have the possibility of holding other data (or references to other data).

6 Comments

Well that would make more sense than my current callback idea, but how about parameter/data transfer? The concrete implementation of EventHandler by default has to know the receiver class, which is OK, but if data is to be transmitted, it also has to know the source of the data (likely the class that added the event to EventSystem in the first place), which would couple them together.
Not sure what you mean - in my thoughts, the receiver class is an instance of EventHandler.
What I thought you mean in your original answer is that Registration has been made an abstract base class named EventHandler, and the callback system in Registration/EventHandler has been replaced with a virtual function used by derived classes that implement the desired response, like calling ReceiverObject->doStuff(). My original intention was that the receiver object owns an istance of Registration that handles the interaction between the receiver and the event system, so I thought from your answer that the receiver object would likewise own an instance of EventHandler.
No, make the RecieverObject a derived type of EventHandler, and store a pointer or reference to that in your list of noitfications.
Ok, but why? If every object that has the ability to receive events would have to be derived from EventHandler, almost the whole game would be derived from it.
|
1

For the parameters issue - You can define base class ParamList whose derivates are templated:

class ParamList
{
     const ID_Type& GetParamsType();
     ...
};
template <typename T>
class Params: public ParamList
{
     ...
}

now callbacks will be of type: void (*Callback)(ParamList&); for safety usage you can add to class Registration const ID_Type& GetExpectedParamsType(); and use it before calling the call back

sames go for notifications: void addNotificationToQueue(int ID, ParamList& param); void addNotificationToSchedule(int ID, ParamList& param,int notificationTime);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.