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>
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>
506 void operator()(A && ... 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 lock_type lock1(m_mutex, std::defer_lock);
552 lock_type lock2(o.m_mutex, std::defer_lock);
553 std::lock(lock1, lock2);
555 std::swap(m_func, o.m_func);
556 m_block.store(o.m_block.exchange(m_block.load()));
572 template <
typename... A>
574 lock_type lock(m_mutex);
575 if (!m_block && m_func) m_func(std::forward<A>(a)...);
588 template <
typename Callable>
591 m_func = std::forward<Callable>(c);
594 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
609 template <
typename Callable>
610 std::enable_if_t<trait::is_callable_v<arg_list, Callable>, Connection>
613 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
615 return Connection(s);
627 template <
typename Callable>
628 std::enable_if_t<trait::is_callable_v<ext_arg_list, Callable>, Connection>
631 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
632 s->conn = Connection(s);
634 return Connection(s);
644 template <
typename Pmf,
typename Ptr>
645 std::enable_if_t<trait::is_callable_v<arg_list, Pmf, Ptr> &&
646 !trait::is_weak_ptr_compatible_v<Ptr>, Connection>
649 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr));
651 return Connection(s);
661 template <
typename Pmf,
typename Ptr>
662 std::enable_if_t<trait::is_callable_v<ext_arg_list, Pmf, Ptr> &&
663 !trait::is_weak_ptr_compatible_v<Ptr>, Connection>
666 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr));
667 s->conn = Connection(s);
669 return Connection(s);
688 template <
typename Pmf,
typename Ptr>
689 std::enable_if_t<!trait::is_callable_v<arg_list, Pmf> &&
690 trait::is_weak_ptr_compatible_v<Ptr>, Connection>
692 using trait::to_weak;
693 auto w = to_weak(std::forward<Ptr>(ptr));
695 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), w);
697 return Connection(s);
716 template <
typename Callable,
typename Trackable>
717 std::enable_if_t<trait::is_callable_v<arg_list, Callable> &&
718 trait::is_weak_ptr_compatible_v<Trackable>, Connection>
720 using trait::to_weak;
721 auto w = to_weak(std::forward<Trackable>(ptr));
723 auto s = std::make_shared<slot_t>(std::forward<Callable>(c), w);
725 return Connection(s);
732 template <
typename... CallArgs>
734 return connect_connection(std::forward<CallArgs>(args)...);
742 lock_type lock(m_mutex);
759 m_block.store(
false);
766 return m_block.load();
770 template <
typename S>
771 void add_slot(S &s) {
772 lock_type lock(m_mutex);
775 m_func = CallSlots(*
this);
776 auto slots = m_func.template target<CallSlots>();
777 s->next = slots->m_slots;
779 }
else if (
auto call_slots = m_func.template target<CallSlots>()) {
781 s->next = call_slots->m_slots;
782 call_slots->m_slots = s;
785 using slot_t = detail::Slot<std::function<void(T...)>, arg_list>;
786 auto s2 = std::make_shared<slot_t>(
787 std::forward<std::function<void(T...)>>(m_func));
788 m_func = CallSlots(*
this);
789 auto slots = m_func.template target<CallSlots>();
790 s2->next = slots->m_slots;
801 std::function<void(T...)> m_func;
803 std::atomic<bool> m_block;
811 template <
typename... T>
812 using Signal = SignalBase<detail::NullMutex, T...>;
823 template <
typename... T>
824 using Signal_mt = SignalBase<mutex, T...>;
831 template <
typename... T>
832 using Signal_r = SignalBase<recursive_mutex, T...>;
ScopedConnection is a RAII version of Connection It disconnects the slot from the signal upon destruc...
Definition: Signal.h:257
std::enable_if_t< trait::is_callable_v< ext_arg_list, Pmf, Ptr > &&!trait::is_weak_ptr_compatible_v< Ptr >, Connection > connect_extended(Pmf &&pmf, Ptr &&ptr)
Overload of connect for pointer over member functions and.
Definition: Signal.h:664
std::enable_if_t<!trait::is_callable_v< arg_list, Pmf > &&trait::is_weak_ptr_compatible_v< Ptr >, Connection > connect(Pmf &&pmf, Ptr &&ptr)
Overload of connect for lifetime object tracking and automatic disconnection.
Definition: Signal.h:691
Definition: optional.h:885
std::enable_if_t< trait::is_callable_v< arg_list, Callable >, Connection > connect_connection(Callable &&c)
Connect a callable of compatible arguments, returning a Connection.
Definition: Signal.h:611
std::enable_if_t< trait::is_callable_v< ext_arg_list, Callable >, Connection > connect_extended(Callable &&c)
Connect a callable with an additional Connection argument.
Definition: Signal.h:629
WPILib C++ utilities (wpiutil) namespace.
Definition: SmallString.h:21
ConnectionBlocker is a RAII object that blocks a connection until destruction.
Definition: Signal.h:154
void block() noexcept
Blocks signal emission Safety: thread safe.
Definition: Signal.h:750
void unblock() noexcept
Unblocks signal emission Safety: thread safe.
Definition: Signal.h:758
void connect(Callable &&c)
Connect a callable of compatible arguments.
Definition: Signal.h:589
std::enable_if_t< trait::is_callable_v< arg_list, Callable > &&trait::is_weak_ptr_compatible_v< Trackable >, Connection > connect(Callable &&c, Trackable &&ptr)
Overload of connect for lifetime object tracking and automatic disconnection.
Definition: Signal.h:719
void disconnect_all()
Disconnects all the slots Safety: Thread safety depends on locking policy.
Definition: Signal.h:741
represent a list of types
Definition: Signal.h:52
A Connection object allows interaction with an ongoing slot connection.
Definition: Signal.h:198
ScopedConnection connect_scoped(CallArgs &&...args)
Creates a connection whose duration is tied to the return object Use the same semantics as connect...
Definition: Signal.h:733
std::enable_if_t< trait::is_callable_v< arg_list, Pmf, Ptr > &&!trait::is_weak_ptr_compatible_v< Ptr >, Connection > connect(Pmf &&pmf, Ptr &&ptr)
Overload of connect for pointers over member functions.
Definition: Signal.h:647
bool blocked() const noexcept
Tests blocking state of signal emission.
Definition: Signal.h:765
SignalBase is an implementation of the observer pattern, through the use of an emitting object and sl...
Definition: Signal.h:495
void operator()(A &&...a)
Emit a signal.
Definition: Signal.h:573