1

I'm trying to write a really simple event or message class in C++. I want the event to hold the time of occurrence and some data which are specific to the event type. What I have at the moment is the following


class EventData{
public:
    virtual ~EventData(void) = 0;
};

struct Event{
    Event(EventType type, Time time, EventData *data = nullptr):
        type_(type), time_(time), data_(data)
    {}
    ~Event(void){
        if(data_) delete data_;
    }
    //Disable copying, event memory will be managed by EventManager
    Event(const Event& other) = delete;

    const EventType  type_;
    const Time       time_;
    const EventData *data_;
};

In my main loop, I have something like this,


bool running = true;
while(running){
    const Event* nextEvent = evtManager.getNextEvent();
    switch(nextEvent.type_){
    case EVT_A:
        const EventAData* data = static_cast&ltEventAData*&gt(nextEvent.data_);
        //do stuff
        break;
    }
    ...
    case EVT_END:
        running = false;
        break;
    }
}

The question then is if there is a more efficient way of doing this, i.e. with templates. the other problem is that someone could accidentally give the wrong EventType, EventData pair, in which case the static_cast will fail.

I should note that I want this Event class to be as fast as possible, especially the access to the time_ member variable.

Grieverheart
  • 510
  • 5
  • 16
  • http://stackoverflow.com/questions/1024648/retrieving-a-c-class-name-programatically may help – Mike Jan 31 '14 at 17:12

2 Answers2

1
  1. There is no need to check whether the pointer is null before deleting it.
  2. You are trying to perform type-erasing, and then perform different tasks depending on the type you just erased. It's inefficient. Look into boost::variant instead, it seems that it's exactly what you need.
  3. If you insist on using this method, you should use typeid to discriminate between types.
Community
  • 1
  • 1
tsuki
  • 907
  • 4
  • 17
  • 1. Thanks for letting me know. For some reason I always though it would raise a runtime error. 2. That's true, it took me some time to understand how boost::variant works, but it seems to perform what I'm doing but in a more efficient and compact way. Your suggestion, thus, is indeed exactly what I'm asking. One drawback is that you have to know all event types beforehand, i.e. if someone wants to add a new event type, he'd have to modify the core source. 3. Do you mean type_info? I don't see why that would be better than using a hashed string for example. – Grieverheart Jan 31 '14 at 20:11
  • I think that your approach with "virtual data class" is fundamentally incompatible with runtime event type creation. You are static-casting data using type_ as a discriminant and you want to preserve the ability to create new EventTypes. You seem to miss the fact, that your EventData is created statically and even if you were able to create new types on the fly, the same isn't applicable to your data holder. On the type_info note - RTTI of the data class is applied by the compiler itself and it will not make an error. You shouldn't be worried about the speed of it, unless you have measured it. – tsuki Feb 01 '14 at 05:32
  • Sorry, I never meant "runtime" event type creation. boost::variant<...> data_ just requires hard coding all event types. If someone wants to define his own data type, he'd have to go and insert it in the variant, which also means you have to recompile everything that uses the variant. Having the type as a hashed string, means you can define a data type outside this compilation unit. – Grieverheart Feb 01 '14 at 17:32
  • Right, I misunderstood you. On the other hand - if you use a typedef on the variant and use it throughout your application, changing allowed types is way safer than using hashed strings and switch casting. Boost::variant uses a visitor and requires an overloaded operator() for each variant type, so adding a new type will cause you to update ALL of your visitors, saving you from memorizing every spot where you do switch-do casting. – tsuki Feb 01 '14 at 17:50
1

Instead of using templates, you could use inheritance.

Event can be an abstract base class with one member variable, _time. Accessing the _time member variable will be fast with a simple accessor method (possibly inlined) or by making _time a public member variable. You’re almost always better off using an accessor method. The access will be plenty fast, and the accessor will allow you the flexibility to change your internal representation of the time at some later point if the need arises.

The EventType enum in your example really describes all the possible subclasses of Event. If you were to create a concrete subclass of the abstract base class Event for every entry in EventType, you really wouldn’t need the EventType enum. The subclass identifies the event type. The constructors for all the subclasses could be unique, each one taking different parameters. Each subclass could provide accessors for its member variables. These additional constructor parameters and accessors could replace the EventData class.

Finally, you have two alternatives for your main loop. First, you might treat the processing currently done in each case statement polymorphically. The Event base class could have a pure virtual function called “processEvent()” which each subclass could override. If that is possible, you could replace the switch statement with a simple function call:

nextEvent->processEvent();

Second, if that isn’t feasible, you could use RTTI to downcast the nextEvent variable from an Event pointer to a pointer of the appropriate subclass. You could then call the subclass-specific accessors to perform processing that’s similar to what you’re currently doing.

  • That's what I use for my game engine; I have an abstract class which has a method getType, which returns the type of the event as a hashed string. RTTI, I think, is too slow, that's why I store the type so I can static_cast instead. Having the processEvent logic inside the event itself is a bit cumbersome as I would have to pass a lot of state to the events. In the end, for the main loop I will just use signals. – Grieverheart Jan 31 '14 at 20:02