]> git.tdb.fi Git - libs/game.git/blob - source/game/eventbus.h
Decorate things which constitute the public API of the library
[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 #include <msp/core/noncopyable.h>
7 #include "mspgame_api.h"
8
9 namespace Msp::Game {
10
11 class EventObserver;
12
13 template<typename T>
14 struct EventDispatcher
15 {
16         struct Handler
17         {
18                 EventObserver *observer;
19                 std::function<void(const T &)> callback;
20         };
21
22         std::vector<Handler> handlers;
23
24         void add_observer(EventObserver *obs, std::function<void(const T &)> &&cb)
25         { handlers.emplace_back(obs, std::move(cb)); }
26
27         void remove_observer(EventObserver *obs)
28         { std::erase_if(handlers, [obs](auto &h){ return h.observer==obs; }); }
29
30         void dispatch(const T &) const;
31         void dispatch_to(EventObserver &, const T &) const;
32 };
33
34
35 class MSPGAME_API EventBus: public NonCopyable
36 {
37 private:
38         using DeleteFunc = void(void *);
39         using RemoveFunc = void(void *, EventObserver &);
40
41         struct Dispatcher
42         {
43                 void *dispatcher = nullptr;
44                 DeleteFunc *deleter = nullptr;
45                 RemoveFunc *remover = nullptr;
46         };
47
48         std::vector<Dispatcher> dispatchers;
49
50 public:
51         ~EventBus();
52
53 private:
54         static unsigned get_next_id();
55
56 public:
57         template<typename T>
58         static unsigned get_event_id();
59
60 private:
61         template<typename T>
62         EventDispatcher<T> &get_dispatcher();
63
64 public:
65         template<typename T>
66         void add_observer(EventObserver &obs, std::function<void(const T &)> cb)
67         { get_dispatcher<T>().add_observer(&obs, std::move(cb)); }
68
69         void replace_observer(EventObserver &, EventObserver &);
70         void remove_observer(EventObserver &);
71
72         template<typename T>
73         void dispatch(const T &) const;
74
75         template<typename T>
76         void dispatch_to(EventObserver &, const T &) const;
77 };
78
79
80 template<typename T>
81 void EventDispatcher<T>::dispatch(const T &event) const
82 {
83         for(const Handler &h: handlers)
84                 h.callback(event);
85 }
86
87 template<typename T>
88 void EventDispatcher<T>::dispatch_to(EventObserver &obs, const T &event) const
89 {
90         for(const Handler &h: handlers)
91                 if(h.observer==&obs)
92                         h.callback(event);
93 }
94
95
96 template<typename T>
97 inline unsigned EventBus::get_event_id()
98 {
99         static unsigned id = get_next_id();
100         return id;
101 }
102
103 template<typename T>
104 inline EventDispatcher<T> &EventBus::get_dispatcher()
105 {
106         unsigned id = get_event_id<T>();
107         if(dispatchers.size()<=id)
108                 dispatchers.resize(id+1);
109
110         Dispatcher &event = dispatchers[id];
111         if(!event.dispatcher)
112         {
113                 event.dispatcher = new EventDispatcher<T>;
114                 event.deleter = [](void *p){ delete static_cast<EventDispatcher<T> *>(p); };
115                 event.remover = [](void *p, EventObserver &o){ static_cast<EventDispatcher<T> *>(p)->remove_observer(&o); };
116         }
117
118         return *static_cast<EventDispatcher<T> *>(event.dispatcher);
119 }
120
121 template<typename T>
122 inline void EventBus::dispatch(const T &event) const
123 {
124         unsigned id = get_event_id<T>();
125         if(id<dispatchers.size() && dispatchers[id].dispatcher)
126                 static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch(event);
127 }
128
129 template<typename T>
130 inline void EventBus::dispatch_to(EventObserver &obs, const T &event) const
131 {
132         unsigned id = get_event_id<T>();
133         if(id<dispatchers.size() && dispatchers[id].dispatcher)
134                 static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch_to(obs, event);
135 }
136
137 } // namespace Msp::Game
138
139 #endif