Skip to content

Instantly share code, notes, and snippets.

@Jgb14002
Created December 14, 2020 21:12
Show Gist options
  • Save Jgb14002/44716ad59c9654ad08b59abbf1c45b40 to your computer and use it in GitHub Desktop.
Save Jgb14002/44716ad59c9654ad08b59abbf1c45b40 to your computer and use it in GitHub Desktop.
Simple C++ EventBus Implementation
#pragma once
#include <unordered_map>
#include <vector>
#include <memory>
#include <typeindex>
struct Event { bool handled = false; };
class EventDispatcher
{
public:
virtual ~EventDispatcher() = default;
inline void dispatch(Event& e) const
{
invokeHandler(e);
}
private:
virtual void invokeHandler(Event& e) const = 0;
};
template<typename Receiver, typename EventType>
class EventHandler : public EventDispatcher
{
public:
using HandlerFn = void(Receiver::*)(EventType&);
EventHandler(Receiver* receiver, HandlerFn handlerFn)
: m_Receiver(receiver), m_HandlerFn(handlerFn) {}
virtual void invokeHandler(Event& e) const override
{
(m_Receiver->*m_HandlerFn)(static_cast<EventType&>(e));
}
bool operator==(const EventHandler<Receiver, EventType>& rhs) const
{
return (m_Receiver == rhs.m_Receiver)
&& (m_HandlerFn == rhs.m_HandlerFn);
}
bool operator==(Receiver* rhs) const
{
return m_Receiver == rhs;
}
private:
Receiver* m_Receiver;
HandlerFn m_HandlerFn;
};
class EventBus
{
public:
using Dispatcher = std::unique_ptr<EventDispatcher>;
using HandlerList = std::vector<Dispatcher>;
EventBus() = default;
EventBus(const EventBus&) = delete;
EventBus& operator=(const EventBus&) = delete;
template<typename EventType>
void publish(EventType&& e)
{
auto entry = m_Subscriptions.find(typeid(EventType));
if (entry != m_Subscriptions.end())
{
for (const auto& handler : entry->second)
{
if (e.handled) return;
handler->dispatch(e);
}
}
}
template<typename Receiver, typename EventType>
void subscribe(Receiver* receiver, void(Receiver::* handlerFn)(EventType&))
{
HandlerList& handlers = m_Subscriptions[typeid(EventType)];
handlers.emplace_back(
std::make_unique<EventHandler<Receiver, EventType>>(receiver, handlerFn)
);
}
template<typename Receiver, typename EventType>
void unsubscribe(Receiver* receiver, void(Receiver::* handlerFn)(EventType&))
{
auto entry = m_Subscriptions.find(typeid(EventType));
if (entry != m_Subscriptions.end())
{
const EventHandler<Receiver, EventType> tmp = { receiver, handlerFn };
HandlerList& handlers = entry->second;
handlers.erase(std::remove_if(
handlers.begin(),
handlers.end(),
[&](const auto& handler)
{
using T = EventHandler<Receiver, EventType>;
return *static_cast<T*>(handler.get()) == tmp;
}
), handlers.end());
}
}
template<typename Receiver>
void unsubscribeAll(Receiver* receiver)
{
for (auto& [_, handlers] : m_Subscriptions)
{
handlers.erase(std::remove_if(
handlers.begin(),
handlers.end(),
[&](const auto& handler)
{
using T = EventHandler<Receiver, Event>;
return *static_cast<T*>(handler.get()) == receiver;
}
), handlers.end());
}
}
private:
std::unordered_map<std::type_index, HandlerList> m_Subscriptions;
};
@JuanDiegoMontoya
Copy link

JuanDiegoMontoya commented Aug 23, 2022

Great gist! FYI it does not compile on some compilers unless you additionally #include <algorithm> for std::remove_if.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment