1
+ #pragma once
2
+
3
+ #include < functional>
4
+ #include < concepts>
5
+ #include < mutex>
6
+ #include < ranges>
7
+ #include < unordered_map>
8
+ #include < atomic>
9
+ #include < algorithm>
10
+ #include < tuple>
11
+
12
+ namespace geode ::event::v3 {
13
+ template <typename T>
14
+ concept IsHashable = requires (T a) {
15
+ { std::hash<T>{}(a) } -> std::convertible_to<std::size_t >;
16
+ };
17
+
18
+ template <typename T>
19
+ concept IsEqualityComparable = requires (T a, T b) {
20
+ { a == b } -> std::convertible_to<bool >;
21
+ };
22
+
23
+ template <class PoolType >
24
+ class Pool : protected PoolType {
25
+ public:
26
+ using EventType = typename PoolType::EventType;
27
+ using CallbackType = typename PoolType::CallbackType;
28
+
29
+ private:
30
+ std::mutex mutex;
31
+ std::atomic_size_t recurse;
32
+ std::vector<std::pair<EventType*, CallbackType>> toAdd;
33
+ std::vector<std::pair<EventType*, EventType*>> toMove;
34
+ std::vector<EventType*> toRemove;
35
+
36
+ void catchup () {
37
+ std::lock_guard lock (mutex);
38
+ for (auto & ev : toRemove) {
39
+ this ->PoolType ::removeListener (ev);
40
+ }
41
+ for (auto & [ev, cb] : toAdd) {
42
+ this ->PoolType ::addListener (ev, std::move (cb));
43
+ }
44
+ for (auto & [from, to] : toMove) {
45
+ this ->PoolType ::moveListener (from, to);
46
+ }
47
+ toRemove.clear ();
48
+ toAdd.clear ();
49
+ toMove.clear ();
50
+ }
51
+
52
+ public:
53
+ template <class ... Args>
54
+ void callListeners (EventType* event, Args&&... args) {
55
+ recurse++;
56
+ auto listeners = this ->getListeners (event);
57
+ for (const auto & listener : listeners) {
58
+ listener (std::forward<Args>(args)...);
59
+ }
60
+ recurse--;
61
+
62
+ if (recurse == 0 ) {
63
+ this ->catchup ();
64
+ }
65
+ }
66
+
67
+ void addListener (EventType* event, CallbackType callback) {
68
+ std::lock_guard lock (mutex);
69
+ if (recurse == 0 ) {
70
+ this ->PoolType ::addListener (event, std::move (callback));
71
+ } else {
72
+ toAdd.emplace_back (event, std::move (callback));
73
+ }
74
+ }
75
+
76
+ void removeListener (EventType* event) {
77
+ std::lock_guard lock (mutex);
78
+ if (recurse == 0 ) {
79
+ this ->PoolType ::removeListener (event);
80
+ } else {
81
+ toRemove.emplace_back (event);
82
+ }
83
+ }
84
+
85
+ void moveListener (EventType* from, EventType* to) {
86
+ std::lock_guard lock (mutex);
87
+ if (recurse == 0 ) {
88
+ this ->PoolType ::moveListener (from, to);
89
+ } else {
90
+ toMove.emplace_back (from, to);
91
+ }
92
+ }
93
+ };
94
+
95
+ template <class E , class C >
96
+ class HashablePool {
97
+ protected:
98
+ using EventType = E;
99
+ using CallbackType = std::function<C>;
100
+
101
+ private:
102
+ std::unordered_multimap<std::size_t , CallbackType> listeners;
103
+
104
+ protected:
105
+ auto getListeners (EventType* event) const {
106
+ auto const hash = std::hash<EventType>{}(*event);
107
+ auto [begin, end] = listeners.equal_range (hash);
108
+ auto getSecond = [](auto const & pair) {
109
+ return pair.second ;
110
+ };
111
+ return std::ranges::subrange (begin, end) | std::views::transform (getSecond);
112
+ }
113
+
114
+ void addListener (EventType* event, CallbackType callback) {
115
+ auto const hash = std::hash<EventType>{}(*event);
116
+ listeners.emplace (hash, std::move (callback));
117
+ }
118
+
119
+ void removeListener (EventType* event) {
120
+ auto const hash = std::hash<EventType>{}(*event);
121
+ listeners.erase (hash);
122
+ }
123
+
124
+ void moveListener (EventType* from, EventType* to) {}
125
+ };
126
+
127
+ template <class E , class C >
128
+ class EqualityComparablePool {
129
+ protected:
130
+ using EventType = E;
131
+ using CallbackType = std::function<C>;
132
+
133
+ private:
134
+ std::vector<std::pair<EventType*, CallbackType>> listeners;
135
+
136
+ protected:
137
+ auto getListeners (EventType* event) const {
138
+ auto isEvent = [event](auto const & pair) {
139
+ return pair.first ->operator ==(*event);
140
+ };
141
+ auto getSecond = [](auto const & pair) {
142
+ return pair.second ;
143
+ };
144
+ return std::ranges::filter_view (listeners, isEvent) | std::views::transform (getSecond);
145
+ }
146
+
147
+ void addListener (EventType* event, CallbackType callback) {
148
+ listeners.emplace_back (event, std::move (callback));
149
+ }
150
+
151
+ void removeListener (EventType* event) {
152
+ std::erase_if (listeners, [&event](auto const & pair) {
153
+ return pair.first == event;
154
+ });
155
+ }
156
+
157
+ void moveListener (EventType* from, EventType* to) {
158
+ std::ranges::for_each (listeners, [&](auto & pair) {
159
+ if (pair.first == from) {
160
+ pair.first = to;
161
+ }
162
+ });
163
+ }
164
+ };
165
+
166
+ template <class PoolType >
167
+ class Event {
168
+ public:
169
+ using CallbackType = typename PoolType::CallbackType;
170
+ using EventType = typename PoolType::EventType;
171
+
172
+ protected:
173
+ static inline PoolType pool;
174
+
175
+ EventType* self () {
176
+ return static_cast <EventType*>(this );
177
+ }
178
+
179
+ private:
180
+ bool moved;
181
+
182
+ public:
183
+ template <class ... Args>
184
+ void post (Args&&... args) {
185
+ pool.callListeners (self (), std::forward<Args>(args)...);
186
+ }
187
+
188
+ EventType listen (CallbackType callback) && {
189
+ moved = false ;
190
+ pool.addListener (self (), std::move (callback));
191
+ return std::move (*self ());
192
+ }
193
+
194
+ EventType& listen (CallbackType callback) & {
195
+ moved = false ;
196
+ pool.addListener (self (), std::move (callback));
197
+ return *self ();
198
+ }
199
+
200
+ Event () : moved(true ) {}
201
+ Event (Event const &) = delete ;
202
+ Event (Event&& other) : moved(false ) {
203
+ pool.moveListener (other.self (), self ());
204
+ other.moved = true ;
205
+ }
206
+ Event& operator =(Event const &) = delete ;
207
+ Event& operator =(Event&& other) {
208
+ pool.moveListener (other.self (), self ());
209
+ other.moved = true ;
210
+ return *this ;
211
+ }
212
+ ~Event () {
213
+ if (moved) return ;
214
+ pool.removeListener (self ());
215
+ }
216
+ };
217
+
218
+ namespace event {
219
+ using namespace geode ::event::v3;
220
+ }
221
+
222
+ class TestEvent : public Event <Pool<EqualityComparablePool<TestEvent, void (std::string_view)>>> {
223
+ public:
224
+ std::optional<size_t > key;
225
+
226
+ TestEvent () : key() {}
227
+ TestEvent (size_t key) : key(key) {}
228
+
229
+ bool operator ==(TestEvent const & other) {
230
+ if (!key) {
231
+ return true ;
232
+ }
233
+ if (!other.key ) {
234
+ return false ;
235
+ }
236
+ return *key == *other.key ;
237
+ }
238
+ };
239
+ }
240
+
241
+ namespace std {
242
+ template <class EventType >
243
+ class hash <geode::event::v3::Event<EventType>> {
244
+ public:
245
+ std::size_t operator ()(geode::event::v3::Event<EventType> const & event) const {
246
+ return 0 ;
247
+ }
248
+ };
249
+ }
0 commit comments