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