neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
i_event.hpp
Go to the documentation of this file.
1// i_event.hpp
2/*
3 * Copyright (c) 2021 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#pragma once
37
38#include <neolib/neolib.hpp>
39#include <functional>
40#include <neolib/core/mutex.hpp>
43
44namespace neolib
45{
47 {
48 static switchable_mutex sMutex;
49 return sMutex;
50 }
51
52 namespace event_system
53 {
54 inline void set_single_threaded()
55 {
57 }
58
63 }
64
65 class i_async_task;
66
68 {
69 public:
70 virtual void register_with_task(i_async_task& aTask) = 0;
71 virtual bool pump_events() = 0;
72 };
73
74 template <typename... Args>
75 class i_event;
76
78 {
79 public:
81 public:
82 virtual void remove() = 0;
83 };
84
85 template <typename... Args>
86 class i_slot : public i_slot_base
87 {
88 public:
90 public:
91 virtual i_event<Args...> const& event() const = 0;
92 virtual void call(Args... aArgs) const = 0;
93 virtual std::thread::id call_thread() const = 0;
94 virtual bool call_in_emitter_thread() const = 0;
95 virtual void set_call_in_emitter_thread(bool aCallInEmitterThread) = 0;
96 virtual bool stateless() const = 0;
97 virtual void set_stateless(bool aStateless) = 0;
98 };
99
107
108 enum class trigger_result
109 {
110 Unknown,
113 };
114
115 inline bool event_consumed(trigger_result aTriggerResult)
116 {
117 switch (aTriggerResult)
118 {
121 default:
122 return false;
124 return true;
125 }
126 }
127
128 template <typename... Args>
129 class slot;
130
131 template <typename... Args>
133 {
135
137 {
139 return std::move(*this);
140 }
141
143 {
144 slot->set_stateless(true);
145 return std::move(*this);
146 }
147 };
148
149 template <typename... Args>
150 class i_event : public i_lifetime
151 {
152 typedef i_event<Args...> self_type;
153 public:
155 public:
156 virtual ~i_event() = default;
157 public:
159 virtual void set_trigger_type(neolib::trigger_type aTriggerType) = 0;
160 public:
161 virtual trigger_result sync_trigger(Args... aArgs) const = 0;
162 virtual void async_trigger(Args... aArgs) const = 0;
163 virtual void accept() const = 0;
164 public:
165 virtual bool has_slots() const = 0;
166 virtual void add_slot(i_slot<Args...>& aSlot) const = 0;
167 virtual void remove_slot(i_slot<Args...>& aSlot) const = 0;
168 public:
169 trigger_result trigger(Args... aArgs) const
170 {
171 switch (trigger_type())
172 {
175 return sync_trigger(aArgs...);
178 async_trigger(aArgs...);
180 default:
182 }
183 }
184 slot_proxy<Args...> operator()(std::function<void(Args...)> const& aCallback) const
185 {
186 return slot_proxy<Args...>{ make_ref<slot<Args...>>(*this, aCallback) };
187 }
188 };
189
190 template <typename... Args>
191 class slot : public reference_counted<lifetime<i_slot<Args...>>>
192 {
193 typedef slot<Args...> self_type;
194 public:
195 slot(i_event<Args...> const& aEvent, std::function<void(Args...)> const& aCallable) :
196 iEvent{ aEvent },
197 iEventDestroyed{ aEvent },
198 iCallable{ aCallable },
199 iCallThread{ std::this_thread::get_id() }
200 {
201 event().add_slot(*this);
202 }
204 {
205 remove();
206 }
207 public:
208 void remove() final
209 {
210 if (!iEventDestroyed)
211 event().remove_slot(*this);
212 }
213 i_event<Args...> const& event() const final
214 {
215 return iEvent;
216 }
217 void call(Args... aArgs) const final
218 {
219 iCallable(aArgs...);
220 }
221 std::thread::id call_thread() const final
222 {
224 return std::this_thread::get_id();
225 return *iCallThread;
226 }
227 bool call_in_emitter_thread() const final
228 {
229 return iCallThread == std::nullopt;
230 }
231 void set_call_in_emitter_thread(bool aCallInEmitterThread) final
232 {
233 if (aCallInEmitterThread)
234 iCallThread = std::nullopt;
235 else
236 iCallThread = std::this_thread::get_id();
237 }
238 bool stateless() const final
239 {
240 return iStateless;
241 }
242 void set_stateless(bool aStateless) final
243 {
244 iStateless = aStateless;
245 }
246 private:
247 i_event<Args...> const& iEvent;
248 destroyed_flag iEventDestroyed;
249 std::function<void(Args...)> iCallable;
250 std::optional<std::thread::id> iCallThread;
251 bool iStateless = false;
252 };
253
254 class sink
255 {
256 public:
258 {
259 }
260 template <typename... Args>
262 {
263 *this = std::move(aSlot);
264 }
266 {
267 clear();
268 }
269 public:
270 bool empty() const
271 {
272 return iSlots.empty();
273 }
274 public:
275 template <typename... Args>
277 {
278 std::scoped_lock lock{ event_mutex() };
279 clear();
280 iSlots.push_back(aSlot.slot);
281 return std::move(aSlot);
282 }
283 template <typename... Args>
285 {
286 std::scoped_lock lock{ event_mutex() };
287 iSlots.push_back(aSlot.slot);
288 return std::move(aSlot);
289 }
290 void clear()
291 {
292 std::scoped_lock lock{ event_mutex() };
293 for (auto& slot : iSlots)
294 slot->remove();
295 iSlots.clear();
296 }
297 private:
298 std::vector<ref_ptr<i_slot_base>> iSlots;
299 };
300
301 #define detail_event_subscribe( declName, ... ) \
302 neolib::slot_proxy<__VA_ARGS__> declName(const std::function<void(__VA_ARGS__)>& aCallback) const { return declName()(aCallback); }\
303 neolib::slot_proxy<__VA_ARGS__> declName(const std::function<void(__VA_ARGS__)>& aCallback) { return declName()(aCallback); }
304
305 #define declare_event( declName, ... ) \
306 virtual const neolib::i_event<__VA_ARGS__>& ev_##declName() const = 0;\
307 virtual neolib::i_event<__VA_ARGS__>& ev_##declName() = 0;\
308 const neolib::i_event<__VA_ARGS__>& declName() const { return ev_##declName(); }\
309 neolib::i_event<__VA_ARGS__>& declName() { return ev_##declName(); }\
310 detail_event_subscribe(declName, __VA_ARGS__)
311}
virtual bool pump_events()=0
virtual void register_with_task(i_async_task &aTask)=0
virtual trigger_result sync_trigger(Args... aArgs) const =0
virtual void remove_slot(i_slot< Args... > &aSlot) const =0
virtual neolib::trigger_type trigger_type() const =0
virtual void add_slot(i_slot< Args... > &aSlot) const =0
virtual void set_trigger_type(neolib::trigger_type aTriggerType)=0
virtual void accept() const =0
self_type abstract_type
Definition i_event.hpp:154
slot_proxy< Args... > operator()(std::function< void(Args...)> const &aCallback) const
Definition i_event.hpp:184
virtual ~i_event()=default
virtual void async_trigger(Args... aArgs) const =0
trigger_result trigger(Args... aArgs) const
Definition i_event.hpp:169
virtual bool has_slots() const =0
i_slot_base abstract_type
Definition i_event.hpp:80
virtual void remove()=0
virtual void set_call_in_emitter_thread(bool aCallInEmitterThread)=0
virtual i_event< Args... > const & event() const =0
virtual bool call_in_emitter_thread() const =0
virtual void call(Args... aArgs) const =0
virtual bool stateless() const =0
virtual void set_stateless(bool aStateless)=0
virtual std::thread::id call_thread() const =0
i_slot abstract_type
Definition i_event.hpp:89
slot_proxy< Args... > && operator+=(slot_proxy< Args... > &&aSlot)
Definition i_event.hpp:284
sink(slot_proxy< Args... > &&aSlot)
Definition i_event.hpp:261
slot_proxy< Args... > && operator=(slot_proxy< Args... > &&aSlot)
Definition i_event.hpp:276
bool empty() const
Definition i_event.hpp:270
void clear()
Definition i_event.hpp:290
void remove() final
Definition i_event.hpp:208
void set_stateless(bool aStateless) final
Definition i_event.hpp:242
bool stateless() const final
Definition i_event.hpp:238
void call(Args... aArgs) const final
Definition i_event.hpp:217
slot(i_event< Args... > const &aEvent, std::function< void(Args...)> const &aCallable)
Definition i_event.hpp:195
void set_call_in_emitter_thread(bool aCallInEmitterThread) final
Definition i_event.hpp:231
bool call_in_emitter_thread() const final
Definition i_event.hpp:227
i_event< Args... > const & event() const final
Definition i_event.hpp:213
std::thread::id call_thread() const final
Definition i_event.hpp:221
void set_multi_threaded_spinlock()
Definition mutex.hpp:224
void set_multi_threaded()
Definition i_event.hpp:59
void set_single_threaded()
Definition i_event.hpp:54
switchable_mutex & event_mutex()
Definition i_event.hpp:46
ref_ptr< ConcreteType > make_ref(Args &&... args)
bool event_consumed(trigger_result aTriggerResult)
Definition i_event.hpp:115
trigger_result
Definition i_event.hpp:109
Definition plf_hive.h:79
ref_ptr< neolib::slot< Args... > > slot
Definition i_event.hpp:134
slot_proxy && operator~()
Definition i_event.hpp:136
slot_proxy && operator!()
Definition i_event.hpp:142