Gaia-ECS v0.9.3
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 {
14 namespace detail {
15 template <typename Ret, typename... Args>
16 auto func_ptr(Ret (*)(Args...)) -> Ret (*)(Args...);
17
18 template <typename Ret, typename Type, typename... Args, typename Other>
19 auto func_ptr(Ret (*)(Type, Args...), Other&&) -> Ret (*)(Args...);
20
21 template <typename Class, typename Ret, typename... Args, typename... Other>
22 auto func_ptr(Ret (Class::*)(Args...), Other&&...) -> Ret (*)(Args...);
23
24 template <typename Class, typename Ret, typename... Args, typename... Other>
25 auto func_ptr(Ret (Class::*)(Args...) const, Other&&...) -> Ret (*)(Args...);
26
27 template <typename Class, typename Type, typename... Other>
28 auto func_ptr(Type Class::*, Other&&...) -> Type (*)();
29
30 template <typename... Type>
31 using func_ptr_t = decltype(detail::func_ptr(std::declval<Type>()...));
32
33 template <typename... Class, typename Ret, typename... Args>
34 GAIA_NODISCARD constexpr auto index_sequence_for(Ret (*)(Args...)) {
35 return std::index_sequence_for<Class..., Args...>{};
36 }
37
38 // Wraps a function or a member of a specified type
39 template <auto>
40 struct connect_arg_t {};
41 // Function wrapper
42 template <auto Func>
43 inline constexpr connect_arg_t<Func> connect_arg{};
44 } // namespace detail
45
46 template <typename>
47 class delegate;
48 template <typename Function>
49 class signal;
50 template <typename Function>
51 class sink;
52
53 //------------------------------------------------------------------------------
54 // delegate
55 //------------------------------------------------------------------------------
56
64 template <typename Ret, typename... Args>
65 class delegate<Ret(Args...)> {
66 using func_type = Ret(const void*, Args...);
67
68 func_type* m_fnc = nullptr;
69 const void* m_ctx = nullptr;
70
71 public:
72 delegate() noexcept = default;
73
76 template <auto FuncToBind>
78 bind<FuncToBind>();
79 }
80
85 template <auto FuncToBind, typename Type>
86 delegate(detail::connect_arg_t<FuncToBind>, Type&& value_or_instance) noexcept {
87 bind<FuncToBind>(GAIA_FWD(value_or_instance));
88 }
89
93 delegate(func_type* func, const void* data = nullptr) noexcept {
94 bind(func, data);
95 }
96
99 template <auto FuncToBind>
100 void bind() noexcept {
101 m_ctx = nullptr;
102
103 if constexpr (std::is_invocable_r_v<Ret, decltype(FuncToBind), Args...>) {
104 m_fnc = [](const void*, Args... args) {
105 return Ret(invoke(FuncToBind, GAIA_FWD(args)...));
106 };
107 } else if constexpr (std::is_member_pointer_v<decltype(FuncToBind)>) {
108 m_fnc = wrap<FuncToBind>(detail::index_sequence_for<std::tuple_element_t<0, std::tuple<Args...>>>(
109 detail::func_ptr_t<decltype(FuncToBind)>{}));
110 } else {
111 m_fnc = wrap<FuncToBind>(detail::index_sequence_for(detail::func_ptr_t<decltype(FuncToBind)>{}));
112 }
113 }
114
121 template <auto FuncToBind, typename Type>
122 void bind(Type& value_or_instance) noexcept {
123 m_ctx = &value_or_instance;
124
125 if constexpr (std::is_invocable_r_v<Ret, decltype(FuncToBind), Type&, Args...>) {
126 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
127 m_fnc = [](const void* ctx, Args... args) {
128 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
129 return Ret(invoke(FuncToBind, *pType, GAIA_FWD(args)...));
130 };
131 } else {
132 m_fnc = wrap<FuncToBind>(
133 value_or_instance, detail::index_sequence_for(detail::func_ptr_t<decltype(FuncToBind), Type>{}));
134 }
135 }
136
141 template <auto FuncToBind, typename Type>
142 void bind(Type* value_or_instance) noexcept {
143 m_ctx = value_or_instance;
144
145 if constexpr (std::is_invocable_r_v<Ret, decltype(FuncToBind), Type*, Args...>) {
146 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
147 m_fnc = [](const void* ctx, Args... args) {
148 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
149 return Ret(invoke(FuncToBind, pType, GAIA_FWD(args)...));
150 };
151 } else {
152 m_fnc = wrap<FuncToBind>(
153 value_or_instance, detail::index_sequence_for(detail::func_ptr_t<decltype(FuncToBind), Type>{}));
154 }
155 }
156
161 void bind(func_type* function, const void* context = nullptr) noexcept {
162 m_fnc = function;
163 m_ctx = context;
164 }
165
167 void reset() noexcept {
168 m_fnc = nullptr;
169 m_ctx = nullptr;
170 }
171
174 GAIA_NODISCARD bool has_func() const noexcept {
175 return m_fnc != nullptr;
176 }
177
180 GAIA_NODISCARD const void* instance() const noexcept {
181 return m_ctx;
182 }
183
187 Ret operator()(Args... args) const {
188 GAIA_ASSERT(m_fnc != nullptr && "Trying to call an unbound delegate!");
189 return m_fnc(m_ctx, GAIA_FWD(args)...);
190 }
191
194 GAIA_NODISCARD explicit operator bool() const noexcept {
195 // There's no way to set just m_ctx so it's enough to test m_fnc
196 return m_fnc != nullptr;
197 }
198
202 GAIA_NODISCARD bool operator==(const delegate<Ret(Args...)>& other) const noexcept {
203 return m_fnc == other.m_fnc && m_ctx == other.m_ctx;
204 }
205
209 GAIA_NODISCARD bool operator!=(const delegate<Ret(Args...)>& other) const noexcept {
210 return !operator==(other);
211 }
212
213 private:
214 template <auto FuncToBind, std::size_t... Index>
215 GAIA_NODISCARD auto wrap(std::index_sequence<Index...>) noexcept {
216 return [](const void*, Args... args) {
217 [[maybe_unused]] const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
218 return Ret(invoke(FuncToBind, GAIA_FWD(std::get<Index>(argsFwd))...));
219 };
220 }
221
222 template <auto FuncToBind, typename Type, std::size_t... Index>
223 GAIA_NODISCARD auto wrap(Type&, std::index_sequence<Index...>) noexcept {
224 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
225 return [](const void* ctx, Args... args) {
226 [[maybe_unused]] const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
227 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
228 return Ret(invoke(FuncToBind, *pType, GAIA_FWD(std::get<Index>(argsFwd))...));
229 };
230 }
231
232 template <auto FuncToBind, typename Type, std::size_t... Index>
233 GAIA_NODISCARD auto wrap(Type*, std::index_sequence<Index...>) noexcept {
234 using const_or_not_type = std::conditional_t<std::is_const_v<Type>, const void*, void*>;
235 return [](const void* ctx, Args... args) {
236 [[maybe_unused]] const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
237 auto pType = static_cast<Type*>(const_cast<const_or_not_type>(ctx));
238 return Ret(invoke(FuncToBind, pType, GAIA_FWD(std::get<Index>(argsFwd))...));
239 };
240 }
241 };
242
249 template <typename Ret, typename... Args>
250 GAIA_NODISCARD bool operator==(const delegate<Ret(Args...)>& lhs, const delegate<Ret(Args...)>& rhs) noexcept {
251 return lhs == rhs;
252 }
253
260 template <typename Ret, typename... Args>
261 GAIA_NODISCARD bool operator!=(const delegate<Ret(Args...)>& lhs, const delegate<Ret(Args...)>& rhs) noexcept {
262 return lhs != rhs;
263 }
264
265 template <auto Func>
266 delegate(detail::connect_arg_t<Func>) noexcept
267 -> delegate<std::remove_pointer_t<detail::func_ptr_t<decltype(Func)>>>;
268
269 template <auto Func, typename Type>
270 delegate(detail::connect_arg_t<Func>, Type&&) noexcept
271 -> delegate<std::remove_pointer_t<detail::func_ptr_t<decltype(Func), Type>>>;
272
273 template <typename Ret, typename... Args>
274 delegate(Ret (*)(const void*, Args...), const void* = nullptr) noexcept -> delegate<Ret(Args...)>;
275
276 //------------------------------------------------------------------------------
277 // signal
278 //------------------------------------------------------------------------------
279
280 template <typename Ret, typename... Args>
281 class signal<Ret(Args...)>;
282
283 namespace detail {
284 template <typename Ret, typename... Args>
285 using container = cnt::darray<delegate<Ret(Args...)>>;
286 } // namespace detail
287
292 template <typename Ret, typename... Args>
293 class signal<Ret(Args...)> {
294 friend class sink<Ret(Args...)>;
295
296 private:
298 detail::container<Ret, Args...> m_listeners;
299
300 public:
301 using size_type = typename detail::container<Ret, Args...>::size_type;
302 using sink_type = sink<Ret(Args...)>;
303
306 GAIA_NODISCARD size_type size() const noexcept {
307 return m_listeners.size();
308 }
309
312 GAIA_NODISCARD bool empty() const noexcept {
313 return m_listeners.empty();
314 }
315
318 void emit(Args... args) {
319 for (auto&& call: std::as_const(m_listeners))
320 call(args...);
321 }
322 };
323
324 //------------------------------------------------------------------------------
325 // Sink
326 //------------------------------------------------------------------------------
327
335 template <typename Ret, typename... Args>
336 class sink<Ret(Args...)> {
337 using signal_type = signal<Ret(Args...)>;
338 using func_type = Ret(const void*, Args...);
339
340 signal_type* m_s;
341
342 public:
345 sink(signal<Ret(Args...)>& ref) noexcept: m_s{&ref} {}
346
350 void move_from(sink& other) {
351 m_s->m_listeners.reserve(m_s->m_listeners.size() + other.m_s->m_listeners.size());
352 for (auto&& listener: other.m_s->m_listeners)
353 m_s->m_listeners.push_back(GAIA_MOV(listener));
354
355 other.reset();
356 }
357
361 template <auto FuncToBind>
362 void bind() {
363 delegate<Ret(Args...)> call{};
364 call.template bind<FuncToBind>();
365 bind_internal(call);
366 }
367
374 template <auto FuncToBind, typename Type>
375 void bind(Type& value_or_instance) {
376 delegate<Ret(Args...)> call{};
377 call.template bind<FuncToBind>(value_or_instance);
378 bind_internal(call);
379 }
380
385 void bind(func_type* func, const void* data = nullptr) {
386 if (func == nullptr && data == nullptr)
387 return;
388
389 delegate<Ret(Args...)> call{};
390 call.bind(func, data);
391 bind_internal(call);
392 }
393
396 template <auto FuncToUnbind>
397 void unbind() {
398 delegate<Ret(Args...)> call{};
399 call.template bind<FuncToUnbind>();
400
401 m_s->m_listeners.retain([&](const auto& l) {
402 return l != call;
403 });
404 }
405
410 template <auto FuncToUnbind, typename Type>
411 void unbind(Type& value_or_instance) {
412 delegate<Ret(Args...)> call{};
413 call.template bind<FuncToUnbind>(value_or_instance);
414
415 auto& listeners = m_s->m_listeners;
416 for (uint32_t i = 0; i < listeners.size();) {
417 if (listeners[i] != call) {
418 ++i;
419 continue;
420 }
421
422 core::swap_erase_unsafe(listeners, i);
423 }
424 }
425
429 template <typename Type>
430 void unbind(Type* value_or_instance) {
431 if (!value_or_instance)
432 return;
433
434 auto& listeners = m_s->m_listeners;
435 for (uint32_t i = 0; i < listeners.size();) {
436 if (listeners[i].instance() != value_or_instance) {
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 unbind(&value_or_instance);
451 }
452
454 void reset() {
455 m_s->m_listeners.clear();
456 }
457
458 private:
459 void bind_internal(const delegate<Ret(Args...)>& call) {
460 if (!core::has(m_s->m_listeners, call))
461 m_s->m_listeners.push_back(GAIA_MOV(call));
462 }
463 };
464
465 template <typename Ret, typename... Args>
466 sink(signal<Ret(Args...)>&) noexcept -> sink<Ret(Args...)>;
467 } // namespace util
468} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
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:86
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:161
void reset() noexcept
Resets a delegate. After a reset, a delegate cannot be invoked anymore.
Definition signal.h:167
GAIA_NODISCARD bool has_func() const noexcept
Returns the functor pointer linked to a delegate, if any.
Definition signal.h:174
void bind() noexcept
Binds a free function or an unbound member to a delegate.
Definition signal.h:100
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:122
Ret operator()(Args... args) const
The delegate invokes the underlying function and returns the result.
Definition signal.h:187
delegate(func_type *func, const void *data=nullptr) noexcept
Constructs a delegate by binding a function with optional context to it.
Definition signal.h:93
GAIA_NODISCARD bool operator!=(const delegate< Ret(Args...)> &other) const noexcept
Compares the contents of two delegates.
Definition signal.h:209
GAIA_NODISCARD bool operator==(const delegate< Ret(Args...)> &other) const noexcept
Compares the contents of two delegates.
Definition signal.h:202
GAIA_NODISCARD const void * instance() const noexcept
Returns the instance or the context linked to a delegate, if any.
Definition signal.h:180
delegate(detail::connect_arg_t< FuncToBind >) noexcept
Constructs a delegate by binding a free function or an unbound member to it.
Definition signal.h:77
void bind(Type *value_or_instance) noexcept
Binds a free function with context or a bound member to a delegate.
Definition signal.h:142
Definition signal.h:47
Signal is a container of listener which it can notify. It works directly with references to classes a...
Definition signal.h:293
GAIA_NODISCARD size_type size() const noexcept
Number of listeners connected to the signal.
Definition signal.h:306
GAIA_NODISCARD bool empty() const noexcept
Check is there is any listener bound to the signal.
Definition signal.h:312
void emit(Args... args)
Signals all listeners.
Definition signal.h:318
Definition signal.h:49
Sink is a helper class used to bind listeners to signals. The separation between signal and sink make...
Definition signal.h:336
sink(signal< Ret(Args...)> &ref) noexcept
Constructs a sink that is allowed to modify a given signal.
Definition signal.h:345
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:350
void bind()
Binds a free function or an unbound member to a signal. The signal handler performs checks to avoid m...
Definition signal.h:362
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:375
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 bound members from a signal.
Definition signal.h:430
void unbind(Type &value_or_instance)
Unbinds a free function with context or a bound member from a signal.
Definition signal.h:411
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:385
void reset()
Unbinds all listeners from a signal.
Definition signal.h:454
void unbind()
Unbinds a free function or an unbound member from a signal.
Definition signal.h:397
Definition signal.h:51
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition signal.h:40