Skip to content

Instantly share code, notes, and snippets.

@CCColda
Last active January 2, 2023 10:55
Show Gist options
  • Save CCColda/e3d3410c2c816cf6e394f133884858c8 to your computer and use it in GitHub Desktop.
Save CCColda/e3d3410c2c816cf6e394f133884858c8 to your computer and use it in GitHub Desktop.
/**
* @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