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