2#include "gaia/config/config.h"
8#include "gaia/cnt/darray.h"
9#include "gaia/core/func.h"
10#include "gaia/core/utility.h"
16 template <
typename Ret,
typename... Args>
17 auto func_ptr(Ret (*)(Args...)) -> Ret (*)(Args...);
19 template <
typename Ret,
typename Type,
typename... Args,
typename Other>
20 auto func_ptr(Ret (*)(Type, Args...), Other&&) -> Ret (*)(Args...);
22 template <
typename Class,
typename Ret,
typename... Args,
typename... Other>
23 auto func_ptr(Ret (Class::*)(Args...), Other&&...) -> Ret (*)(Args...);
25 template <
typename Class,
typename Ret,
typename... Args,
typename... Other>
26 auto func_ptr(Ret (Class::*)(Args...) const, Other&&...) -> Ret (*)(Args...);
28 template <
typename Class,
typename Type,
typename... Other>
29 auto func_ptr(Type Class::*, Other&&...) -> Type (*)();
31 template <
typename... Type>
32 using func_ptr_t =
decltype(detail::func_ptr(std::declval<Type>()...));
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...>{};
41 struct connect_arg_t {};
44 inline constexpr connect_arg_t<Func> connect_arg{};
50 template <
typename Function>
52 template <
typename Function>
66 template <
typename Ret,
typename... Args>
68 using func_type = Ret(
const void*, Args...);
70 template <std::
size_t I>
71 using arg_t = std::tuple_element_t<I, std::tuple<Args...>>;
73 func_type* m_fnc =
nullptr;
74 const void* m_ctx =
nullptr;
81 template <auto FuncToBind>
82 delegate(detail::connect_arg_t<FuncToBind>)
noexcept {
90 template <auto FuncToBind,
typename Type>
91 delegate(detail::connect_arg_t<FuncToBind>, Type&& value_or_instance)
noexcept {
92 bind<FuncToBind>(GAIA_FWD(value_or_instance));
98 delegate(func_type* func,
const void* data =
nullptr) noexcept {
104 template <auto FuncToBind>
108 if constexpr (std::is_invocable_r_v<Ret,
decltype(FuncToBind), Args...>) {
109 m_fnc = [](
const void*, Args... args) {
110 return Ret(invoke(FuncToBind, GAIA_FWD(args)...));
112 }
else if constexpr (std::is_member_pointer_v<
decltype(FuncToBind)>) {
113 m_fnc = wrap<FuncToBind>(detail::index_sequence_for<std::tuple_element_t<0, std::tuple<Args...>>>(
114 detail::func_ptr_t<
decltype(FuncToBind)>{}));
116 m_fnc = wrap<FuncToBind>(detail::index_sequence_for(detail::func_ptr_t<
decltype(FuncToBind)>{}));
126 template <auto FuncToBind,
typename Type>
127 void bind(Type& value_or_instance)
noexcept {
128 m_ctx = &value_or_instance;
130 if constexpr (std::is_invocable_r_v<Ret,
decltype(FuncToBind), Type&, Args...>) {
131 using const_or_not_type = std::conditional_t<std::is_const_v<Type>,
const void*,
void*>;
132 m_fnc = [](
const void* ctx, Args... args) {
133 auto pType =
static_cast<Type*
>(
const_cast<const_or_not_type
>(ctx));
134 return Ret(invoke(FuncToBind, *pType, GAIA_FWD(args)...));
137 m_fnc = wrap<FuncToBind>(
138 value_or_instance, detail::index_sequence_for(detail::func_ptr_t<
decltype(FuncToBind), Type>{}));
146 template <auto FuncToBind,
typename Type>
147 void bind(Type* value_or_instance)
noexcept {
148 m_ctx = value_or_instance;
150 if constexpr (std::is_invocable_r_v<Ret,
decltype(FuncToBind), Type*, Args...>) {
151 using const_or_not_type = std::conditional_t<std::is_const_v<Type>,
const void*,
void*>;
152 m_fnc = [](
const void* ctx, Args... args) {
153 auto pType =
static_cast<Type*
>(
const_cast<const_or_not_type
>(ctx));
154 return Ret(invoke(FuncToBind, pType, GAIA_FWD(args)...));
157 m_fnc = wrap<FuncToBind>(
158 value_or_instance, detail::index_sequence_for(detail::func_ptr_t<
decltype(FuncToBind), Type>{}));
166 void bind(func_type* function,
const void* context =
nullptr) noexcept {
180 return m_fnc !=
nullptr;
185 GAIA_NODISCARD
const void*
instance() const noexcept {
193 GAIA_ASSERT(m_fnc !=
nullptr &&
"Trying to call an unbound delegate!");
194 return m_fnc(m_ctx, GAIA_FWD(args)...);
199 GAIA_NODISCARD
explicit operator bool() const noexcept {
201 return m_fnc !=
nullptr;
208 return m_fnc == other.m_fnc && m_ctx == other.m_ctx;
215 return !operator==(other);
219 template <
auto FuncToBind, std::size_t... Index>
220 GAIA_NODISCARD
auto wrap(std::index_sequence<Index...>)
noexcept {
221 return [](
const void*, Args... args) {
222 [[maybe_unused]]
const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
223 return Ret(invoke(FuncToBind, GAIA_FWD_AS(arg_t<Index>, std::get<Index>(argsFwd))...));
227 template <
auto FuncToBind,
typename Type, std::size_t... Index>
228 GAIA_NODISCARD
auto wrap(Type&, std::index_sequence<Index...>)
noexcept {
229 using const_or_not_type = std::conditional_t<std::is_const_v<Type>,
const void*,
void*>;
230 return [](
const void* ctx, Args... args) {
231 [[maybe_unused]]
const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
232 auto pType =
static_cast<Type*
>(
const_cast<const_or_not_type
>(ctx));
233 return Ret(invoke(FuncToBind, *pType, GAIA_FWD_AS(arg_t<Index>, std::get<Index>(argsFwd))...));
237 template <
auto FuncToBind,
typename Type, std::size_t... Index>
238 GAIA_NODISCARD
auto wrap(Type*, std::index_sequence<Index...>)
noexcept {
239 using const_or_not_type = std::conditional_t<std::is_const_v<Type>,
const void*,
void*>;
240 return [](
const void* ctx, Args... args) {
241 [[maybe_unused]]
const auto argsFwd = std::forward_as_tuple(GAIA_FWD(args)...);
242 auto pType =
static_cast<Type*
>(
const_cast<const_or_not_type
>(ctx));
243 return Ret(invoke(FuncToBind, pType, GAIA_FWD_AS(arg_t<Index>, std::get<Index>(argsFwd))...));
254 template <
typename Ret,
typename... Args>
255 GAIA_NODISCARD
bool operator==(
const delegate<Ret(Args...)>& lhs,
const delegate<Ret(Args...)>& rhs)
noexcept {
265 template <
typename Ret,
typename... Args>
266 GAIA_NODISCARD
bool operator!=(
const delegate<Ret(Args...)>& lhs,
const delegate<Ret(Args...)>& rhs)
noexcept {
274 delegate(detail::connect_arg_t<Func> tag)
noexcept
275 -> delegate<std::remove_pointer_t<detail::func_ptr_t<
decltype(Func)>>>;
282 template <auto Func,
typename Type>
283 delegate(detail::connect_arg_t<Func> tag, Type&& value_or_instance)
noexcept
284 -> delegate<std::remove_pointer_t<detail::func_ptr_t<
decltype(Func), Type>>>;
291 template <
typename Ret,
typename... Args>
292 delegate(Ret (*func)(
const void*, Args...),
const void* data =
nullptr) noexcept -> delegate<Ret(Args...)>;
298 template <typename Ret, typename... Args>
299 class signal<Ret(Args...)>;
303 template <
typename Ret,
typename... Args>
304 using container = cnt::darray<delegate<Ret(Args...)>>;
312 template <
typename Ret,
typename... Args>
314 friend class sink<Ret(Args...)>;
318 detail::container<Ret, Args...> m_listeners;
322 using size_type =
typename detail::container<Ret, Args...>::size_type;
329 return m_listeners.size();
334 GAIA_NODISCARD
bool empty() const noexcept {
335 return m_listeners.empty();
341 for (
auto&& call: std::as_const(m_listeners))
357 template <
typename Ret,
typename... Args>
360 using func_type = Ret(
const void*, Args...);
373 m_s->m_listeners.reserve(m_s->m_listeners.size() + other.m_s->m_listeners.size());
374 for (
auto&& listener: other.m_s->m_listeners)
375 m_s->m_listeners.push_back(GAIA_MOV(listener));
383 template <auto FuncToBind>
386 call.template bind<FuncToBind>();
396 template <auto FuncToBind,
typename Type>
397 void bind(Type& value_or_instance) {
399 call.template bind<FuncToBind>(value_or_instance);
407 void bind(func_type* func,
const void* data =
nullptr) {
408 if (func ==
nullptr && data ==
nullptr)
412 call.bind(func, data);
418 template <auto FuncToUnbind>
421 call.template bind<FuncToUnbind>();
423 m_s->m_listeners.retain([&](
const auto& l) {
432 template <auto FuncToUnbind,
typename Type>
435 call.template bind<FuncToUnbind>(value_or_instance);
437 auto& listeners = m_s->m_listeners;
438 for (uint32_t i = 0; i < listeners.size();) {
439 if (listeners[i] != call) {
444 core::swap_erase_unsafe(listeners, i);
451 template <
typename Type>
453 if (!value_or_instance)
456 auto& listeners = m_s->m_listeners;
457 for (uint32_t i = 0; i < listeners.size();) {
458 if (listeners[i].instance() != value_or_instance) {
463 core::swap_erase_unsafe(listeners, i);
470 template <
typename Type>
472 unbind(&value_or_instance);
477 m_s->m_listeners.clear();
481 void bind_internal(
const delegate<Ret(Args...)>& call) {
482 if (!core::has(m_s->m_listeners, call))
483 m_s->m_listeners.push_back(GAIA_MOV(call));
491 template <
typename Ret,
typename... Args>
492 sink(signal<Ret(Args...)>& ref)
noexcept -> sink<Ret(Args...)>;
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:91
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:166
void reset() noexcept
Resets a delegate. After a reset, a delegate cannot be invoked anymore.
Definition signal.h:172
GAIA_NODISCARD bool has_func() const noexcept
Returns the functor pointer linked to a delegate, if any.
Definition signal.h:179
void bind() noexcept
Binds a free function or an unbound member to a delegate.
Definition signal.h:105
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:127
Ret operator()(Args... args) const
The delegate invokes the underlying function and returns the result.
Definition signal.h:192
delegate(func_type *func, const void *data=nullptr) noexcept
Constructs a delegate by binding a function with optional context to it.
Definition signal.h:98
GAIA_NODISCARD bool operator!=(const delegate< Ret(Args...)> &other) const noexcept
Compares the contents of two delegates.
Definition signal.h:214
GAIA_NODISCARD bool operator==(const delegate< Ret(Args...)> &other) const noexcept
Compares the contents of two delegates.
Definition signal.h:207
GAIA_NODISCARD const void * instance() const noexcept
Returns the instance or the context linked to a delegate, if any.
Definition signal.h:185
delegate(detail::connect_arg_t< FuncToBind >) noexcept
Constructs a delegate by binding a free function or an unbound member to it.
Definition signal.h:82
void bind(Type *value_or_instance) noexcept
Binds a free function with context or a bound member to a delegate.
Definition signal.h:147
Signal is a container of listener which it can notify. It works directly with references to classes a...
Definition signal.h:313
GAIA_NODISCARD size_type size() const noexcept
Number of listeners connected to the signal.
Definition signal.h:328
typename detail::container< Ret, Args... >::size_type size_type
Type used to represent listener counts.
Definition signal.h:322
GAIA_NODISCARD bool empty() const noexcept
Check is there is any listener bound to the signal.
Definition signal.h:334
void emit(Args... args)
Signals all listeners.
Definition signal.h:340
Sink is a helper class used to bind listeners to signals. The separation between signal and sink make...
Definition signal.h:358
sink(signal< Ret(Args...)> &ref) noexcept
Constructs a sink that is allowed to modify a given signal.
Definition signal.h:367
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:372
void bind()
Binds a free function or an unbound member to a signal. The signal handler performs checks to avoid m...
Definition signal.h:384
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:397
void unbind(Type &value_or_instance)
Unbinds a free function with context or bound members from a signal.
Definition signal.h:471
void unbind(Type *value_or_instance)
Unbinds a free function with context or bound members from a signal.
Definition signal.h:452
void unbind(Type &value_or_instance)
Unbinds a free function with context or a bound member from a signal.
Definition signal.h:433
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:407
void reset()
Unbinds all listeners from a signal.
Definition signal.h:476
void unbind()
Unbinds a free function or an unbound member from a signal.
Definition signal.h:419