]> git.tdb.fi Git - libs/game.git/blob - source/game/eventbus.h
Use template lambdas to avoid repetition of types
[libs/game.git] / source / game / eventbus.h
1 #ifndef MSP_GAME_EVENTBUS_H_
2 #define MSP_GAME_EVENTBUS_H_
3
4 #include <functional>
5 #include <vector>
6
7 namespace Msp::Game {
8
9 class EventObserver;
10
11 template<typename T>
12 struct EventDispatcher
13 {
14         struct Handler
15         {
16                 EventObserver *observer;
17                 std::function<void(const T &)> callback;
18         };
19
20         std::vector<Handler> handlers;
21
22         void add_observer(EventObserver *obs, std::function<void(const T &)> &&cb)
23         { handlers.emplace_back(obs, std::move(cb)); }
24
25         void remove_observer(EventObserver *obs)
26         { std::erase_if(handlers, [obs](auto &h){ return h.observer==obs; }); }
27
28         void dispatch(const T &) const;
29 };
30
31
32 class EventBus
33 {
34 private:
35         using DeleteFunc = void(void *);
36         using RemoveFunc = void(void *, EventObserver &);
37
38         struct Dispatcher
39         {
40                 void *dispatcher = nullptr;
41                 DeleteFunc *deleter = nullptr;
42                 RemoveFunc *remover = nullptr;
43         };
44
45         std::vector<Dispatcher> dispatchers;
46
47         static unsigned get_next_id();
48
49 public:
50         template<typename T>
51         static unsigned get_event_id();
52
53 private:
54         template<typename T>
55         EventDispatcher<T> &get_emitter();
56
57 public:
58         template<typename T>
59         void add_observer(EventObserver &obs, std::function<void(const T &)> cb)
60         { get_emitter<T>().add_observer(&obs, std::move(cb)); }
61
62         void replace_observer(EventObserver &, EventObserver &);
63         void remove_observer(EventObserver &);
64
65         template<typename T>
66         void dispatch(const T &) const;
67 };
68
69
70 template<typename T>
71 void EventDispatcher<T>::dispatch(const T &event) const
72 {
73         for(const Handler &h: handlers)
74                 h.callback(event);
75 }
76
77
78 template<typename T>
79 inline unsigned EventBus::get_event_id()
80 {
81         static unsigned id = get_next_id();
82         return id;
83 }
84
85 template<typename T>
86 inline EventDispatcher<T> &EventBus::get_emitter()
87 {
88         unsigned id = get_event_id<T>();
89         if(dispatchers.size()<=id)
90                 dispatchers.resize(id+1);
91
92         Dispatcher &event = dispatchers[id];
93         if(!event.dispatcher)
94         {
95                 event.dispatcher = new EventDispatcher<T>;
96                 event.deleter = [](void *p){ delete static_cast<EventDispatcher<T> *>(p); };
97                 event.remover = [](void *p, EventObserver &o){ static_cast<EventDispatcher<T> *>(p)->remove_observer(&o); };
98         }
99
100         return *static_cast<EventDispatcher<T> *>(event.dispatcher);
101 }
102
103 template<typename T>
104 inline void EventBus::dispatch(const T &event) const
105 {
106         unsigned id = get_event_id<T>();
107         if(id<dispatchers.size() && dispatchers[id].dispatcher)
108                 static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch(event);
109 }
110
111 } // namespace Msp::Game
112
113 #endif