Skip to content

Commit d85211a

Browse files
committed
initial crtp impl, works but doesnt work between mods, will try to make it not crtp
1 parent 01ee436 commit d85211a

File tree

1 file changed

+249
-0
lines changed

1 file changed

+249
-0
lines changed
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
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

Comments
 (0)