Skip to content

Instantly share code, notes, and snippets.

@asmwarrior
Created July 28, 2022 06:01
Show Gist options
  • Save asmwarrior/473a4ad40bfba7a1e8eb777ed0bb7846 to your computer and use it in GitHub Desktop.
Save asmwarrior/473a4ad40bfba7a1e8eb777ed0bb7846 to your computer and use it in GitHub Desktop.
code I copy and modified from this link: c++ - ECS Event/Messaging implementation - Code Review Stack Exchange — https://codereview.stackexchange.com/questions/79211/ecs-event-messaging-implementation
#include <iostream>
#include <cstdlib>
#include <functional>
#include <vector>
struct BaseEvent
{
virtual ~BaseEvent() {}
protected:
static size_t getNextType();
};
size_t BaseEvent::getNextType()
{
static size_t type_count = 0;
return type_count++;
}
template <typename EventType>
struct Event : BaseEvent
{
static size_t type()
{
static size_t t_type = BaseEvent::getNextType();
return t_type;
}
Event(const EventType& event) : event_(event) {}
const EventType& event_;
};
struct EventManager
{
template <class EventType>
using call_type = std::function<void(const EventType&)>;
template <typename EventType>
void subscribe(call_type<EventType> callable)
{
// When events such as COLLISION don't derive
// from Event, you have to get the type by
// using one more level of indirection.
size_t type = Event<EventType>::type();
if (type >= m_subscribers.size())
m_subscribers.resize(type+1);
m_subscribers[type].push_back(CallbackWrapper<EventType>(callable));
}
template <typename EventType>
void emit(const EventType& event)
{
// Same change to get the type.
size_t type = Event<EventType>::type();
if (type >= m_subscribers.size())
m_subscribers.resize(type+1);
// This a crucial change to the code.
// You construct a temporary Event object by
// using the EventType object and use Event.
// This requires a change to Event, which follows below.
Event<EventType> eventWrapper(event);
for (auto& receiver : m_subscribers[type])
receiver(eventWrapper);
}
template <typename EventType>
struct CallbackWrapper
{
CallbackWrapper(call_type<EventType> callable) : m_callable(callable) {}
void operator() (const BaseEvent& event) {
// The event handling code requires a small change too.
// A reference to the EventType object is stored
// in Event. You get the EventType reference from the
// Event and make the final call.
m_callable(static_cast<const Event<EventType>&>(event).event_); }
call_type<EventType> m_callable;
};
std::vector<std::vector<call_type<BaseEvent>>> m_subscribers;
};
struct PLAYER_HIT
{ int damage; };
struct PLAYER_LVL_UP
{ int new_level; };
struct COLLISION
{ int entity1; int entity2; };
struct PLAYER_GUI
{
PLAYER_GUI(EventManager& em)
{
using namespace std::placeholders;
em.subscribe<PLAYER_HIT>(
std::bind(&PLAYER_GUI::handle_hit, this, _1));
em.subscribe<PLAYER_LVL_UP>(
std::bind(&PLAYER_GUI::handle_lvl_up, this, _1));
em.subscribe<COLLISION>(
std::bind(&PLAYER_GUI::handle_collision, this, _1));
}
void handle_hit(const PLAYER_HIT& event)
{
std::cout << "handle_hit: damage= " << event.damage << std::endl;
}
void handle_lvl_up(const PLAYER_LVL_UP& event)
{
std::cout << "handle_lvl_up: new_level= " << event.new_level << std::endl;
}
void handle_collision(const COLLISION& event)
{
std::cout << "handle_collision: " << event.entity1 << "," << event.entity2 << std::endl;
}
};
int main()
{
EventManager em;
PLAYER_GUI gui(em);
em.emit(PLAYER_LVL_UP{1});
em.emit(PLAYER_HIT{2});
em.emit(COLLISION{3,4});
return 0;
}
@swissiety
Copy link

If we assume the handlers take just the event they are listen to as a parameter we can simplify the necessary glue code for the event subscription by adding:

class BaseHandler
{
protected:
    template <typename EventType, typename HandlerType>
    inline void subscribe(EventManager &em, void (HandlerType::*method)(const EventType &))
    {
        em.subscribe<EventType>(std::bind(method, static_cast<HandlerType* >(this), std::placeholders::_1));
    }
};

and extend the handler(s) (here: PLAYERGUI) from BaseHandler, the usage would be:

subscribe(em, &PLAYER_GUI::handle_collision);

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