Reputation: 2224
Since you love to know what it's for, here a little background.
We intensively use our own event system. Typical implementation :
Listener
register to some Event
EventManager
dispatch events in the main loopListener
handles given Event
and do some ...Now, Listener
have to identify Event
types to potentially cast it into something useful. Event
class has a int
member called id
for that purpose. To guarantee the uniqueness of ids they are given by an enum
witch need to be in a central and accessible place so that anybody can add an id
before creating a new Event
type ... You get it.
I once tried to reduce the relative complexity of this system to allow users (programmers) to create new Event
in a simpler way without losing the safety of the uniqueness.
So I decided to go with something like this:
EventBase.h
class EventBase
{
public:
virtual int getId() const = 0;
static int registerEvent() { return ++numberOfEvent_;}
private:
static int numberOfEvent_;
};
EventBase.cpp
int EventBase::numberOfChild_ = 0;
Event.h
template<class T>
class Event : public EventBase
{
public:
virtual int getId() const { return id_;}
static const int id_;
};
template<class T> const int Event<T>::id_ = EventBase::registerEvent();
Event_1.h
class Event_1 : public Event<Event_1> {};
Event_2.h
class Event_2 : public Event<Event_2> {};
So basically any new Event
type have it's own static const id depending of the number of Event
type defined, so far so good we don't have to mess with the enum
... My only problem is that we need to properly define the template parameter, otherwise many Event
can share the same id.
So my question, is there a way to 'hide' the template parameter ?
To end with some like this :
class Event_1 : public Event {};
Upvotes: 1
Views: 232
Reputation: 3727
I would recommend using a virtual function on each event that does whatever the event is. If you really need the processing of events to happen in a central place (if so, are you sure you need that?) then you can use the visitor pattern so that each event has a virtual function that calls an event-specific method on your central event processor. That way you can detect the type of the Event without any casts or type ids.
To answer your actual question, if you want to stay with type ids, and you don't care about efficiency, then you can do this:
class Event {
public:
int getId() {
std::map<std::string, int>::iterator it = types.find(typeid(*this).name());
if (it == types.end()) {
int newId = types.size();
return types[typeid(*this).name()] = newId;
} else
return it->second;
}
private:
static std::map<std::string, int> types;
};
This probably doesn't compile as I haven't tried it, but you get the idea. You can also use the type_info.before method to avoid the use of strings, but that still leaves the map lookup. If you use a hash table then this might not be all that slow, but it still won't be as fast as the solution you have already got.
It might also work more quickly to use the address of the const char* returned by type_info::name directly as an id, but I'm not sure that there is a guarantee that it always returns the same address for the same type, though it is reasonable to hope that it will do so. That would be pretty fast in that case. I don't really recommend this solution.
I think your concern with your own stated solution is that two distinct event classes might derive from the same Event e.g. due to a cut-and-paste mistake. To catch that, you could check that Event::getId never gets called on objects with different dynamic types like this:
#ifdef DEBUG
static type_info myType = typeid(*this);
assert(myType == typeid(*this));
#endif
Note that this will assert if you sub-class an event with an id but don't want to give the new child class a separate type id.
Upvotes: 0
Reputation: 385144
C++ already has a type system.
Make each Event
have a different, polymorphic, derived type.
Your Listener
s should register for an event like so (presuming singleton EventManager
for the sake of argument):
EventManager::registerCallback<cattle_prod_event>(bind(&Me::onCattleProd, this, _1));
And implement a member function:
void Me::onCattleProd(cattle_prod_event const* msg) {}
Now you know exactly what the message type it is at the point of receipt.
The only downside here is that storing the callbacks becomes a little more involved. (But, build your system from the API down!)
Upvotes: 3