Last active
January 2, 2023 10:55
-
-
Save CCColda/e3d3410c2c816cf6e394f133884858c8 to your computer and use it in GitHub Desktop.
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
/** | |
* @headerfile Event.hpp | |
* @author COlda | |
* @copyright MIT | |
* @see https://gist.github.com/CCColda/e3d3410c2c816cf6e394f133884858c8 | |
*/ | |
#ifndef COLDA_EVENT_H | |
#define COLDA_EVENT_H | |
#include <cassert> | |
#include <functional> | |
#include <list> | |
#include <map> | |
#include <string> | |
#include <type_traits> | |
#include <typeinfo> | |
/** | |
* @brief Represents an event with a name, and respective data | |
* @tparam T The type of the associated event data - must not be a pointer or reference | |
*/ | |
template <typename T> | |
class Event { | |
static_assert(!std::is_pointer<T>::value, "Event value must not be a raw pointer"); | |
static_assert(!std::is_reference<T>::value, "Event value must not be a reference"); | |
public: | |
const std::string name; | |
void *const data; | |
const std::size_t typehash; | |
public: | |
inline Event(const std::string &iname, void *const idata) | |
: name(iname), data(idata), typehash(getStaticTypeHash()) {} | |
inline Event(const std::string &iname, const T &idata) | |
: name(iname), data((void *)&idata), typehash(getStaticTypeHash()) | |
{ | |
} | |
inline static std::size_t getStaticTypeHash() | |
{ | |
return typeid(T).hash_code(); | |
} | |
/** @brief Returns the event data with the type @c T */ | |
[[nodiscard]] inline const T &get() const | |
{ | |
return *reinterpret_cast<T *>(data); | |
} | |
/** @brief Provides explicit conversion for type @c T */ | |
[[nodiscard]] inline explicit operator T() const | |
{ | |
return get(); | |
} | |
}; | |
/** @brief Represents an event emitter. Stores event callbacks. */ | |
class EventEmitter { | |
public: | |
/** @typedef An event callback function, for event data type @c E */ | |
template <typename E> | |
using EventCallback = std::function<void(const Event<E> &event)>; | |
private: | |
using CallbackDelegate = std::function<void(void *const data, std::size_t typehash)>; | |
struct Callback { | |
std::string callbackName; | |
CallbackDelegate delegate; | |
}; | |
std::map<std::string, std::list<Callback>> m_callbacks; | |
public: | |
/** | |
* @brief Emits an event with its respective data to all the listeners attached with @c on() | |
* @tparam E The event data type | |
* @param event The event | |
*/ | |
template <typename E> | |
inline void emit(const Event<E> &event) const | |
{ | |
if (m_callbacks.count(event.name) == 0) | |
return; | |
for (const auto cb : m_callbacks.at(event.name)) | |
cb.delegate(event.data, event.typehash); | |
} | |
/** | |
* @brief Adds an event listener with an optional name | |
* @param eventName The name of the event | |
* @param callback The event callback function | |
* @param callbackName The name of the callback | |
* @tparam E The event data type | |
* @see EventCallback | |
*/ | |
template <typename E> | |
inline void on(const std::string &eventName, EventCallback<E> callback, std::string callbackName = "") | |
{ | |
if (m_callbacks.count(eventName) == 0) | |
m_callbacks[eventName] = {}; | |
m_callbacks[eventName].push_back( | |
Callback{ | |
callbackName, | |
[eventName, callback](void *const data, std::size_t typehash) { | |
assert(typehash == Event<E>::getStaticTypeHash() && "Type mismatch between event and callback"); | |
callback(Event<E>(eventName, data)); | |
}}); | |
} | |
/** @brief Removes all callbacks from @c eventName with the name @c callbackName */ | |
inline void remove(const std::string &eventName, const std::string &callbackName) | |
{ | |
if (m_callbacks.count(eventName) == 0) | |
return; | |
m_callbacks[eventName].remove_if([callbackName](const Callback &cb) { | |
return cb.callbackName == callbackName; | |
}); | |
} | |
/** @brief Removes all callbacks from @c eventName */ | |
inline void removeAll(const std::string &eventName) | |
{ | |
if (m_callbacks.count(eventName) != 0) | |
m_callbacks.erase(eventName); | |
} | |
}; | |
namespace EventUtility { | |
struct EmptyEventData {}; | |
/** @brief Represents an empty event */ | |
using EmptyEvent = Event<EmptyEventData>; | |
/** @brief Creates an empty event with a @c name */ | |
[[nodiscard]] inline EmptyEvent createEmptyEvent(const std::string &name){return EmptyEvent(name, EmptyEventData{});}; | |
} // namespace EventUtility | |
#endif // !defined(COLDA_EVENT_H) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment