On 08/20/2011 09:19 AM, Harry van Haaren wrote:
My (hacky?) solution: Create a class, call it
"Event". Runtime now looks
like so:
1. Create a EventType enum, set the details
2. Write those "Events" into the ringbuffer
3. Switch based on EventType, and handle the event.
While not terribly ugly, that Event class starts to get bigger &
nastier, so I concidered sub-classing it... but I'm not sure this is
going in the right direction.
IMHO, You're more or less going in the right direction. There's two
main ways to structure your EventType.. and they are both essentially
using inheritance. We'll call them "The C way" and "The C++ Way".
They
are more or less equivalent.
Consider the following to be pseudocode. I didn't test it.
THE C WAY
=========
struct base_event_t_ {
uint16_t type;
uint16_t size;
};
typedef struct base_event_t_ base_event_t;
#define EVENT_ONE 1
struct event_one_t_ {
uint16_t type;
uint16_t size; /* must be EVENT_ONE */
/* extra event-specific data */
};
typedef struct event_one_t_ event_one_t;
#define EVENT_TWO 2
struct event_two_t_ {
uint16_t type;
uint16_t size; /* must be EVENT_TWO */
/* extra event-specific data */
};
typedef struct event_one_t_;
/* ... */
void my_function(event_t *ev)
{
switch(ev->type) {
case EVENT_ONE:
handle_event_one( (event_one_t*) ev );
break;
case EVENT_TWO:
handle_event_two( (event_two_t*) ev );
break;
}
}
DISADVANTAGE: You have to manually manage the type and size arguments.
THE C++ WAY
===========
class event_t
{
public:
enum {
EVENT_ONE = 1,
EVENT_TWO = 2
}; /* note: using an enum isn't necc. */
virtual ~event_t() {}
int type() = 0;
uint32_t size() = 0;
};
class event_one_t : public event_t
{
public:
int type() { return int(EVENT_ONE); }
uint32_t size() { return sizeof(event_one); }
/* event-specific stuff */
};
class event_two_t : public event_t
{
public:
int type() { return int(EVENT_TWO); }
uint32_t size() { return sizeof(event_two); }
/* event-specific stuff */
};
/* ... */
void my_function(const event_t& ev)
{
switch(ev->type()) {
case event_t::EVENT_ONE:
handle_event_one( dynamic_cast<const event_one_t>(ev) );
break;
case event_t::EVENT_TWO:
handle_event_two( dynamic_cast<const event_two_t>(ev) );
break;
}
}
DISADVANTAGE: Using dynamic_cast<> uses RTTI and can have a performance
penalty... but guards against programming mistakes (throwing an
exception if cast is invalid). Switch to reinterpret_cast<> if speed is
a problem.
-gabriel