WPILibC++ 2024.3.2
Signal.h
Go to the documentation of this file.
1/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8/*
9
10Sigslot, a signal-slot library
11
12https://github.com/palacaze/sigslot
13
14MIT License
15
16Copyright (c) 2017 Pierre-Antoine Lacaze
17
18Permission is hereby granted, free of charge, to any person obtaining a copy
19of this software and associated documentation files (the "Software"), to deal
20in the Software without restriction, including without limitation the rights
21to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22copies of the Software, and to permit persons to whom the Software is
23furnished to do so, subject to the following conditions:
24
25The above copyright notice and this permission notice shall be included in all
26copies or substantial portions of the Software.
27
28THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34SOFTWARE.
35 */
36#pragma once
37#include <atomic>
38#include <functional>
39#include <memory>
40#include <type_traits>
41#include <utility>
42
43#include "wpi/mutex.h"
44
45namespace wpi {
46
47namespace sig {
48
49namespace trait {
50
51/// represent a list of types
52template <typename...> struct typelist {};
53
54/**
55 * Pointers that can be converted to a weak pointer concept for tracking
56 * purpose must implement the to_weak() function in order to make use of
57 * ADL to convert that type and make it usable
58 */
59
60template<typename T>
61std::weak_ptr<T> to_weak(std::weak_ptr<T> w) {
62 return w;
63}
64
65template<typename T>
66std::weak_ptr<T> to_weak(std::shared_ptr<T> s) {
67 return s;
68}
69
70// tools
71namespace detail {
72
73template <class...>
74struct voider { using type = void; };
75
76// void_t from c++17
77template <class...T>
78using void_t = typename detail::voider<T...>::type;
79
80
81template <typename, typename, typename = void, typename = void>
82struct is_callable : std::false_type {};
83
84template <typename F, typename P, typename... T>
85struct is_callable<F, P, typelist<T...>,
86 void_t<decltype(((*std::declval<P>()).*std::declval<F>())(std::declval<T>()...))>>
87 : std::true_type {};
88
89template <typename F, typename... T>
90struct is_callable<F, typelist<T...>,
91 void_t<decltype(std::declval<F>()(std::declval<T>()...))>>
92 : std::true_type {};
93
94
95template <typename T, typename = void>
96struct is_weak_ptr : std::false_type {};
97
98template <typename T>
99struct is_weak_ptr<T, void_t<decltype(std::declval<T>().expired()),
100 decltype(std::declval<T>().lock()),
101 decltype(std::declval<T>().reset())>>
102 : std::true_type {};
103
104template <typename T, typename = void>
105struct is_weak_ptr_compatible : std::false_type {};
106
107template <typename T>
108struct is_weak_ptr_compatible<T, void_t<decltype(to_weak(std::declval<T>()))>>
109 : is_weak_ptr<decltype(to_weak(std::declval<T>()))> {};
110
111} // namespace detail
112
113/// determine if a pointer is convertible into a "weak" pointer
114template <typename P>
116
117/// determine if a type T (Callable or Pmf) is callable with supplied arguments in L
118template <typename L, typename... T>
119constexpr bool is_callable_v = detail::is_callable<T..., L>::value;
120
121} // namespace trait
122
123
124namespace detail {
125
126/* SlotState holds slot type independent state, to be used to interact with
127 * slots indirectly through connection and ScopedConnection objects.
128 */
130public:
131 constexpr SlotState() noexcept
132 : m_connected(true),
133 m_blocked(false) {}
134
135 virtual ~SlotState() = default;
136
137 bool connected() const noexcept { return m_connected; }
138 bool disconnect() noexcept { return m_connected.exchange(false); }
139
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); }
143
144private:
145 std::atomic<bool> m_connected;
146 std::atomic<bool> m_blocked;
147};
148
149} // namespace detail
150
151/**
152 * ConnectionBlocker is a RAII object that blocks a connection until destruction
153 */
155public:
156 ConnectionBlocker() = default;
157 ~ConnectionBlocker() noexcept { release(); }
158
161
163 : m_state{std::move(o.m_state)}
164 {}
165
167 release();
168 m_state.swap(o.m_state);
169 return *this;
170 }
171
172private:
173 friend class Connection;
174 ConnectionBlocker(std::weak_ptr<detail::SlotState> s) noexcept
175 : m_state{std::move(s)}
176 {
177 auto d = m_state.lock();
178 if (d) d->block();
179 }
180
181 void release() noexcept {
182 auto d = m_state.lock();
183 if (d) d->unblock();
184 }
185
186private:
187 std::weak_ptr<detail::SlotState> m_state;
188};
189
190
191/**
192 * A Connection object allows interaction with an ongoing slot connection
193 *
194 * It allows common actions such as connection blocking and disconnection.
195 * Note that Connection is not a RAII object, one does not need to hold one
196 * such object to keep the signal-slot connection alive.
197 */
199public:
200 Connection() = default;
201 virtual ~Connection() = default;
202
203 Connection(const Connection &) noexcept = default;
204 Connection & operator=(const Connection &) noexcept = default;
205 Connection(Connection &&) noexcept = default;
206 Connection & operator=(Connection &&) noexcept = default;
207
208 bool valid() const noexcept {
209 return !m_state.expired();
210 }
211
212 bool connected() const noexcept {
213 const auto d = m_state.lock();
214 return d && d->connected();
215 }
216
217 bool disconnect() noexcept {
218 auto d = m_state.lock();
219 return d && d->disconnect();
220 }
221
222 bool blocked() const noexcept {
223 const auto d = m_state.lock();
224 return d && d->blocked();
225 }
226
227 void block() noexcept {
228 auto d = m_state.lock();
229 if(d)
230 d->block();
231 }
232
233 void unblock() noexcept {
234 auto d = m_state.lock();
235 if(d)
236 d->unblock();
237 }
238
239 ConnectionBlocker blocker() const noexcept {
241 }
242
243protected:
244 template <typename, typename...> friend class SignalBase;
245 Connection(std::weak_ptr<detail::SlotState> s) noexcept
246 : m_state{std::move(s)}
247 {}
248
249protected:
250 std::weak_ptr<detail::SlotState> m_state;
251};
252
253/**
254 * ScopedConnection is a RAII version of Connection
255 * It disconnects the slot from the signal upon destruction.
256 */
258public:
259 ScopedConnection() = default;
261 disconnect();
262 }
263
264 ScopedConnection(const Connection &c) noexcept : Connection(c) {}
265 ScopedConnection(Connection &&c) noexcept : Connection(std::move(c)) {}
266
267 ScopedConnection(const ScopedConnection &) noexcept = delete;
268 ScopedConnection & operator=(const ScopedConnection &) noexcept = delete;
269
271 : Connection{std::move(o.m_state)}
272 {}
273
275 disconnect();
276 m_state.swap(o.m_state);
277 return *this;
278 }
279
280private:
281 template <typename, typename...> friend class SignalBase;
282 ScopedConnection(std::weak_ptr<detail::SlotState> s) noexcept
283 : Connection{std::move(s)}
284 {}
285};
286
287namespace detail {
288
289template <typename...>
290class SlotBase;
291
292template <typename... T>
293using SlotPtr = std::shared_ptr<SlotBase<T...>>;
294
295/* A base class for slot objects. This base type only depends on slot argument
296 * types, it will be used as an element in an intrusive singly-linked list of
297 * slots, hence the public next member.
298 */
299template <typename... Args>
300class SlotBase : public SlotState {
301public:
302 using base_types = trait::typelist<Args...>;
303
304 virtual ~SlotBase() noexcept = default;
305
306 // method effectively responsible for calling the "slot" function with
307 // supplied arguments whenever emission happens.
308 virtual void call_slot(Args...) = 0;
309
310 template <typename... U>
311 void operator()(U && ...u) {
312 if (SlotState::connected() && !SlotState::blocked())
313 call_slot(std::forward<U>(u)...);
314 }
315
316 SlotPtr<Args...> next;
317};
318
319template <typename, typename...> class Slot {};
320
321/*
322 * A slot object holds state information, and a callable to to be called
323 * whenever the function call operator of its SlotBase base class is called.
324 */
325template <typename Func, typename... Args>
326class Slot<Func, trait::typelist<Args...>> : public SlotBase<Args...> {
327public:
328 template <typename F>
329 constexpr Slot(F && f) : func{std::forward<F>(f)} {}
330
331 virtual void call_slot(Args ...args) override {
332 func(args...);
333 }
334
335private:
336 std::decay_t<Func> func;
337};
338
339/*
340 * Variation of slot that prepends a Connection object to the callable
341 */
342template <typename Func, typename... Args>
343class Slot<Func, trait::typelist<Connection&, Args...>> : public SlotBase<Args...> {
344public:
345 template <typename F>
346 constexpr Slot(F && f) : func{std::forward<F>(f)} {}
347
348 virtual void call_slot(Args ...args) override {
349 func(conn, args...);
350 }
351
353
354private:
355 std::decay_t<Func> func;
356};
357
358/*
359 * A slot object holds state information, an object and a pointer over member
360 * function to be called whenever the function call operator of its SlotBase
361 * base class is called.
362 */
363template <typename Pmf, typename Ptr, typename... Args>
364class Slot<Pmf, Ptr, trait::typelist<Args...>> : public SlotBase<Args...> {
365public:
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)} {}
370
371 virtual void call_slot(Args ...args) override {
372 ((*ptr).*pmf)(args...);
373 }
374
375private:
376 std::decay_t<Pmf> pmf;
377 std::decay_t<Ptr> ptr;
378};
379
380/*
381 * Variation of slot that prepends a Connection object to the callable
382 */
383template <typename Pmf, typename Ptr, typename... Args>
384class Slot<Pmf, Ptr, trait::typelist<Connection&, Args...>> : public SlotBase<Args...> {
385public:
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)} {}
390
391 virtual void call_slot(Args ...args) override {
392 ((*ptr).*pmf)(conn, args...);
393 }
394
396
397private:
398 std::decay_t<Pmf> pmf;
399 std::decay_t<Ptr> ptr;
400};
401
402template <typename, typename, typename...> class SlotTracked {};
403
404/*
405 * An implementation of a slot that tracks the life of a supplied object
406 * through a weak pointer in order to automatically disconnect the slot
407 * on said object destruction.
408 */
409template <typename Func, typename WeakPtr, typename... Args>
410class SlotTracked<Func, WeakPtr, trait::typelist<Args...>> : public SlotBase<Args...> {
411public:
412 template <typename F, typename P>
413 constexpr SlotTracked(F && f, P && p)
414 : func{std::forward<F>(f)},
415 ptr{std::forward<P>(p)}
416 {}
417
418 virtual void call_slot(Args ...args) override {
419 if (! SlotState::connected())
420 return;
421 if (ptr.expired())
422 SlotState::disconnect();
423 else
424 func(args...);
425 }
426
427private:
428 std::decay_t<Func> func;
429 std::decay_t<WeakPtr> ptr;
430};
431
432template <typename, typename, typename...> class SlotPmfTracked {};
433
434/*
435 * An implementation of a slot as a pointer over member function, that tracks
436 * the life of a supplied object through a weak pointer in order to automatically
437 * disconnect the slot on said object destruction.
438 */
439template <typename Pmf, typename WeakPtr, typename... Args>
440class SlotPmfTracked<Pmf, WeakPtr, trait::typelist<Args...>> : public SlotBase<Args...> {
441public:
442 template <typename F, typename P>
443 constexpr SlotPmfTracked(F && f, P && p)
444 : pmf{std::forward<F>(f)},
445 ptr{std::forward<P>(p)}
446 {}
447
448 virtual void call_slot(Args ...args) override {
449 if (! SlotState::connected())
450 return;
451 auto sp = ptr.lock();
452 if (!sp)
453 SlotState::disconnect();
454 else
455 ((*sp).*pmf)(args...);
456 }
457
458private:
459 std::decay_t<Pmf> pmf;
460 std::decay_t<WeakPtr> ptr;
461};
462
463
464// noop mutex for thread-unsafe use
465struct NullMutex {
466 NullMutex() = default;
467 NullMutex(const NullMutex &) = delete;
468 NullMutex operator=(const NullMutex &) = delete;
469 NullMutex(NullMutex &&) = delete;
471
472 bool try_lock() { return true; }
473 void lock() {}
474 void unlock() {}
475};
476
477} // namespace detail
478
479
480/**
481 * SignalBase is an implementation of the observer pattern, through the use
482 * of an emitting object and slots that are connected to the signal and called
483 * with supplied arguments when a signal is emitted.
484 *
485 * wpi::SignalBase is the general implementation, whose locking policy must be
486 * set in order to decide thread safety guarantees. wpi::Signal and wpi::Signal_st
487 * are partial specializations for multi-threaded and single-threaded use.
488 *
489 * It does not allow slots to return a value.
490 *
491 * @tparam Lockable a lock type to decide the lock policy
492 * @tparam T... the argument types of the emitting and slots functions.
493 */
494template <typename Lockable, typename... T>
496 using lock_type = std::unique_lock<Lockable>;
497 using SlotPtr = detail::SlotPtr<T...>;
498
499 struct CallSlots {
500 SlotPtr m_slots;
501 SignalBase& m_base;
502
503 CallSlots(SignalBase& base) : m_base(base) {}
504
505 template <typename... A>
506 void operator()(A && ... a) {
507 SlotPtr *prev = nullptr;
508 SlotPtr *curr = m_slots ? &m_slots : nullptr;
509
510 while (curr) {
511 // call non blocked, non connected slots
512 if ((*curr)->connected()) {
513 if (!m_base.m_block && !(*curr)->blocked())
514 (*curr)->operator()(a...);
515 prev = curr;
516 curr = (*curr)->next ? &((*curr)->next) : nullptr;
517 }
518 // remove slots marked as disconnected
519 else {
520 if (prev) {
521 (*prev)->next = (*curr)->next;
522 curr = (*prev)->next ? &((*prev)->next) : nullptr;
523 }
524 else
525 curr = (*curr)->next ? &((*curr)->next) : nullptr;
526 }
527 }
528 }
529 };
530
531public:
534
535 SignalBase() noexcept : m_block(false) {}
538 }
539
540 SignalBase(const SignalBase&) = delete;
541 SignalBase & operator=(const SignalBase&) = delete;
542
544 : m_block{o.m_block.load()}
545 {
546 lock_type lock(o.m_mutex);
547 std::swap(m_func, o.m_func);
548 }
549
551 std::scoped_lock lock(m_mutex, o.m_mutex);
552
553 std::swap(m_func, o.m_func);
554 m_block.store(o.m_block.exchange(m_block.load()));
555 return *this;
556 }
557
558 /**
559 * Emit a signal
560 *
561 * Effect: All non blocked and connected slot functions will be called
562 * with supplied arguments.
563 * Safety: With proper locking (see wpi::Signal), emission can happen from
564 * multiple threads simultaneously. The guarantees only apply to the
565 * signal object, it does not cover thread safety of potentially
566 * shared state used in slot functions.
567 *
568 * @param a arguments to emit
569 */
570 template <typename... A>
571 void operator()(A && ... a) const {
572 lock_type lock(m_mutex);
573 if (!m_block && m_func) m_func(std::forward<A>(a)...);
574 }
575
576 /**
577 * Connect a callable of compatible arguments
578 *
579 * Effect: Creates and stores a new slot responsible for executing the
580 * supplied callable for every subsequent signal emission.
581 * Safety: Thread-safety depends on locking policy.
582 *
583 * @param c a callable
584 */
585 template <typename Callable>
586 void connect(Callable && c) {
587 if (!m_func) {
588 m_func = std::forward<Callable>(c);
589 } else {
591 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
592 add_slot(s);
593 }
594 }
595
596 /**
597 * Connect a callable of compatible arguments, returning a Connection
598 *
599 * Effect: Creates and stores a new slot responsible for executing the
600 * supplied callable for every subsequent signal emission.
601 * Safety: Thread-safety depends on locking policy.
602 *
603 * @param c a callable
604 * @return a Connection object that can be used to interact with the slot
605 */
606 template <typename Callable>
607 std::enable_if_t<trait::is_callable_v<arg_list, Callable>, Connection>
608 connect_connection(Callable && c) {
610 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
611 add_slot(s);
612 return Connection(s);
613 }
614
615 /**
616 * Connect a callable with an additional Connection argument
617 *
618 * The callable's first argument must be of type Connection. This overload
619 * the callable to manage it's own connection through this argument.
620 *
621 * @param c a callable
622 * @return a Connection object that can be used to interact with the slot
623 */
624 template <typename Callable>
625 std::enable_if_t<trait::is_callable_v<ext_arg_list, Callable>, Connection>
626 connect_extended(Callable && c) {
628 auto s = std::make_shared<slot_t>(std::forward<Callable>(c));
629 s->conn = Connection(s);
630 add_slot(s);
631 return Connection(s);
632 }
633
634 /**
635 * Overload of connect for pointers over member functions
636 *
637 * @param pmf a pointer over member function
638 * @param ptr an object pointer
639 * @return a Connection object that can be used to interact with the slot
640 */
641 template <typename Pmf, typename Ptr>
642 std::enable_if_t<trait::is_callable_v<arg_list, Pmf, Ptr> &&
643 !trait::is_weak_ptr_compatible_v<Ptr>, Connection>
644 connect(Pmf && pmf, Ptr && ptr) {
646 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr));
647 add_slot(s);
648 return Connection(s);
649 }
650
651 /**
652 * Overload of connect for pointer over member functions and
653 *
654 * @param pmf a pointer over member function
655 * @param ptr an object pointer
656 * @return a Connection object that can be used to interact with the slot
657 */
658 template <typename Pmf, typename Ptr>
659 std::enable_if_t<trait::is_callable_v<ext_arg_list, Pmf, Ptr> &&
660 !trait::is_weak_ptr_compatible_v<Ptr>, Connection>
661 connect_extended(Pmf && pmf, Ptr && ptr) {
663 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr));
664 s->conn = Connection(s);
665 add_slot(s);
666 return Connection(s);
667 }
668
669 /**
670 * Overload of connect for lifetime object tracking and automatic disconnection
671 *
672 * Ptr must be convertible to an object following a loose form of weak pointer
673 * concept, by implementing the ADL-detected conversion function to_weak().
674 *
675 * This overload covers the case of a pointer over member function and a
676 * trackable pointer of that class.
677 *
678 * Note: only weak references are stored, a slot does not extend the lifetime
679 * of a suppied object.
680 *
681 * @param pmf a pointer over member function
682 * @param ptr a trackable object pointer
683 * @return a Connection object that can be used to interact with the slot
684 */
685 template <typename Pmf, typename Ptr>
686 std::enable_if_t<!trait::is_callable_v<arg_list, Pmf> &&
687 trait::is_weak_ptr_compatible_v<Ptr>, Connection>
688 connect(Pmf && pmf, Ptr && ptr) {
689 using trait::to_weak;
690 auto w = to_weak(std::forward<Ptr>(ptr));
691 using slot_t = detail::SlotPmfTracked<Pmf, decltype(w), arg_list>;
692 auto s = std::make_shared<slot_t>(std::forward<Pmf>(pmf), w);
693 add_slot(s);
694 return Connection(s);
695 }
696
697 /**
698 * Overload of connect for lifetime object tracking and automatic disconnection
699 *
700 * Trackable must be convertible to an object following a loose form of weak
701 * pointer concept, by implementing the ADL-detected conversion function to_weak().
702 *
703 * This overload covers the case of a standalone callable and unrelated trackable
704 * object.
705 *
706 * Note: only weak references are stored, a slot does not extend the lifetime
707 * of a suppied object.
708 *
709 * @param c a callable
710 * @param ptr a trackable object pointer
711 * @return a Connection object that can be used to interact with the slot
712 */
713 template <typename Callable, typename Trackable>
714 std::enable_if_t<trait::is_callable_v<arg_list, Callable> &&
715 trait::is_weak_ptr_compatible_v<Trackable>, Connection>
716 connect(Callable && c, Trackable && ptr) {
717 using trait::to_weak;
718 auto w = to_weak(std::forward<Trackable>(ptr));
719 using slot_t = detail::SlotTracked<Callable, decltype(w), arg_list>;
720 auto s = std::make_shared<slot_t>(std::forward<Callable>(c), w);
721 add_slot(s);
722 return Connection(s);
723 }
724
725 /**
726 * Creates a connection whose duration is tied to the return object
727 * Use the same semantics as connect
728 */
729 template <typename... CallArgs>
730 ScopedConnection connect_scoped(CallArgs && ...args) {
731 return connect_connection(std::forward<CallArgs>(args)...);
732 }
733
734 /**
735 * Disconnects all the slots
736 * Safety: Thread safety depends on locking policy
737 */
739 lock_type lock(m_mutex);
740 clear();
741 }
742
743 /**
744 * Blocks signal emission
745 * Safety: thread safe
746 */
747 void block() noexcept {
748 m_block.store(true);
749 }
750
751 /**
752 * Unblocks signal emission
753 * Safety: thread safe
754 */
755 void unblock() noexcept {
756 m_block.store(false);
757 }
758
759 /**
760 * Tests blocking state of signal emission
761 */
762 bool blocked() const noexcept {
763 return m_block.load();
764 }
765
766private:
767 template <typename S>
768 void add_slot(S &s) {
769 lock_type lock(m_mutex);
770 if (!m_func) {
771 // nothing stored
772 m_func = CallSlots(*this);
773 auto slots = m_func.template target<CallSlots>();
774 s->next = slots->m_slots;
775 slots->m_slots = s;
776 } else if (auto call_slots = m_func.template target<CallSlots>()) {
777 // already CallSlots
778 s->next = call_slots->m_slots;
779 call_slots->m_slots = s;
780 } else {
781 // was normal std::function, need to move it into a call slot
782 using slot_t = detail::Slot<std::function<void(T...)>, arg_list>;
783 auto s2 = std::make_shared<slot_t>(
784 std::forward<std::function<void(T...)>>(m_func));
785 m_func = CallSlots(*this);
786 auto slots = m_func.template target<CallSlots>();
787 s2->next = slots->m_slots;
788 s->next = s2;
789 slots->m_slots = s;
790 }
791 }
792
793 void clear() {
794 m_func = nullptr;
795 }
796
797private:
798 std::function<void(T...)> m_func;
799 mutable Lockable m_mutex;
800 std::atomic<bool> m_block;
801};
802
803/**
804 * Specialization of SignalBase to be used in single threaded contexts.
805 * Slot connection, disconnection and signal emission are not thread-safe.
806 * This is significantly smaller than the thread-safe variant.
807 */
808template <typename... T>
810
811/**
812 * Specialization of SignalBase to be used in multi-threaded contexts.
813 * Slot connection, disconnection and signal emission are thread-safe.
814 *
815 * Beware of accidentally using recursive signal emission or cycles between
816 * two or more signals in your code. Locking std::mutex more than once is
817 * undefined behavior, even if it "seems to work somehow". Use signal_r
818 * instead for that use case.
819 */
820template <typename... T>
822
823/**
824 * Specialization of SignalBase to be used in multi-threaded contexts, allowing
825 * for recursive signal emission and emission cycles.
826 * Slot connection, disconnection and signal emission are thread-safe.
827 */
828template <typename... T>
830
831} // namespace sig
832} // namespace wpi
ConnectionBlocker is a RAII object that blocks a connection until destruction.
Definition: Signal.h:154
~ConnectionBlocker() noexcept
Definition: Signal.h:157
ConnectionBlocker(const ConnectionBlocker &)=delete
ConnectionBlocker & operator=(const ConnectionBlocker &)=delete
ConnectionBlocker & operator=(ConnectionBlocker &&o) noexcept
Definition: Signal.h:166
ConnectionBlocker(ConnectionBlocker &&o) noexcept
Definition: Signal.h:162
A Connection object allows interaction with an ongoing slot connection.
Definition: Signal.h:198
void unblock() noexcept
Definition: Signal.h:233
bool connected() const noexcept
Definition: Signal.h:212
ConnectionBlocker blocker() const noexcept
Definition: Signal.h:239
virtual ~Connection()=default
bool blocked() const noexcept
Definition: Signal.h:222
friend class SignalBase
Definition: Signal.h:244
Connection & operator=(const Connection &) noexcept=default
Connection(const Connection &) noexcept=default
void block() noexcept
Definition: Signal.h:227
Connection(std::weak_ptr< detail::SlotState > s) noexcept
Definition: Signal.h:245
std::weak_ptr< detail::SlotState > m_state
Definition: Signal.h:250
bool valid() const noexcept
Definition: Signal.h:208
Connection(Connection &&) noexcept=default
bool disconnect() noexcept
Definition: Signal.h:217
ScopedConnection is a RAII version of Connection It disconnects the slot from the signal upon destruc...
Definition: Signal.h:257
ScopedConnection(ScopedConnection &&o) noexcept
Definition: Signal.h:270
ScopedConnection(const ScopedConnection &) noexcept=delete
friend class SignalBase
Definition: Signal.h:281
~ScopedConnection()
Definition: Signal.h:260
ScopedConnection(const Connection &c) noexcept
Definition: Signal.h:264
ScopedConnection & operator=(const ScopedConnection &) noexcept=delete
ScopedConnection(Connection &&c) noexcept
Definition: Signal.h:265
ScopedConnection & operator=(ScopedConnection &&o) noexcept
Definition: Signal.h:274
SignalBase is an implementation of the observer pattern, through the use of an emitting object and sl...
Definition: Signal.h:495
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:716
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:661
~SignalBase()
Definition: Signal.h:536
void disconnect_all()
Disconnects all the slots Safety: Thread safety depends on locking policy.
Definition: Signal.h:738
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:688
SignalBase() noexcept
Definition: Signal.h:535
bool blocked() const noexcept
Tests blocking state of signal emission.
Definition: Signal.h:762
void block() noexcept
Blocks signal emission Safety: thread safe.
Definition: Signal.h:747
SignalBase & operator=(const SignalBase &)=delete
void unblock() noexcept
Unblocks signal emission Safety: thread safe.
Definition: Signal.h:755
SignalBase(const SignalBase &)=delete
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:730
SignalBase & operator=(SignalBase &&o)
Definition: Signal.h:550
trait::typelist< T... > arg_list
Definition: Signal.h:532
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:626
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:608
SignalBase(SignalBase &&o)
Definition: Signal.h:543
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:644
void connect(Callable &&c)
Connect a callable of compatible arguments.
Definition: Signal.h:586
void operator()(A &&... a) const
Emit a signal.
Definition: Signal.h:571
constexpr Slot(F &&f)
Definition: Signal.h:329
virtual void call_slot(Args ...args) override
Definition: Signal.h:331
virtual void call_slot(Args ...args) override
Definition: Signal.h:348
virtual void call_slot(Args ...args) override
Definition: Signal.h:371
constexpr Slot(F &&f, P &&p)
Definition: Signal.h:367
virtual void call_slot(Args ...args) override
Definition: Signal.h:391
Definition: Signal.h:300
virtual ~SlotBase() noexcept=default
SlotPtr< Args... > next
Definition: Signal.h:316
Definition: Signal.h:319
constexpr SlotPmfTracked(F &&f, P &&p)
Definition: Signal.h:443
virtual void call_slot(Args ...args) override
Definition: Signal.h:448
Definition: Signal.h:432
Definition: Signal.h:129
bool connected() const noexcept
Definition: Signal.h:137
virtual ~SlotState()=default
void unblock() noexcept
Definition: Signal.h:142
bool disconnect() noexcept
Definition: Signal.h:138
void block() noexcept
Definition: Signal.h:141
constexpr SlotState() noexcept
Definition: Signal.h:131
bool blocked() const noexcept
Definition: Signal.h:140
constexpr SlotTracked(F &&f, P &&p)
Definition: Signal.h:413
virtual void call_slot(Args ...args) override
Definition: Signal.h:418
Definition: Signal.h:402
auto ptr(T p) -> const void *
\rst Converts p to const void* for pointer formatting.
Definition: format.h:4100
detail namespace with internal helper functions
Definition: xchar.h:20
type
Definition: core.h:556
Definition: array.h:89
WPI_BASIC_JSON_TPL_DECLARATION void swap(wpi::WPI_BASIC_JSON_TPL &j1, wpi::WPI_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< wpi::WPI_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< wpi::WPI_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.h:5219
static constexpr const unit_t< compound_unit< charge::coulomb, inverse< substance::mol > > > F(N_A *e)
Faraday constant.
static constexpr const velocity::meters_per_second_t c(299792458.0)
Speed of light in vacuum.
cubed< length::millimeter > L
Definition: volume.h:49
std::shared_ptr< SlotBase< T... > > SlotPtr
Definition: Signal.h:293
typename detail::voider< T... >::type void_t
Definition: Signal.h:78
constexpr bool is_callable_v
determine if a type T (Callable or Pmf) is callable with supplied arguments in L
Definition: Signal.h:119
std::weak_ptr< T > to_weak(std::weak_ptr< T > w)
Pointers that can be converted to a weak pointer concept for tracking purpose must implement the to_w...
Definition: Signal.h:61
std::weak_ptr< T > to_weak(std::shared_ptr< T > s)
Definition: Signal.h:66
constexpr bool is_weak_ptr_compatible_v
determine if a pointer is convertible into a "weak" pointer
Definition: Signal.h:115
Definition: ntcore_cpp.h:26
Definition: Signal.h:465
NullMutex(NullMutex &&)=delete
NullMutex operator=(const NullMutex &)=delete
void lock()
Definition: Signal.h:473
NullMutex operator=(NullMutex &&)=delete
bool try_lock()
Definition: Signal.h:472
void unlock()
Definition: Signal.h:474
NullMutex(const NullMutex &)=delete
Definition: Signal.h:74
void type
Definition: Signal.h:74
represent a list of types
Definition: Signal.h:52
::std::recursive_mutex recursive_mutex
Definition: mutex.h:18
::std::mutex mutex
Definition: mutex.h:17
#define S(label, offset, message)
Definition: Errors.h:119