Created
July 4, 2016 15:38
-
-
Save LukasKalbertodt/b2bb98bfb19b3a2545a4e85c9ec31adb to your computer and use it in GitHub Desktop.
Just found this snippet of code... it's at least 3 years old. An exception-like event manager for games...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Template based Event-System by Lukas Kalbertodt <[email protected]> | |
*/ | |
#pragma once | |
// needed std headers | |
#include <map> | |
#include <memory> | |
#include <cstring> | |
#include <set> | |
/** | |
* \brief base class for all events | |
* Has one static method to get a unique id to a event type (nicer than typeid) | |
* Derived classes should always have a | |
* typedef MyBase base; | |
* where "MyBase" is the base class of the derived class. | |
* Without this typedef the event can not be used with the EventManager! | |
*/ | |
class Event | |
{ | |
public: | |
template <typename T> static void* GetEventID() | |
{ | |
static int id; | |
return static_cast<void*>(&id); | |
} | |
}; | |
/** | |
* \brief helper traits for lambda function types | |
* Helper traits to get return type and arg type of a lambda function. | |
* Uses decltype(&T::operator()) to get types. Works just for one function | |
* argument, but it is enough for this event system. | |
* result_type -> type of the return value | |
* arg_type -> type of the argument | |
*/ | |
template <typename T> | |
struct lambda_traits | |
: public lambda_traits<decltype(&T::operator())> | |
{}; | |
// specialization for lambdas without parameter (this is an error) | |
template <typename ClassType, typename ReturnType> | |
struct lambda_traits<ReturnType(ClassType::*)() const> | |
{ | |
static_assert(std::is_void<ClassType>::value, | |
"Given lambda function does not have any arguments. It must have const Event& as argument!"); | |
}; | |
// specialization for lambdas we accept | |
template <typename ClassType, typename ReturnType, typename Arg> | |
struct lambda_traits<ReturnType(ClassType::*)(Arg) const> | |
{ | |
typedef ReturnType result_type; | |
typedef Arg arg_type; | |
}; | |
/** | |
* \brief Wrapper for different functions | |
* Wrapper for member functions, normal functions and lambda functions. Is | |
* sortable, based on | |
* 1. Size of the function pointer/function object | |
* 2. Type of the function | |
* 3. Raw compare of function pointer/function object data | |
* Attention: Different compilers have different sizes for function pointer/ | |
* function objects. The sort order can differ between different compilers! | |
* | |
* Uses type erasure to save different function pointer/objects. Member functions | |
* do not save the pointer to the object at which it will be called. Object has | |
* to be passed while calling. This object is ignored when calling a normal | |
* function or a lambda function. | |
*/ | |
class EventFunctionWrapper | |
{ | |
private: | |
// Base class for type erasure | |
struct FunctionConcept | |
{ | |
FunctionConcept() : m_cmpData(nullptr) {} | |
// calling the underlying function on the memberHost (if the underlying | |
// function is a member function) | |
virtual void operator()(void* memberHost, const Event& event) const = 0; | |
// Helper functions for comparing raw data | |
virtual void* GetRawData() = 0; | |
virtual int GetDataSize() = 0; | |
// pointer to data buffer with the comparable data | |
char* m_cmpData; | |
}; | |
// class for member functions | |
template <typename THost, typename TEvent> | |
struct MemberFunctionModel : public FunctionConcept | |
{ | |
// member function pointer | |
void(THost::*m_func)(const TEvent&); | |
char m_cmpData[sizeof(void(THost::*)(const TEvent&)) + 1]; | |
MemberFunctionModel(void(THost::*ptr)(const TEvent&)) : m_func(ptr) | |
{ | |
memset(m_cmpData, 0, 1); // set identifier byte for member function (0) | |
memcpy(m_cmpData + 1, &m_func, sizeof(m_func)); // copy member pointer in buffer | |
} | |
virtual void operator()(void* memberHost, const Event& event) const | |
{ | |
(static_cast<THost*>(memberHost)->*m_func)(static_cast<const TEvent&>(event)); | |
} | |
virtual void* GetRawData() | |
{ | |
return m_cmpData; | |
} | |
virtual int GetDataSize() | |
{ | |
return sizeof(m_func); | |
} | |
}; | |
// class for lambda function | |
template <typename TLambda> | |
struct LambdaFunctionModel : public FunctionConcept | |
{ | |
// lambda function object of strange type | |
TLambda m_func; | |
char m_cmpData[sizeof(TLambda) + 1 + sizeof(&TLambda::operator())]; | |
LambdaFunctionModel(TLambda f) : m_func(f) | |
{ | |
memset(m_cmpData, 1, 1); // set identifier byte for lambda functions (1) | |
memcpy(m_cmpData + 1, &m_func, sizeof(m_func)); // copy lambda data | |
auto ptr = &TLambda::operator(); | |
memcpy(m_cmpData + 1 + sizeof(m_func), &ptr, sizeof(ptr)); // copy function pointer data | |
} | |
virtual void operator()(void* memberHost, const Event& event) const | |
{ | |
m_func(static_cast<typename lambda_traits<TLambda>::arg_type>(event)); | |
} | |
virtual void* GetRawData() | |
{ | |
return m_cmpData; | |
} | |
virtual int GetDataSize() | |
{ | |
return sizeof(m_func); | |
} | |
}; | |
// class for normal functions | |
template <typename TEvent> | |
struct RawFunctionModel : public FunctionConcept | |
{ | |
// function pointer | |
void(*m_func)(const TEvent&); | |
char m_cmpData[sizeof(void(*)(const TEvent&)) + 1]; | |
RawFunctionModel(void(*ptr)(const TEvent&)) : m_func(ptr) | |
{ | |
memset(m_cmpData, 2, 1); // set identifier byte for normal function (2) | |
memcpy(m_cmpData + 1, &m_func, sizeof(m_func)); // copy function pointer | |
} | |
virtual void operator()(void* memberHost, const Event& event) const | |
{ | |
m_func(static_cast<const TEvent&>(event)); | |
} | |
virtual void* GetRawData() | |
{ | |
return m_cmpData; | |
} | |
virtual int GetDataSize() | |
{ | |
return sizeof(m_func); | |
} | |
}; | |
// save erased type | |
std::shared_ptr<FunctionConcept> m_funcConcept; | |
public: | |
// constructor for member functions | |
template <typename THost, typename TEvent> | |
EventFunctionWrapper(void(THost::*ptr)(const TEvent&)) | |
: m_funcConcept(new MemberFunctionModel<THost, TEvent>(ptr)) | |
{} | |
// constructor for lambda functions | |
template <typename TLambda> | |
EventFunctionWrapper(TLambda ptr) | |
: m_funcConcept(new LambdaFunctionModel<TLambda>(ptr)) | |
{} | |
// constructor for normal functions | |
template <typename TEvent> | |
EventFunctionWrapper(void(*ptr)(const TEvent&)) | |
: m_funcConcept(new RawFunctionModel<TEvent>(ptr)) | |
{} | |
// calling the underlying function | |
void operator()(void* memberHost, const Event& event) const | |
{ | |
(*m_funcConcept)(memberHost, event); | |
} | |
// compare operator (for more information see above in class description) | |
bool operator==(const EventFunctionWrapper& other) | |
{ | |
if(other.m_funcConcept->GetDataSize() != m_funcConcept->GetDataSize()) | |
return false; | |
return (memcmp(other.m_funcConcept->GetRawData(), m_funcConcept->GetRawData(), m_funcConcept->GetDataSize()) == 0); | |
} | |
// compare operator (for more information see above in class description) | |
bool operator<(const EventFunctionWrapper& other) const | |
{ | |
if(other.m_funcConcept->GetDataSize() != m_funcConcept->GetDataSize()) | |
return m_funcConcept->GetDataSize() < other.m_funcConcept->GetDataSize(); | |
return (memcmp(other.m_funcConcept->GetRawData(), m_funcConcept->GetRawData(), m_funcConcept->GetDataSize()) > 0); | |
} | |
// compare operator (for more information see above in class description) | |
bool operator>(const EventFunctionWrapper& other) const | |
{ | |
if(other.m_funcConcept->GetDataSize() != m_funcConcept->GetDataSize()) | |
return m_funcConcept->GetDataSize() > other.m_funcConcept->GetDataSize(); | |
return (memcmp(other.m_funcConcept->GetRawData(), m_funcConcept->GetRawData(), m_funcConcept->GetDataSize()) < 0); | |
} | |
}; | |
/** | |
* \brief Manages everything with Events | |
* Manages Listener to specific event types and calls those Listener when a | |
* event of that type or a derived type is triggered. | |
* Works as a singleton! | |
*/ | |
class EventManager | |
{ | |
public: | |
// typedef for better reading of source code | |
typedef void* EventType; | |
typedef void* HostType; | |
// --------------------------------------------------------------- | |
// ------- PUBLIC FUNCTIONS -------------------------------------- | |
// --------------------------------------------------------------- | |
~EventManager() {} | |
/** | |
* \brief Returns one instance of EventManager | |
*/ | |
static EventManager* GetInstance() | |
{ | |
static EventManager instance; | |
return &instance; | |
} | |
/** | |
* \brief Triggers Event and call all Listener | |
* When the Event is triggered all Listener on that event are called. Then | |
* all Listener on the base event are called (recursive). | |
* Note: Never specify the second parameter! You also do not need to specify the template | |
* argument, which should be specified with param e. | |
* This overloaded function will be called with T!=Event. If T=Event the overloaded method | |
* TriggerEvent(T& e, ...) is invoked. | |
* | |
* \param e The event to trigger. Must be of Type Event or any derived Type. | |
*/ | |
template <typename T> | |
void TriggerEvent(const T& e) | |
{ | |
// check if T is derived from Event | |
static_assert(std::is_base_of<Event, T>::value, "Given Type is not derived from Event!"); | |
// call all functions listening on T | |
this->CallAllListener(GetEventID<T>(), e); | |
// call all functions listening on base class of T | |
this->TriggerEvent(static_cast<const typename T::base&>(e)); | |
} | |
/** | |
* \brief Triggers Event and call all Listener | |
* When the Event is triggered all Listener on that event are called. | |
* Note: Never specify a second parameter! You also do not need to specify the template | |
* argument, which should be specified with param e. | |
* This overloaded function will be called with T=Event. Every other T will invoke the | |
* overloaded function TriggerEvent(T& e, typename T::base* = nullptr). | |
* | |
* \param e The event to trigger. Must be of Type Event or any derived Type. | |
*/ | |
void TriggerEvent(const Event& e) | |
{ | |
// call all functions listening on T | |
this->CallAllListener(GetEventID<Event>(), e); | |
} | |
/** | |
* \brief Add a function as listening function to a specific EventType. | |
* After registering the function is called when a event of the given type or a derived type is triggered. | |
* This method should be used for member functions! For registering other function types see the overloaded | |
* methods of RegisterListener below. | |
* | |
* \param callback pointer to member to the function that should be called if the event is triggered | |
* \param obj the object on which the member function should be called | |
* \tparam T The type of Event to listen on (must be given as explicit template parameter). | |
* Must be equal to Event or a derived class of Event | |
* \tparam F The type of argument, which the member function given with callback accepts. | |
* Implicit given by param callback. F must be T or a baseclass of T! | |
*/ | |
template <typename T, typename O, typename F> | |
void RegisterListener(void(O::*callback)(const F&), O* obj) | |
{ | |
// check if T is derived from Event or equal to Event | |
static_assert(std::is_base_of<Event, T>::value, "Given EventType (Parameter 1) is not derived from Event!"); | |
// check if the member function fits to T (F must be a baseclass of T or equal to T) | |
static_assert(std::is_base_of<F, T>::value, "Given function (Parameter 2) does not accept a Event from type T or any baseclass of T!"); | |
// wrap pointer to member in our Wrapper and insert it in the set associated with the host and event type | |
EventType eID = GetEventID<T>(); | |
m_Listener[eID][obj].insert(EventFunctionWrapper(callback)); | |
} | |
// RegisterListener for lambda functions (for more information see above) | |
template <typename T, typename L> | |
void RegisterListener(L lambda, HostType listenerHost = nullptr) | |
{ | |
// check if T is derived from Event or equal to Event | |
static_assert(std::is_base_of<Event, T>::value, "Given EventType (Parameter 1) is not derived from Event!"); | |
// check if lambda function accepts a valid argument | |
static_assert(std::is_base_of<typename std::remove_reference<typename lambda_traits<L>::arg_type>::type, T>::value, | |
"Given function (Parameter 2) does not accept a Event from type T or any baseclass of T!"); | |
// wrap lambda in our Wrapper and insert it in the set associated with the event type (host) | |
EventType eID = GetEventID<T>(); | |
m_Listener[eID][listenerHost].insert(EventFunctionWrapper(lambda)); | |
} | |
// RegisterListener for normal functions (for more information see above) | |
template <typename T, typename F> | |
void RegisterListener(void(*fPtr)(const F&), HostType listenerHost = nullptr) | |
{ | |
// check if T is derived from Event or equal to Event | |
static_assert(std::is_base_of<Event, T>::value, "Given EventType (Parameter 1) is not derived from Event!"); | |
// check if function accepts a valid argument | |
static_assert(std::is_base_of<F, T>::value, "Given function (Parameter 2) does not accept a Event from type T or any baseclass of T!"); | |
// wrap function in our Wrapper and insert it in the set associated with the event type (host) | |
EventType eID = GetEventID<T>(); | |
m_Listener[eID][listenerHost].insert(EventFunctionWrapper(fPtr)); | |
} | |
/** | |
* \brief Removes the listener associated with the given event type, function and object | |
* This function removes a listener from the intern listener-list. Listener will be | |
* deleted, which listen on type T, are associated with the object 'listenerHost' and which | |
* function pointer equals the function pointer 'callback'. | |
* | |
* \param callback pointer to member to a function associated with the listeners to be deleted | |
* \param listenerHost the object associated with the listeners to be deleted | |
* \tparam T The type of Event associated with the listeners to be deleted | |
*/ | |
template <typename T, typename O, typename F> | |
void RemoveListener(void(O::*callback)(const F&), O* listenerHost) | |
{ | |
// check if T is derived from Event or equal to Event | |
static_assert(std::is_base_of<Event, T>::value, "Given EventType (Parameter 1) is not derived from Event!"); | |
// check if the member function fits to T (F must be a baseclass of T or equal to T) | |
static_assert(std::is_base_of<F, T>::value, "Given function (Parameter 2) does not accept a Event from type T or any baseclass of T!"); | |
// delete function from set | |
EventType eID = GetEventID<T>(); | |
m_Listener[eID][listenerHost].erase(EventFunctionWrapper(callback)); | |
} | |
/** | |
* \brief Removes the listener associated with the given event type and object | |
* This function removes a listener from the intern listener-list. Listener will be | |
* deleted, which listen on type T and are associated with the object 'obj' | |
* | |
* \param obj the object associated with the listeners to be deleted | |
* \tparam T The type of Event associated with the listeners to be deleted | |
*/ | |
template <typename T, typename O> | |
void RemoveListener(O* obj) | |
{ | |
EventType eID = GetEventID<T>(); | |
m_Listener[eID].erase(obj); | |
} | |
/** | |
* \brief Removes the listener associated with the given member function | |
* | |
* \param callback pointer to member to a function associated with the listeners to be deleted | |
* \tparam T The type of Event associated with the listeners to be deleted | |
*/ | |
template <typename T, typename O, typename F> | |
void RemoveListener(void(O::*callback)(const F&)) | |
{ | |
EventType eID = GetEventID<T>(); | |
for(auto itHosts = m_Listener[eID].begin(); itHosts != m_Listener[eID].end(); itHosts++) | |
{ | |
itHosts->second.erase(EventFunctionWrapper(callback)); | |
} | |
} | |
/** | |
* \brief Removes the listener associated with the given function and object | |
* This function removes a listener from the intern listener-list. Listener will be | |
* deleted, which are associated with the object 'obj' and which function pointer | |
* equals the function pointer 'callback'. Listener are deleted regardless of event type. | |
* | |
* \param callback pointer to member to a function associated with the listeners to be deleted | |
* \param obj the object associated with the listeners to be deleted | |
*/ | |
template <typename O, typename F> | |
void RemoveAllListener(void(O::*callback)(const F&), O* listenerHost) | |
{ | |
for(auto itEvents = m_Listener.begin(); itEvents != m_Listener.end(); itEvents++) | |
{ | |
itEvents->second[listenerHost].erase(callback); | |
} | |
} | |
/** | |
* \brief Removes the listener associated with the given object | |
* This function removes a listener from the intern listener-list. Listener will be | |
* deleted, which are associated with the object 'obj', regardless of event type. | |
* | |
* \param obj the object associated with the listeners to be deleted | |
*/ | |
template <typename O> | |
void RemoveAllListener(O* obj) | |
{ | |
for(auto it = m_Listener.begin(); it != m_Listener.end(); it++) | |
{ | |
(it->second).erase(obj); | |
} | |
} | |
/** | |
* \brief Removes the listener associated with the given member function | |
* of all event types | |
* | |
* \param callback pointer to member to a function associated with the listeners to be deleted | |
*/ | |
template <typename O, typename F> | |
void RemoveAllListener(void(O::*callback)(const F&)) | |
{ | |
for(auto itEvents = m_Listener.begin(); itEvents != m_Listener.end(); itEvents++) | |
{ | |
for(auto itHosts = itEvents->second.begin(); itHosts != itEvents->second.end(); itHosts++) | |
{ | |
itHosts->second.erase(EventFunctionWrapper(callback)); | |
} | |
} | |
} | |
private: | |
// --------------------------------------------------------------- | |
// ------- PRIVATE FUNCTIONS ------------------------------------- | |
// --------------------------------------------------------------- | |
// private constructor | |
EventManager() {} | |
/** | |
* \brief Calls all listening function to specific EventID | |
* | |
* \param eID The ID from the EventType | |
* \param e The triggered event | |
*/ | |
void CallAllListener(EventType eID, const Event& e) | |
{ | |
auto hostMap = m_Listener[eID]; | |
for(auto itHosts = hostMap.begin(); itHosts != hostMap.end(); itHosts++) | |
{ | |
auto functionSet = itHosts->second; | |
for(auto itFunctions = functionSet.begin(); itFunctions != functionSet.end(); itFunctions++) | |
{ | |
(*itFunctions)(itHosts->first, e); | |
} | |
} | |
} | |
// small helper to get real event id, independent of references | |
template <typename T> | |
void* GetEventID() | |
{ | |
return Event::GetEventID<typename std::remove_reference<T>::type>(); | |
} | |
// --------------------------------------------------------------- | |
// ------- MEMBER VARIABLEs -------------------------------------- | |
// --------------------------------------------------------------- | |
std::map<EventType, std::map<HostType, std::set<EventFunctionWrapper>>> m_Listener; | |
}; | |
/** | |
* \brief Helper to manage event listeners | |
* Works as base class. The derived class should then register listener | |
* over the function of this helper class. Then all listener are removed, | |
* when the destructor of the derived object is called. | |
*/ | |
template <typename T> | |
class EventListenerHelper | |
{ | |
protected: | |
EventListenerHelper() | |
{ | |
m_Obj = static_cast<T*>(this); | |
m_Mng = EventManager::GetInstance(); | |
} | |
virtual ~EventListenerHelper() | |
{ | |
m_Mng->RemoveAllListener(m_Obj); | |
} | |
/** | |
* \brief Wrapper for EventManager::RegisterListener (see there for further description) | |
* notice: There is no wrapper for normal function because it makes no | |
* sense to bind normal functions to object | |
*/ | |
template <typename E, typename F> | |
void RegisterListener(void(T::*callback)(const F&)) | |
{ | |
m_Mng->RegisterListener<E>(callback, m_Obj); | |
} | |
// wrapper for lambda function | |
// notice: If the object is deleted, the registered lambda function will | |
// be unregistered. If this is not intended use the RegisterListener | |
// method directly from EventManager | |
template <typename E, typename L> | |
void RegisterListener(L lambda) | |
{ | |
m_Mng->RegisterListener<E>(lambda, m_Obj); | |
} | |
private: | |
T* m_Obj; | |
EventManager* m_Mng; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment