]> git.tdb.fi Git - libs/game.git/blobdiff - source/game/eventbus.h
Add an event bus for delivering events
[libs/game.git] / source / game / eventbus.h
diff --git a/source/game/eventbus.h b/source/game/eventbus.h
new file mode 100644 (file)
index 0000000..ce9c812
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef MSP_GAME_EVENTBUS_H_
+#define MSP_GAME_EVENTBUS_H_
+
+#include <functional>
+#include <vector>
+
+namespace Msp::Game {
+
+class EventObserver;
+
+template<typename T>
+struct EventDispatcher
+{
+       struct Handler
+       {
+               EventObserver *observer;
+               std::function<void(const T &)> callback;
+       };
+
+       std::vector<Handler> handlers;
+
+       void add_observer(EventObserver *obs, std::function<void(const T &)> &&cb)
+       { handlers.emplace_back(obs, std::move(cb)); }
+
+       void remove_observer(EventObserver *obs)
+       { std::erase_if(handlers, [obs](const Handler &h){ return h.observer==obs; }); }
+
+       void dispatch(const T &) const;
+};
+
+
+class EventBus
+{
+private:
+       using DeleteFunc = void(void *);
+       using RemoveFunc = void(void *, EventObserver &);
+
+       struct Dispatcher
+       {
+               void *dispatcher = nullptr;
+               DeleteFunc *deleter = nullptr;
+               RemoveFunc *remover = nullptr;
+       };
+
+       std::vector<Dispatcher> dispatchers;
+
+       static unsigned get_next_id();
+
+public:
+       template<typename T>
+       static unsigned get_event_id();
+
+private:
+       template<typename T>
+       EventDispatcher<T> &get_emitter();
+
+public:
+       template<typename T>
+       void add_observer(EventObserver &obs, std::function<void(const T &)> cb)
+       { get_emitter<T>().add_observer(obs, std::move(cb)); }
+
+       void replace_observer(EventObserver &, EventObserver &);
+       void remove_observer(EventObserver &);
+
+       template<typename T>
+       void dispatch(const T &) const;
+};
+
+
+template<typename T>
+void EventDispatcher<T>::dispatch(const T &event) const
+{
+       for(const Handler &h: handlers)
+               h.callback(event);
+}
+
+
+template<typename T>
+inline unsigned EventBus::get_event_id()
+{
+       static unsigned id = get_next_id();
+       return id;
+}
+
+template<typename T>
+inline EventDispatcher<T> &EventBus::get_emitter()
+{
+       unsigned id = get_event_id<T>();
+       if(dispatchers.size()<=id)
+               dispatchers.resize(id+1);
+
+       Dispatcher &event = dispatchers[id];
+       if(!event.dispatcher)
+       {
+               event.dispatcher = new EventDispatcher<T>;
+               event.deleter = [](void *p){ delete static_cast<EventDispatcher<T> *>(p); };
+               event.remover = [](void *p, EventObserver &o){ static_cast<EventDispatcher<T> *>(p)->remove_observer(o); };
+       }
+
+       return static_cast<EventDispatcher<T> *>(event.dispatcher);
+}
+
+template<typename T>
+inline void EventBus::dispatch(const T &event) const
+{
+       unsigned id = get_event_id<T>();
+       if(id<dispatchers.size() && dispatchers[id].dispatcher)
+               static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch(event);
+}
+
+} // namespace Msp::Game
+
+#endif