I am learning C++ and have been trying to create an event system for use in a small game. This will be the mechanism by which game entities communicate. I would be eternally grateful if someone with experience could critique my code. Running through it myself, I'm a little concerned about the use of Runtime Type Information (RTTI). I know that this is discouraged but I can't find any alternative to providing some basic type safety.
class EventDispatcher
{
public:
/**
* Callback function type for event handling.
*/
template<typename EventType>
struct Callback
{
/**
* Constructor.
*/
Callback(boost::function<void(const EventType& event)> callback) :
handler(callback)
{
// Nothing to do.
}
/**
* Callback function.
*/
boost::function<void(const EventType& event)> handler;
};
/**
* Constructor.
*/
EventDispatcher();
/**
* Destructor.
*/
~EventDispatcher();
/**
* Subscribe to receive events.
*
* @param callback Event handler callback.
*/
template <typename EventType>
void Subscribe(Callback<EventType> callback)
{
GetSpecificDispatcher<EventType>()->Subscribe(callback);
}
/**
* Immediately broadcast an event to subscribers.
*
* @param args... Event constructor arguments.
*/
template <typename EventType, typename... Arguments>
void Broadcast(const Arguments... args)
{
GetSpecificDispatcher<EventType>()->Broadcast(EventType(args...));
}
/**
* Queue an event to be broadcast to subscribers on the next Update
* call.
*
* @param args... Event constructor arguments.
*/
template <typename EventType, typename... Arguments>
void Enqueue(const Arguments... args)
{
GetSpecificDispatcher<EventType>()->Enqueue(EventType(args...));
}
/**
* Broadcasts all events in the queue to subscribers.
*/
void Update()
{
for (auto iter = dispatchers.begin(); iter != dispatchers.end(); ++iter)
{
iter->second->Update();
}
}
protected:
/**
* Event specific dispatcher interface.
*/
class ISpecificDispatcher
{
public:
virtual void Update() = 0;
};
/**
* Event specific dispatcher.
*/
template<typename EventType>
class SpecificDispatcher : public ISpecificDispatcher
{
public:
/**
* Subscribe to receive events.
*
* @param callback Event handler callback.
*/
void Subscribe(Callback<EventType> callback)
{
callbacks.push_back(callback);
}
/**
* Immediately broadcast an event to subscribers.
*
* @param args... Event constructor arguments.
*/
template <typename... Arguments>
void Broadcast(const Arguments... args)
{
EventType event(args...);
for (const Callback<EventType>& callback : callbacks)
{
callback.handler(event);
}
}
/**
* Queue an event to be broadcast to subscribers on the next Update
* call.
*
* @param args... Event constructor arguments.
*/
template <typename... Arguments>
void Enqueue(const Arguments... args)
{
eventQueue.push(EventType(args...));
}
/**
* Broadcasts all events in the queue to subscribers.
*/
virtual void Update()
{
while (!eventQueue.empty())
{
const EventType& event = eventQueue.front();
for (const Callback<EventType>& callback : callbacks)
{
callback.handler(event);
}
eventQueue.pop();
}
}
private:
/**
* Queued events.
*/
std::queue<EventType> eventQueue;
/**
* Event callback handlers.
*/
std::vector<Callback<EventType>> callbacks;
};
/**
* Returns a pointer to the specific dispatcher for the event type.
* A specific dispatcher is first created if one does not exist.
*/
template<typename EventType>
boost::shared_ptr<SpecificDispatcher<EventType>> GetSpecificDispatcher()
{
auto iter = dispatchers.find(typeid(EventType).name());
if (iter == dispatchers.end())
{
// A specific dispatcher was not found.
// We better create one!
boost::shared_ptr<SpecificDispatcher<EventType>> specificDispatcher(new SpecificDispatcher<EventType>());
dispatchers[typeid(EventType).name()] = specificDispatcher;
return specificDispatcher;
}
else
{
return boost::static_pointer_cast<SpecificDispatcher<EventType>>(iter->second);
}
}
private:
/**
* Event specific dispatchers.
*/
std::map<std::string, boost::shared_ptr<ISpecificDispatcher>> dispatchers;
};