hdk-grid 0.0.1
Header-only foundational library for the Hollow Development Kit.
Loading...
Searching...
No Matches
EPS.hpp
Go to the documentation of this file.
1#pragma once
4#include <cstddef>
5#include <cstdint>
6#include <functional>
7#include <list>
8#include <memory>
9#include <mutex>
10#include <unordered_map>
11#include <utility>
12#include <vector>
13namespace hdk::grid::eps {
14 template <typename... ArgTypes> class Conduit {
15 private:
16 struct State;
17
18 public:
19 typedef std::function<void(ArgTypes...)> Callback_t;
20
21 class Tap {
22 public:
23 Tap() = default;
24 Tap(const Tap&) = delete;
25 Tap& operator=(const Tap&) = delete;
26 Tap(Tap&& other) noexcept
27 : state(std::move(other.state)), id(other.id) {
28 other.id = 0;
29 }
30 Tap& operator=(Tap&& other) noexcept {
31 if (this != &other) {
32 Detach();
33 state = std::move(other.state);
34 id = other.id;
35 other.id = 0;
36 }
37 return *this;
38 }
39
40 bool IsActive() const {
41 auto locked = state.lock();
42 if (!locked || id == 0) {
43 return false;
44 }
45 std::lock_guard<std::mutex> guard(locked->mutex);
46 return locked->index.find(id) != locked->index.end();
47 }
48
49 bool Detach() {
50 auto locked = state.lock();
51 if (!locked || id == 0) {
52 return false;
53 }
54 std::lock_guard<std::mutex> guard(locked->mutex);
55 auto it = locked->index.find(id);
56 if (it == locked->index.end()) {
57 id = 0;
58 return false;
59 }
60 locked->entries.erase(it->second);
61 locked->index.erase(it);
62 id = 0;
63 return true;
64 }
65
66 bool Pause() {
67 auto locked = state.lock();
68 if (!locked || id == 0) {
69 return false;
70 }
71 std::lock_guard<std::mutex> guard(locked->mutex);
72 auto it = locked->index.find(id);
73 if (it == locked->index.end()) {
74 id = 0;
75 return false;
76 }
77 it->second->paused = true;
78 return true;
79 }
80
81 bool Resume() {
82 auto locked = state.lock();
83 if (!locked || id == 0) {
84 return false;
85 }
86 std::lock_guard<std::mutex> guard(locked->mutex);
87 auto it = locked->index.find(id);
88 if (it == locked->index.end()) {
89 id = 0;
90 return false;
91 }
92 it->second->paused = false;
93 return true;
94 }
95
96 bool IsPaused() const {
97 auto locked = state.lock();
98 if (!locked || id == 0) {
99 return false;
100 }
101 std::lock_guard<std::mutex> guard(locked->mutex);
102 auto it = locked->index.find(id);
103 if (it == locked->index.end()) {
104 return false;
105 }
106 return it->second->paused;
107 }
108
109 private:
110 friend class Conduit;
111 Tap(std::weak_ptr<State> subscription_state, std::uint64_t subscription_id)
112 : state(std::move(subscription_state)), id(subscription_id) { }
113
114 std::weak_ptr<State> state;
115 std::uint64_t id = 0;
116 };
117
118 virtual ~Conduit() = default;
119
120 virtual Tap Attach(Callback_t callback) {
121 std::lock_guard<std::mutex> guard(state->mutex);
122 const std::uint64_t id = state->next_id++;
123 auto it = state->entries.insert(state->entries.end(), Entry{ id, std::move(callback), false });
124 state->index.emplace(id, it);
125 return Tap(state, id);
126 }
127
128 virtual bool Detach(std::uint64_t id) {
129 if (id == 0) {
130 return false;
131 }
132 std::lock_guard<std::mutex> guard(state->mutex);
133 auto it = state->index.find(id);
134 if (it == state->index.end()) {
135 return false;
136 }
137 state->entries.erase(it->second);
138 state->index.erase(it);
139 return true;
140 }
141
142 virtual bool Pause(std::uint64_t id) {
143 if (id == 0) {
144 return false;
145 }
146 std::lock_guard<std::mutex> guard(state->mutex);
147 auto it = state->index.find(id);
148 if (it == state->index.end()) {
149 return false;
150 }
151 it->second->paused = true;
152 return true;
153 }
154
155 virtual bool Resume(std::uint64_t id) {
156 if (id == 0) {
157 return false;
158 }
159 std::lock_guard<std::mutex> guard(state->mutex);
160 auto it = state->index.find(id);
161 if (it == state->index.end()) {
162 return false;
163 }
164 it->second->paused = false;
165 return true;
166 }
167
168 virtual bool IsPaused(std::uint64_t id) const {
169 if (id == 0) {
170 return false;
171 }
172 std::lock_guard<std::mutex> guard(state->mutex);
173 auto it = state->index.find(id);
174 if (it == state->index.end()) {
175 return false;
176 }
177 return it->second->paused;
178 }
179
180 virtual void Trigger(ArgTypes... args) const {
181 std::vector<Callback_t> callbacks;
182 {
183 std::lock_guard<std::mutex> guard(state->mutex);
184 callbacks.reserve(state->entries.size());
185 for (const auto& entry : state->entries) {
186 if (!entry.paused) {
187 callbacks.push_back(entry.callback);
188 }
189 }
190 }
191 for (auto& callback : callbacks) {
192 callback(args...);
193 }
194 }
195
196 void operator()(ArgTypes... args) const { Trigger(args...); }
197 Tap operator%(Callback_t callback) { return Attach(std::move(callback)); }
198
199 private:
200 struct Entry {
201 std::uint64_t id;
202 Callback_t callback;
203 bool paused;
204 };
205
206 struct State {
207 std::mutex mutex;
208 std::list<Entry> entries;
209 std::unordered_map<std::uint64_t, typename std::list<Entry>::iterator> index;
210 std::uint64_t next_id = 1;
211 };
212
213 std::shared_ptr<State> state = std::make_shared<State>();
214 };
215
216 class TapScope {
217 public:
218 TapScope() = default;
219 TapScope(const TapScope&) = delete;
220 TapScope& operator=(const TapScope&) = delete;
221 TapScope(TapScope&&) = delete;
224
225 template <typename TapT> void Add(TapT tap) {
226 if (!tap.IsActive()) {
227 return;
228 }
229 std::lock_guard<std::mutex> guard(mutex);
230 entries.emplace_back(std::make_unique<TapEntry<TapT>>(std::move(tap)));
231 }
232
233 template <typename TapT> TapScope& operator<<(TapT tap) {
234 Add(std::move(tap));
235 return *this;
236 }
237
238 void DetachAll() {
239 std::vector<std::unique_ptr<TapEntryBase>> to_run;
240 {
241 std::lock_guard<std::mutex> guard(mutex);
242 to_run.swap(entries);
243 }
244 for (auto& tap : to_run) {
245 tap->Detach();
246 }
247 }
248
249 void PauseAll() {
250 std::lock_guard<std::mutex> guard(mutex);
251 for (auto& tap : entries) {
252 tap->Pause();
253 }
254 }
255
256 void ResumeAll() {
257 std::lock_guard<std::mutex> guard(mutex);
258 for (auto& tap : entries) {
259 tap->Resume();
260 }
261 }
262
263 void Clear() {
264 std::lock_guard<std::mutex> guard(mutex);
265 entries.clear();
266 }
267
268 std::size_t Size() const {
269 std::lock_guard<std::mutex> guard(mutex);
270 return entries.size();
271 }
272
273
274 private:
275 struct TapEntryBase {
276 virtual ~TapEntryBase() = default;
277 virtual void Detach() = 0;
278 virtual void Pause() = 0;
279 virtual void Resume() = 0;
280 };
281
282 template <typename TapT> struct TapEntry : TapEntryBase {
283 explicit TapEntry(TapT&& tap_value)
284 : tap(std::move(tap_value)) { }
285
286 void Detach() override { tap.Detach(); }
287 void Pause() override { tap.Pause(); }
288 void Resume() override { tap.Resume(); }
289
290 TapT tap;
291 };
292
293 mutable std::mutex mutex;
294 std::vector<std::unique_ptr<TapEntryBase>> entries;
295
296 };
297} // namespace hdk::grid::eps
Tap & operator=(const Tap &)=delete
Tap & operator=(Tap &&other) noexcept
Definition EPS.hpp:30
Tap(const Tap &)=delete
bool Detach()
Manually unsubscribe from the event.
Definition EPS.hpp:49
bool IsPaused() const
Definition EPS.hpp:96
Tap(Tap &&other) noexcept
Definition EPS.hpp:26
bool IsActive() const
Check if this subscription is still active.
Definition EPS.hpp:40
void operator()(ArgTypes... args) const
Convenience operator for triggering the event.
Definition EPS.hpp:196
std::function< void(ArgTypes...)> Callback_t
Type alias for event callback function.
Definition EPS.hpp:19
virtual bool Pause(std::uint64_t id)
Definition EPS.hpp:142
virtual bool Detach(std::uint64_t id)
Detach by subscription ID.
Definition EPS.hpp:128
Tap operator%(Callback_t callback)
Definition EPS.hpp:197
virtual bool IsPaused(std::uint64_t id) const
Definition EPS.hpp:168
virtual ~Conduit()=default
virtual Tap Attach(Callback_t callback)
Register a callback for this event.
Definition EPS.hpp:120
virtual void Trigger(ArgTypes... args) const
Trigger the event, invoking all registered callbacks.
Definition EPS.hpp:180
virtual bool Resume(std::uint64_t id)
Definition EPS.hpp:155
TapScope(TapScope &&)=delete
TapScope & operator=(TapScope &&)=delete
std::size_t Size() const
Get the number of subscriptions in this group.
Definition EPS.hpp:268
void Add(TapT tap)
Add a subscription to this group.
Definition EPS.hpp:225
TapScope(const TapScope &)=delete
void DetachAll()
Detach all contained subscriptions.
Definition EPS.hpp:238
TapScope & operator=(const TapScope &)=delete
TapScope & operator<<(TapT tap)
Fluent API for adding subscriptions.
Definition EPS.hpp:233
void Clear()
Clear all subscriptions without unsubscribing.
Definition EPS.hpp:263