Since others have mentioned the visitor pattern, here is a slight twist using Boost.Variant. This library is often a good choice (or at least it has been for me) when you need a set of different behaviors based on a value. Compared to a void*
, it has the benefit of static type checking: if you write a visitor class the misses one of the cases, your code will not compile rather than failing at run time.
Step 1: Define message types:
struct EVENT_EXIT { }; // just a tag, really
struct EVENT_PLAYER_CHAT { Player * p; std::string msg; };
typedef boost::variant<EVENT_EXIT,
EVENT_PLAYER_CHAT> event;
Step 2: Define a visitor:
struct event_handler : public boost::static_visitor<void> {
void operator()(EVENT_EXIT const& e) {
// handle exit event here
}
void operator()(EVENT_PLAYER_CHAT const& e) {
// handle chat event here
std::cout << e.msg << std::endl;
}
};
This defines an event handler that nicely separates out the code for each kind of event. The existence of all operator()
overloads is checked at compile time (on template instantiation), so if you add an event type later, the compiler will force you to add corresponding handler code.
Note that event_handler
subclasses boost::static_visitor<void>
. This determines the return type for each of the operator()
overloads.
Step 3: Use your event handler:
event_handler handler;
// ...
event const& e = get_event(); //variant type
boost::apply_visitor(handler, e); // will not compile unless handler
// implements operator() for each
// kind of event
Here, apply_visitor
will call the appropriate overload for the 'actual' value of e
. For example, if we define get_event
as follows:
event get_event() {
return EXIT_EVENT();
}
Then the return value will be converted implicitly to event(EXIT_EVENT())
. Then apply_visitor
will call the corresponding operator()(EXIT_EVENT const&)
overload.