40 #include <type_traits>
43 #include "wpi/mutex.h"
61 std::weak_ptr<T> to_weak(std::weak_ptr<T> w) {
66 std::weak_ptr<T> to_weak(std::shared_ptr<T> s) {
74 struct voider {
using type = void; };
81 template <
typename,
typename,
typename =
void,
typename =
void>
84 template <
typename F,
typename P,
typename... T>
86 void_t<decltype(((*std::declval<P>()).*std::declval<F>())(std::declval<T>()...))>>
89 template <
typename F,
typename... T>
91 void_t<decltype(std::declval<F>()(std::declval<T>()...))>>
95 template <
typename T,
typename =
void>
99 struct is_weak_ptr<T, void_t<decltype(std::declval<T>().expired()),
100 decltype(std::declval<T>().lock()),
101 decltype(std::declval<T>().reset())>>
104 template <
typename T,
typename =
void>
107 template <
typename T>
109 :
is_weak_ptr<decltype(to_weak(std::declval<T>()))> {};
114 template <
typename P>
118 template <
typename L,
typename... T>
137 bool connected()
const noexcept {
return m_connected; }
138 bool disconnect() noexcept {
return m_connected.exchange(
false); }
140 bool blocked()
const noexcept {
return m_blocked.load(); }
141 void block() noexcept { m_blocked.store(
true); }
142 void unblock() noexcept { m_blocked.store(
false); }
145 std::atomic<bool> m_connected;
146 std::atomic<bool> m_blocked;
163 : m_state{std::move(o.m_state)}
168 m_state.swap(o.m_state);
175 : m_state{std::move(s)}
177 auto d = m_state.lock();
181 void release() noexcept {
182 auto d = m_state.lock();
187 std::weak_ptr<detail::SlotState> m_state;
208 bool valid()
const noexcept {
209 return !m_state.expired();
212 bool connected()
const noexcept {
213 const auto d = m_state.lock();
214 return d && d->connected();
217 bool disconnect() noexcept {
218 auto d = m_state.lock();
219 return d && d->disconnect();
222 bool blocked()
const noexcept {
223 const auto d = m_state.lock();
224 return d && d->blocked();
227 void block() noexcept {
228 auto d = m_state.lock();
233 void unblock() noexcept {
234 auto d = m_state.lock();
244 template <
typename,
typename...>
friend class SignalBase;
245 Connection(std::weak_ptr<detail::SlotState> s) noexcept
246 : m_state{std::move(s)}
250 std::weak_ptr<detail::SlotState> m_state;
276 m_state.swap(o.m_state);
281 template <
typename,
typename...>
friend class SignalBase;
289 template <
typename...>
292 template <
typename... T>
293 using SlotPtr = std::shared_ptr<
SlotBase<T...>>;
299 template <
typename... Args>
304 virtual ~
SlotBase() noexcept =
default;
308 virtual void call_slot(Args...) = 0;
310 template <
typename... U>
311 void operator()(U && ...u) {
312 if (SlotState::connected() && !SlotState::blocked())
313 call_slot(std::forward<U>(u)...);
316 SlotPtr<Args...> next;
319 template <
typename,
typename...>
class Slot {};
325 template <
typename Func,
typename... Args>
326 class Slot<Func, trait::typelist<Args...>> :
public SlotBase<Args...> {
328 template <
typename F>
329 constexpr
Slot(F && f) : func{std::forward<F>(f)} {}
331 virtual void call_slot(Args ...args)
override {
336 std::decay_t<Func> func;
342 template <
typename Func,
typename... Args>
343 class Slot<Func, trait::typelist<Connection&, Args...>> :
public SlotBase<Args...> {
345 template <
typename F>
346 constexpr
Slot(F && f) : func{std::forward<F>(f)} {}
348 virtual void call_slot(Args ...args)
override {
355 std::decay_t<Func> func;
363 template <
typename Pmf,
typename Ptr,
typename... Args>
364 class Slot<Pmf, Ptr, trait::typelist<Args...>> :
public SlotBase<Args...> {
366 template <
typename F,
typename P>
367 constexpr
Slot(F && f, P && p)
368 : pmf{std::forward<F>(f)},
369 ptr{std::forward<P>(p)} {}
371 virtual void call_slot(Args ...args)
override {
372 ((*ptr).*pmf)(args...);
376 std::decay_t<Pmf> pmf;
377 std::decay_t<Ptr> ptr;
383 template <
typename Pmf,
typename Ptr,
typename... Args>
384 class Slot<Pmf, Ptr, trait::typelist<Connection&, Args...>> :
public SlotBase<Args...> {
386 template <
typename F,
typename P>
387 constexpr
Slot(F && f, P && p)
388 : pmf{std::forward<F>(f)},
389 ptr{std::forward<P>(p)} {}
391 virtual void call_slot(Args ...args)
override {
392 ((*ptr).*pmf)(conn, args...);
398 std::decay_t<Pmf> pmf;
399 std::decay_t<Ptr> ptr;
409 template <
typename Func,
typename WeakPtr,
typename... Args>
412 template <
typename F,
typename P>
414 : func{std::forward<F>(f)},
415 ptr{std::forward<P>(p)}
418 virtual void call_slot(Args ...args)
override {
419 if (! SlotState::connected())
422 SlotState::disconnect();
428 std::decay_t<Func> func;
429 std::decay_t<WeakPtr> ptr;
439 template <
typename Pmf,
typename WeakPtr,
typename... Args>
442 template <
typename F,
typename P>
444 : pmf{std::forward<F>(f)},
445 ptr{std::forward<P>(p)}
448 virtual void call_slot(Args ...args)
override {
449 if (! SlotState::connected())
451 auto sp = ptr.lock();
453 SlotState::disconnect();
455 ((*sp).*pmf)(args...);
459 std::decay_t<Pmf> pmf;
460 std::decay_t<WeakPtr> ptr;
472 bool try_lock() {
return true; }
494 template <
typename Lockable,
typename... T>
496 using lock_type = std::unique_lock<Lockable>;
497 using SlotPtr = detail::SlotPtr<T...>;
505 template <
typename... A>
507 SlotPtr *prev =
nullptr;
508 SlotPtr *curr = m_slots ? &m_slots :
nullptr;
512 if ((*curr)->connected()) {
513 if (!m_base.m_block && !(*curr)->blocked())
514 (*curr)->operator()(a...);
516 curr = (*curr)->next ? &((*curr)->next) :
nullptr;
521 (*prev)->next = (*curr)->next;
522 curr = (*prev)->next ? &((*prev)->next) :
nullptr;
525 curr = (*curr)->next ? &((*curr)->next) :
nullptr;
544 : m_block{o.m_block.load()}
546 lock_type lock(o.m_mutex);
547 std::swap(m_func, o.m_func);
551 std::scoped_lock lock(m_mutex, o.m_mutex);
553 std::swap(m_func, o.m_func);
554 m_block.store(o.m_block.exchange(m_block.load()));
570 template <
typename... A>
572 lock_type lock(m_mutex);
573 if (!m_block && m_func) m_func(std::forward<A>(a)...);
586 template <
typename Callable>
589 m_func = std::forward<Callable>(c);
592 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
607 template <
typename Callable>
608 std::enable_if_t<trait::is_callable_v<arg_list, Callable>,
Connection>
611 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
625 template <
typename Callable>
626 std::enable_if_t<trait::is_callable_v<ext_arg_list, Callable>,
Connection>
629 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
642 template <
typename Pmf,
typename Ptr>
643 std::enable_if_t<trait::is_callable_v<arg_list, Pmf, Ptr> &&
644 !trait::is_weak_ptr_compatible_v<Ptr>,
Connection>
647 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr));
659 template <
typename Pmf,
typename Ptr>
660 std::enable_if_t<trait::is_callable_v<ext_arg_list, Pmf, Ptr> &&
661 !trait::is_weak_ptr_compatible_v<Ptr>,
Connection>
664 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr));
686 template <
typename Pmf,
typename Ptr>
687 std::enable_if_t<!trait::is_callable_v<arg_list, Pmf> &&
688 trait::is_weak_ptr_compatible_v<Ptr>,
Connection>
690 using trait::to_weak;
691 auto w = to_weak(std::forward<Ptr>(ptr));
693 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), w);
714 template <
typename Callable,
typename Trackable>
715 std::enable_if_t<trait::is_callable_v<arg_list, Callable> &&
716 trait::is_weak_ptr_compatible_v<Trackable>,
Connection>
718 using trait::to_weak;
719 auto w = to_weak(std::forward<Trackable>(ptr));
721 auto s = std::make_shared<slot_t>(std::forward<Callable>(c), w);
730 template <
typename... CallArgs>
740 lock_type lock(m_mutex);
757 m_block.store(
false);
764 return m_block.load();
768 template <
typename S>
769 void add_slot(S &s) {
770 lock_type lock(m_mutex);
773 m_func = CallSlots(*
this);
774 auto slots = m_func.template target<CallSlots>();
775 s->next = slots->m_slots;
777 }
else if (
auto call_slots = m_func.template target<CallSlots>()) {
779 s->next = call_slots->m_slots;
780 call_slots->m_slots = s;
783 using slot_t = detail::Slot<std::function<void(T...)>, arg_list>;
784 auto s2 = std::make_shared<slot_t>(
785 std::forward<std::function<
void(T...)>>(m_func));
786 m_func = CallSlots(*
this);
787 auto slots = m_func.template target<CallSlots>();
788 s2->next = slots->m_slots;
799 std::function<void(T...)> m_func;
800 mutable Lockable m_mutex;
801 std::atomic<bool> m_block;
809 template <
typename... T>
810 using Signal = SignalBase<detail::NullMutex, T...>;
821 template <
typename... T>
822 using Signal_mt = SignalBase<mutex, T...>;
829 template <
typename... T>
830 using Signal_r = SignalBase<recursive_mutex, T...>;