I am trying to build a generic event system. The Delegates and Events should not know anything about the other and a Manager will handle everything.
With this in mind I created a templated delegate/listener is made up of a function pointer and templated parameters.
class IDelegate
{
public:
IDelegate() {};
virtual ~IDelegate() = 0;
virtual void exec() = 0;
};
template<class Class, typename... Args>
class Delegate : public IDelegate
{
public:
typedef (Class::*Function)(Args);
Delegate(Class* inst, Function func) : instance(inst), function(func) {};
~Delegate() { instance = nullptr };
void exec(Args args)
{
instance->function(args);
}
private:
Class* instance;
Function function;
};
I did something similar on the Event side. The Events being made up of an ID and the Arguments that will need to be past to the function pointer.
class IEvent
{
public:
IEvent() {};
virtual ~IEvent() = 0;
};
template<typename... Args>
class Event : IEvent
{
public:
Event(ID eventID, Args args) : id(eventID), arguments(args) {};
~Event() = default;
ID id;
Args arguments;
};
I choose to use templates in the hope of not needing to manually create every event/delegate class that may be needed.
Lastly I wanted to make a Manager that would be a singleton.
//EventHandler.h
#pragma once
#include <string>
#include <unordered_map>
#include <queue>
#include "Event.h"
typedef std::string ID;
typedef std::unordered_multimap<ID, void*> Listeners;
class EventHandler
{
public:
EventHandler(const EventHandler& copy) = delete;
~EventHandler();
EventHandler& operator= (const EventHandler& rhs) = delete;
void Initialize();
template<class Class, class TEvent>
void Run();
void Shutdown();
static Listeners::iterator& Register(ID id, IDelegate* listener);
static void Deregister(Listeners::iterator& iterator);
static void Post(IEvent* evnt);
private:
static Listeners listeners;
static std::queue<IEvent*> events;
static EventHandler* instance;
EventHandler() {};
EventHandler(const EventHandler& copy);
EventHandler& operator= (const EventHandler& rhs);
};
//EventHandler.cpp
#include "EventHandler.h"
EventHandler::~EventHandler()
{
instance = nullptr;
}
void EventHandler::Initialize()
{
instance = this;
}
void EventHandler::Run()
{
//TODO: Determine the Event and cast or instantiate to the right class
IEvent* evnt = events.front; //This should not be IEvent*, but Event<>*
events.pop();
listeners[evnt->id].exec(evnt->arguments); //The delegate may need to be casted too.
}
void EventHandler::Shutdown()
{
instance = nullptr;
}
Listeners::iterator& EventHandler::Register(ID id, IDelegate* listener)
{
Listeners::iterator iter = listeners.emplace(id, listener);
return iter;
}
void EventHandler::Deregister(Listeners::iterator& iterator)
{
listeners.erase(iterator);
}
void EventHandler::Post(IEvent* evnt)
{
events.emplace(evnt);
}
Where I'm running into trouble is figuring out what Event I'm actually using in Run(). If possible I would like to do this without a switch or something similar as that would defeat the use of the templated classes. I have considered making the function pointer all be the same signature as this would simplify some of the code, but would make the system less flexible.
Thank you for any help.