Skip to content

Instantly share code, notes, and snippets.

@h3r
Last active July 13, 2020 21:25
Show Gist options
  • Save h3r/32fb6880b639b2ca6a61a81af5ded7f5 to your computer and use it in GitHub Desktop.
Save h3r/32fb6880b639b2ca6a61a81af5ded7f5 to your computer and use it in GitHub Desktop.
C++ Header only Event system
#pragma once
#ifndef INC_ENTITY_MSGS_
#define INC_ENTITY_MSGS_
/*
A super basic event system based on LEvent implementation from @jagenjo.
Some improvements should be done:
* swap to numerical ids instead of strings: https://github.com/mariusbancila/stduuid
* add sorting method to controll the call order on trigger
This is a little example to test it works:
void onUpdate(const Event::onUpdate& msg){
std::cout << "hola" << std::endl;
}
void main() {
std::cout << "START!" << std::endl;
EventManager::bind<Event::onUpdate>("Core", "me", &onUpdate);
Event::onUpdate msg = {};
EventManager::trigger<Event::onUpdate>("Core",&msg);
EM.unbind<Event::onBeforeRender>("Render", "me");
std::cout << "END!" << std::endl;
system("PAUSE");
}
*/
#include <cassert>
#include <algorithm>
#include <unordered_map>
#include <functional>
class EventManager {
//Hacks para guardarme un puntero a una clase templatizad
struct IMsgBaseCallback {
virtual void trigger(const void* msg) = 0;
};
struct TCallbackSlot {
std::string m_sender = nullptr;
std::string m_observer = nullptr;
IMsgBaseCallback* m_callback = nullptr;
};
template< typename TMsg >
struct TMsgCallback : public IMsgBaseCallback {
// La signature de un metodo de la class TComp que recibe
// como argumento una referencia a una instancia de TMsg
// y que no devuelve nada.
typedef void (*TMethodCallback)(const TMsg&);
using Signature = std::function<void(const TMsg&)>;
// Una instancia de un puntero a uno de esos metodos
Signature m_method;
TMsgCallback(Signature new_method)
: m_method(new_method)
{ }
void trigger(const void* generic_msg) override
{
const TMsg* msg = static_cast<const TMsg*>(generic_msg);
assert(m_method != nullptr);
m_method(*msg);
}
};
inline static std::unordered_map < std::string, std::unordered_multimap<uint32_t, TCallbackSlot> > all_events;
public:
EventManager() {}
template< typename TMsg, typename TMethod >
static void bind(std::string sender, std::string observer, TMethod method)
{
auto it = all_events.find(sender);
if (it == all_events.end())
all_events.emplace(sender, std::unordered_multimap<uint32_t, TCallbackSlot>());
auto id = TMsg::getMsgID();
all_events[sender].emplace(
id,
TCallbackSlot({ sender, observer, new TMsgCallback<TMsg>(method) })
);
}
template< typename TMsg >
static void unbind(std::string sender, std::string observer)
{
auto it = all_events.find(sender);
if (it == all_events.end())
return;
//remove all if observer not provided
if (observer.empty()) {
all_events[sender].clear();
all_events.erase(sender);
return;
}
auto result = all_events[sender].equal_range(TMsg::getMsgID());
auto iter = result.first;
for (; iter != result.second; )
{
iter = (iter->second.m_observer == observer) ? all_events[sender].erase(iter) : ++iter;
}
}
template< typename TMsg >
static void trigger(std::string sender, const void* generic_msg = nullptr)
{
#ifndef NDEBUG
//Special tracking to avoid callback hell issues
static bool msg_being_triggered = false;
static std::pair< uint32_t, std::string > msg;
if (!msg_being_triggered) {
msg_being_triggered = true;
msg = std::make_pair(TMsg::getMsgID(), sender);
}
else{
assert(msg.first != TMsg::getMsgID() && msg.second != sender
|| fatal("[Error] Triggering same event from same origin while resolving an event trigger!"));
}
#endif
auto it = all_events.find(sender);
if (it == all_events.end())
return;
auto result = it->second.equal_range(TMsg::getMsgID());
for (auto& it = result.first; it != result.second; it++) {
it->second.m_callback->trigger(generic_msg);
}
#ifndef NDEBUG
msg_being_triggered = false;
#endif
}
};
namespace Event {
namespace {
struct TEvent {
virtual ~TEvent() {}
//Base event and event uuid generator
static uint32_t getNextUniqueMsgID() {
static uint32_t unique_msg_id = 0;
++unique_msg_id;
return unique_msg_id;
}
// Each TMsgStruct defining this will get assigned a new uint32_t
// associated and stored to this static method of each struct type
#define DECL_MSG_ID() \
static uint32_t getMsgID() { \
static uint32_t msg_id = getNextUniqueMsgID(); \
return msg_id; \
}
};
}
//Gameloop events
struct onUpdate : public TEvent { DECL_MSG_ID() };
struct onBeforeRender : public TEvent { DECL_MSG_ID() };
struct onRenderDebugMenu : public TEvent { DECL_MSG_ID() };
//Imgui events
struct onImGuiInit : public TEvent { DECL_MSG_ID() };
struct onImGuiEnd : public TEvent { DECL_MSG_ID() };
//Input events
struct onPlayerJoin : public TEvent { std::string who = "undefined"; DECL_MSG_ID() };
struct onPlayerLeft : public TEvent { std::string who = "undefined"; DECL_MSG_ID() };
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment