Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
signal.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <tuple>
5#include <typeinfo>
6#include <utility>
7
8#include "gaia/cnt/darray.h"
9#include "gaia/core/func.h"
10#include "gaia/core/utility.h"
11
12namespace gaia {
13 namespace util {
15 namespace detail {
16 template <typename Ret, typename... Args>
17 auto func_ptr(Ret (*)(Args...)) -> Ret (*)(Args...);
18
19 template <typename Ret, typename Type, typename... Args, typename Other>
20 auto func_ptr(Ret (*)(Type, Args...), Other&&) -> Ret (*)(Args...);
21
22 template <typename Class, typename Ret, typename... Args, typename... Other>
23 auto func_ptr(Ret (Class::*)(Args...), Other&&...) -> Ret (*)(Args...);
24
25 template <typename Class, typename Ret, typename... Args, typename... Other>
26 auto func_ptr(Ret (Class::*)(Args...) const, Other&&...) -> Ret (*)(Args...);
27
28 template <typename Class, typename Type, typename... Other>
29 auto func_ptr(Type Class::*, Other&&...) -> Type (*)();
30
31 template <typename... Type>
32 using func_ptr_t = decltype(detail::func_ptr(std::declval<Type>()...));
33
34 template <typename... Class, typename Ret, typename... Args>
35 GAIA_NODISCARD constexpr auto index_sequence_for(Ret (*)(Args...)) {
36 return std::index_sequence_for<Class..., Args...>{};
37 }
38
39 // Wraps a function or a member of a specified type
40 template <auto>
41 struct connect_arg_t {};
42 // Function wrapper
43 template <auto Func>
44 inline constexpr connect_arg_t<Func> connect_arg{};
45 } // namespace detail
47
48 template <typename>
49 class delegate;
50 template <typename Function>
51 class signal;
52 template <typename Function>
53 class sink;
54
55 //------------------------------------------------------------------------------
56 // delegate
57 //------------------------------------------------------------------------------
58
66 template <typename Ret, typename... Args>
67 class delegate<Ret(Args...)> {
68 using func_type = Ret(const void*, Args...);
69
70 func_type* m_fnc = nullptr;
71 const void* m_ctx = nullptr;
72
73 public:
74 delegate() noexcept = default;
75
78 template <auto FuncToBind>
79 delegate(detail::connect_arg_t<FuncToBind>) noexcept {
80 bind<FuncToBind>();
81 }
82
87 template <auto FuncToBind, typename Type>
88 delegate(detail::connect_arg_t<FuncToBind>, Type&& value_or_instance) noexcept {
89 bind<FuncToBind>(GAIA_FWD(value_or_instance));
90 }
91
95 delegate(func_type* func, const void* data = nullptr) noexcept {
96 bind(func, data);
97 }
98
101 template <auto FuncToBind>
102 void bind() noexcept {
103 m_ctx = nullptr;
104
105 if constexpr (std::is_invocable_r_v<Ret, decltype(FuncToBind), Args...>) {
106 m_fnc = [](const void*, Args... args) {
107 return Ret(invoke(FuncToBind, GAIA_FWD(args)...));
108 };
109 } else if constexpr (std::is_member_pointer_v<decltype(FuncToBind)>) {
110 m_fnc = wrap<FuncToBind>(detail::index_sequence_for<std::tuple_element_t<0, std::tuple<Args...>>>(
111 detail::func_ptr_t<decltype(FuncToBind)>{}));
112 } else {
113 m_fnc = wrap<FuncToBind>(detail::index_sequence_for(detail::func_ptr_t<decltype(FuncToBind)>{}));
114 }
115 }
116
123 template <auto FuncToBind, typename Type>
124 void bind(Type& value_or_instance) noexcept {
125 m_ctx = &value_or_instance;
126
127 if constexpr (std::is_invocable_r_v<Ret, decltype(FuncToBind), Type&, Args...>) {
128 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
129 m_fnc = [](const void* ctx, Args... args) {
130 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
131 return Ret(invoke(FuncToBind, *pType, GAIA_FWD(args)...));
132 };
133 } else {
134 m_fnc = wrap<FuncToBind>(
135 value_or_instance, detail::index_sequence_for(detail::func_ptr_t<decltype(FuncToBind), Type>{}));
136 }
137 }
138
143 template <auto FuncToBind, typename Type>
144 void bind(Type* value_or_instance) noexcept {
145 m_ctx = value_or_instance;
146
147 if constexpr (std::is_invocable_r_v<Ret, decltype(FuncToBind), Type*, Args...>) {
148 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
149 m_fnc = [](const void* ctx, Args... args) {
150 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
151 return Ret(invoke(FuncToBind, pType, GAIA_FWD(args)...));
152 };
153 } else {
154 m_fnc = wrap<FuncToBind>(
155 value_or_instance, detail::index_sequence_for(detail::func_ptr_t<decltype(FuncToBind), Type>{}));
156 }
157 }
158
163 void bind(func_type* function, const void* context = nullptr) noexcept {
164 m_fnc = function;
165 m_ctx = context;
166 }
167
169 void reset() noexcept {
170 m_fnc = nullptr;
171 m_ctx = nullptr;
172 }
173
176 GAIA_NODISCARD bool has_func() const noexcept {
177 return m_fnc != nullptr;
178 }
179
182 GAIA_NODISCARD const void* instance() const noexcept {
183 return m_ctx;
184 }
185
189 Ret operator()(Args... args) const {
190 GAIA_ASSERT(m_fnc != nullptr && "Trying to call an unbound delegate!");
191 return m_fnc(m_ctx, GAIA_FWD(args)...);
192 }
193
196 GAIA_NODISCARD explicit operator bool() const noexcept {
197 // There's no way to set just m_ctx so it's enough to test m_fnc
198 return m_fnc != nullptr;
199 }
200
204 GAIA_NODISCARD bool operator==(const delegate<Ret(Args...)>& other) const noexcept {
205 return m_fnc == other.m_fnc && m_ctx == other.m_ctx;
206 }
207
211 GAIA_NODISCARD bool operator!=(const delegate<Ret(Args...)>& other) const noexcept {
212 return !operator==(other);
213 }
214
215 private:
216 template <auto FuncToBind, std::size_t... Index>
217 GAIA_NODISCARD auto wrap(std::index_sequence<Index...>) noexcept {
218 return [](const void*, Args... args) {
219 [[maybe_unused]] const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
220 return Ret(invoke(FuncToBind, GAIA_FWD(std::get<Index>(argsFwd))...));
221 };
222 }
223
224 template <auto FuncToBind, typename Type, std::size_t... Index>
225 GAIA_NODISCARD auto wrap(Type&, std::index_sequence<Index...>) noexcept {
226 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
227 return [](const void* ctx, Args... args) {
228 [[maybe_unused]] const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
229 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
230 return Ret(invoke(FuncToBind, *pType, GAIA_FWD(std::get<Index>(argsFwd))...));
231 };
232 }
233
234 template <auto FuncToBind, typename Type, std::size_t... Index>
235 GAIA_NODISCARD auto wrap(Type*, std::index_sequence<Index...>) noexcept {
236 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
237 return [](const void* ctx, Args... args) {
238 [[maybe_unused]] const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
239 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
240 return Ret(invoke(FuncToBind, pType, GAIA_FWD(std::get<Index>(argsFwd))...));
241 };
242 }
243 };
244
251 template <typename Ret, typename... Args>
252 GAIA_NODISCARD bool operator==(const delegate<Ret(Args...)>& lhs, const delegate<Ret(Args...)>& rhs) noexcept {
253 return lhs == rhs;
254 }
255
262 template <typename Ret, typename... Args>
263 GAIA_NODISCARD bool operator!=(const delegate<Ret(Args...)>& lhs, const delegate<Ret(Args...)>& rhs) noexcept {
264 return lhs != rhs;
265 }
266
270 template <auto Func>
271 delegate(detail::connect_arg_t<Func> tag) noexcept
272 -> delegate<std::remove_pointer_t<detail::func_ptr_t<decltype(Func)>>>;
273
279 template <auto Func, typename Type>
280 delegate(detail::connect_arg_t<Func> tag, Type&& value_or_instance) noexcept
281 -> delegate<std::remove_pointer_t<detail::func_ptr_t<decltype(Func), Type>>>;
282
288 template <typename Ret, typename... Args>
289 delegate(Ret (*func)(const void*, Args...), const void* data = nullptr) noexcept -> delegate<Ret(Args...)>;
290
291 //------------------------------------------------------------------------------
292 // signal
293 //------------------------------------------------------------------------------
294
295 template <typename Ret, typename... Args>
296 class signal<Ret(Args...)>;
297
299 namespace detail {
300 template <typename Ret, typename... Args>
301 using container = cnt::darray<delegate<Ret(Args...)>>;
302 } // namespace detail
304
309 template <typename Ret, typename... Args>
310 class signal<Ret(Args...)> {
311 friend class sink<Ret(Args...)>;
312
313 private:
315 detail::container<Ret, Args...> m_listeners;
316
317 public:
319 using size_type = typename detail::container<Ret, Args...>::size_type;
321 using sink_type = sink<Ret(Args...)>;
322
325 GAIA_NODISCARD size_type size() const noexcept {
326 return m_listeners.size();
327 }
328
331 GAIA_NODISCARD bool empty() const noexcept {
332 return m_listeners.empty();
333 }
334
337 void emit(Args... args) {
338 for (auto&& call: std::as_const(m_listeners))
339 call(args...);
340 }
341 };
342
343 //------------------------------------------------------------------------------
344 // Sink
345 //------------------------------------------------------------------------------
346
354 template <typename Ret, typename... Args>
355 class sink<Ret(Args...)> {
356 using signal_type = signal<Ret(Args...)>;
357 using func_type = Ret(const void*, Args...);
358
359 signal_type* m_s;
360
361 public:
364 sink(signal<Ret(Args...)>& ref) noexcept: m_s{&ref} {}
365
369 void move_from(sink& other) {
370 m_s->m_listeners.reserve(m_s->m_listeners.size() + other.m_s->m_listeners.size());
371 for (auto&& listener: other.m_s->m_listeners)
372 m_s->m_listeners.push_back(GAIA_MOV(listener));
373
374 other.reset();
375 }
376
380 template <auto FuncToBind>
381 void bind() {
382 delegate<Ret(Args...)> call{};
383 call.template bind<FuncToBind>();
384 bind_internal(call);
385 }
386
393 template <auto FuncToBind, typename Type>
394 void bind(Type& value_or_instance) {
395 delegate<Ret(Args...)> call{};
396 call.template bind<FuncToBind>(value_or_instance);
397 bind_internal(call);
398 }
399
404 void bind(func_type* func, const void* data = nullptr) {
405 if (func == nullptr && data == nullptr)
406 return;
407
408 delegate<Ret(Args...)> call{};
409 call.bind(func, data);
410 bind_internal(call);
411 }
412
415 template <auto FuncToUnbind>
416 void unbind() {
417 delegate<Ret(Args...)> call{};
418 call.template bind<FuncToUnbind>();
419
420 m_s->m_listeners.retain([&](const auto& l) {
421 return l != call;
422 });
423 }
424
429 template <auto FuncToUnbind, typename Type>
430 void unbind(Type& value_or_instance) {
431 delegate<Ret(Args...)> call{};
432 call.template bind<FuncToUnbind>(value_or_instance);
433
434 auto& listeners = m_s->m_listeners;
435 for (uint32_t i = 0; i < listeners.size();) {
436 if (listeners[i] != call) {
437 ++i;
438 continue;
439 }
440
441 core::swap_erase_unsafe(listeners, i);
442 }
443 }
444
448 template <typename Type>
449 void unbind(Type* value_or_instance) {
450 if (!value_or_instance)
451 return;
452
453 auto& listeners = m_s->m_listeners;
454 for (uint32_t i = 0; i < listeners.size();) {
455 if (listeners[i].instance() != value_or_instance) {
456 ++i;
457 continue;
458 }
459
460 core::swap_erase_unsafe(listeners, i);
461 }
462 }
463
467 template <typename Type>
468 void unbind(Type& value_or_instance) {
469 unbind(&value_or_instance);
470 }
471
473 void reset() {
474 m_s->m_listeners.clear();
475 }
476
477 private:
478 void bind_internal(const delegate<Ret(Args...)>& call) {
479 if (!core::has(m_s->m_listeners, call))
480 m_s->m_listeners.push_back(GAIA_MOV(call));
481 }
482 };
483
488 template <typename Ret, typename... Args>
489 sink(signal<Ret(Args...)>& ref) noexcept -> sink<Ret(Args...)>;
490 } // namespace util
491} // namespace gaia
delegate(detail::connect_arg_t< FuncToBind >, Type &&value_or_instance) noexcept
Constructs a delegate by binding a free function with context or a bound member to it.
Definition signal.h:88
void bind(func_type *function, const void *context=nullptr) noexcept
Binds an user defined function with optional context to a delegate. The context is returned as the fi...
Definition signal.h:163
void reset() noexcept
Resets a delegate. After a reset, a delegate cannot be invoked anymore.
Definition signal.h:169
GAIA_NODISCARD bool has_func() const noexcept
Returns the functor pointer linked to a delegate, if any.
Definition signal.h:176
void bind() noexcept
Binds a free function or an unbound member to a delegate.
Definition signal.h:102
void bind(Type &value_or_instance) noexcept
Binds a free function with context or a bound member to a delegate. When used to bind a ree function ...
Definition signal.h:124
Ret operator()(Args... args) const
The delegate invokes the underlying function and returns the result.
Definition signal.h:189
delegate(func_type *func, const void *data=nullptr) noexcept
Constructs a delegate by binding a function with optional context to it.
Definition signal.h:95
GAIA_NODISCARD bool operator!=(const delegate< Ret(Args...)> &other) const noexcept
Compares the contents of two delegates.
Definition signal.h:211
GAIA_NODISCARD bool operator==(const delegate< Ret(Args...)> &other) const noexcept
Compares the contents of two delegates.
Definition signal.h:204
GAIA_NODISCARD const void * instance() const noexcept
Returns the instance or the context linked to a delegate, if any.
Definition signal.h:182
delegate(detail::connect_arg_t< FuncToBind >) noexcept
Constructs a delegate by binding a free function or an unbound member to it.
Definition signal.h:79
void bind(Type *value_or_instance) noexcept
Binds a free function with context or a bound member to a delegate.
Definition signal.h:144
Definition signal.h:49
Signal is a container of listener which it can notify. It works directly with references to classes a...
Definition signal.h:310
GAIA_NODISCARD size_type size() const noexcept
Number of listeners connected to the signal.
Definition signal.h:325
typename detail::container< Ret, Args... >::size_type size_type
Type used to represent listener counts.
Definition signal.h:319
GAIA_NODISCARD bool empty() const noexcept
Check is there is any listener bound to the signal.
Definition signal.h:331
void emit(Args... args)
Signals all listeners.
Definition signal.h:337
Definition signal.h:51
Sink is a helper class used to bind listeners to signals. The separation between signal and sink make...
Definition signal.h:355
sink(signal< Ret(Args...)> &ref) noexcept
Constructs a sink that is allowed to modify a given signal.
Definition signal.h:364
void move_from(sink &other)
Moves signals from another sink to this one. Result is stored in this object. The sink we merge from ...
Definition signal.h:369
void bind()
Binds a free function or an unbound member to a signal. The signal handler performs checks to avoid m...
Definition signal.h:381
void bind(Type &value_or_instance)
Binds a free function with context or a bound member to a signal. When used to bind a free function w...
Definition signal.h:394
void unbind(Type &value_or_instance)
Unbinds a free function with context or bound members from a signal.
Definition signal.h:468
void unbind(Type *value_or_instance)
Unbinds a free function with context or bound members from a signal.
Definition signal.h:449
void unbind(Type &value_or_instance)
Unbinds a free function with context or a bound member from a signal.
Definition signal.h:430
void bind(func_type *func, const void *data=nullptr)
Binds an user defined function with optional context to a signal. The context is returned as the firs...
Definition signal.h:404
void reset()
Unbinds all listeners from a signal.
Definition signal.h:473
void unbind()
Unbinds a free function or an unbound member from a signal.
Definition signal.h:416
Definition signal.h:53