GA::kit v0.3
G&A's in-house C++ application framework
Loading...
Searching...
No Matches
sigslot.hpp
Go to the documentation of this file.
1/*
2MIT License
3
4Copyright (c) 2017 Pierre-Antoine Lacaze
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26#include <atomic>
27#include <memory>
28#include <mutex>
29#include <type_traits>
30#include <utility>
31#include <thread>
32#include <vector>
33
34#if defined __clang__ || (__GNUC__ > 5)
35#define SIGSLOT_MAY_ALIAS __attribute__((__may_alias__))
36#else
37#define SIGSLOT_MAY_ALIAS
38#endif
39
40#if defined(__GXX_RTTI) || defined(__cpp_rtti) || defined(_CPPRTTI)
41#define SIGSLOT_RTTI_ENABLED 1
42#include <typeinfo>
43#endif
44
45namespace sigslot {
46
47namespace detail {
48
49// Used to detect an object of observer type
50struct observer_type {};
51
52} // namespace detail
53
54namespace trait {
55
57template <typename...> struct typelist {};
58
65template <typename T>
66std::weak_ptr<T> to_weak(std::weak_ptr<T> w) {
67 return w;
68}
69
70template <typename T>
71std::weak_ptr<T> to_weak(std::shared_ptr<T> s) {
72 return s;
73}
74
75// tools
76namespace detail {
77
78template <typename...>
79struct voider { using type = void; };
80
81// void_t from c++17
82template <typename...T>
83using void_t = typename detail::voider<T...>::type;
84
85template <typename, typename = void>
86struct has_call_operator : std::false_type {};
87
88template <typename F>
89struct has_call_operator<F, void_t<decltype(&std::remove_reference<F>::type::operator())>>
90 : std::true_type {};
91
92template <typename, typename, typename = void, typename = void>
93struct is_callable : std::false_type {};
94
95template <typename F, typename P, typename... T>
96struct is_callable<F, P, typelist<T...>,
97 void_t<decltype(((*std::declval<P>()).*std::declval<F>())(std::declval<T>()...))>>
98 : std::true_type {};
99
100template <typename F, typename... T>
101struct is_callable<F, typelist<T...>,
102 void_t<decltype(std::declval<F>()(std::declval<T>()...))>>
103 : std::true_type {};
104
105
106template <typename T, typename = void>
107struct is_weak_ptr : std::false_type {};
108
109template <typename T>
110struct is_weak_ptr<T, void_t<decltype(std::declval<T>().expired()),
111 decltype(std::declval<T>().lock()),
112 decltype(std::declval<T>().reset())>>
113 : std::true_type {};
114
115template <typename T, typename = void>
116struct is_weak_ptr_compatible : std::false_type {};
117
118template <typename T>
119struct is_weak_ptr_compatible<T, void_t<decltype(to_weak(std::declval<T>()))>>
120 : is_weak_ptr<decltype(to_weak(std::declval<T>()))> {};
121
122} // namespace detail
123
124static constexpr bool with_rtti =
125#ifdef SIGSLOT_RTTI_ENABLED
126 true;
127#else
128 false;
129#endif
130
132template <typename P>
134
136template <typename L, typename... T>
137constexpr bool is_callable_v = detail::is_callable<T..., L>::value;
138
139template <typename T>
141
142template <typename T>
144
145template <typename T>
146constexpr bool is_pointer_v = std::is_pointer<T>::value;
147
148template <typename T>
149constexpr bool is_func_v = std::is_function<T>::value;
150
151template <typename T>
152constexpr bool is_pmf_v = std::is_member_function_pointer<T>::value;
153
154template <typename T>
155constexpr bool is_observer_v = std::is_base_of<::sigslot::detail::observer_type,
156 std::remove_pointer_t<T>>::value;
157
158} // namespace trait
159
160template <typename, typename...>
161class signal_base;
162
166using group_id = std::int32_t;
167
168namespace detail {
169
177/*
178 * Function pointers and member function pointers size differ from compiler to
179 * compiler, and for virtual members compared to non virtual members. On some
180 * compilers, multiple inheritance has an impact too. Hence, we form an union
181 * big enough to store any kind of function pointer.
182 */
183namespace mock {
184
185struct a { virtual ~a() = default; void f(); virtual void g(); };
186struct b { virtual ~b() = default; virtual void h(); };
187struct c : a, b { void g() override; };
188
190 decltype(&c::g) m;
191 decltype(&a::g) v;
192 decltype(&a::f) d;
193 void (*f)();
194 void *o;
195 };
196
197} // namespace mock
198
199/*
200 * This union is used to compare function pointers
201 * Generic callables cannot be compared. Here we compare pointers but there is
202 * no guarantee that this always works.
203 */
205 void* value() {
206 return &data[0];
207 }
208
209 const void* value() const {
210 return &data[0];
211 }
212
213 template <typename T>
214 T& value() {
215 return *static_cast<T*>(value());
216 }
217
218 template <typename T>
219 const T& value() const {
220 return *static_cast<const T*>(value());
221 }
222
223 inline explicit operator bool() const {
224 return value() != nullptr;
225 }
226
227 inline bool operator==(const func_ptr &o) const {
228 return std::equal(std::begin(data), std::end(data), std::begin(o.data));
229 }
230
232 char data[sizeof(mock::fun_types)];
233};
234
235
236template <typename T, typename = void>
238 static void ptr(const T &/*t*/, func_ptr &d) {
239 d.value<std::nullptr_t>() = nullptr;
240 }
241
242 static constexpr bool is_disconnectable = false;
243 static constexpr bool must_check_object = true;
244};
245
246template <typename T>
247struct function_traits<T, std::enable_if_t<trait::is_func_v<T>>> {
248 static void ptr(T &t, func_ptr &d) {
249 d.value<T*>() = &t;
250 }
251
252 static constexpr bool is_disconnectable = true;
253 static constexpr bool must_check_object = false;
254};
255
256template <typename T>
257struct function_traits<T*, std::enable_if_t<trait::is_func_v<T>>> {
258 static void ptr(T *t, func_ptr &d) {
259 d.value<T*>() = t;
260 }
261
262 static constexpr bool is_disconnectable = true;
263 static constexpr bool must_check_object = false;
264};
265
266template <typename T>
267struct function_traits<T, std::enable_if_t<trait::is_pmf_v<T>>> {
268 static void ptr(const T &t, func_ptr &d) {
269 d.value<T>() = t;
270 }
271
272 static constexpr bool is_disconnectable = trait::with_rtti;
273 static constexpr bool must_check_object = true;
274};
275
276// for function objects, the assumption is that we are looking for the call operator
277template <typename T>
278struct function_traits<T, std::enable_if_t<trait::has_call_operator_v<T>>> {
279 using call_type = decltype(&std::remove_reference<T>::type::operator());
280
281 static void ptr(const T &/*t*/, func_ptr &d) {
282 function_traits<call_type>::ptr(&T::operator(), d);
283 }
284
285 static constexpr bool is_disconnectable = function_traits<call_type>::is_disconnectable;
286 static constexpr bool must_check_object = function_traits<call_type>::must_check_object;
287};
288
289template <typename T>
291 func_ptr d;
292 std::uninitialized_fill(std::begin(d.data), std::end(d.data), '\0');
294 return d;
295}
296
297/*
298 * obj_ptr is used to store a pointer to an object.
299 * The object_pointer traits are needed to handle trackable objects correctly,
300 * as they are likely to not be pointers.
301 */
302using obj_ptr = const void*;
303
304template <typename T>
305obj_ptr get_object_ptr(const T &t);
306
307template <typename T, typename = void>
309 static obj_ptr get(const T&) {
310 return nullptr;
311 }
312};
313
314template <typename T>
315struct object_pointer<T*, std::enable_if_t<trait::is_pointer_v<T*>>> {
316 static obj_ptr get(const T *t) {
317 return reinterpret_cast<obj_ptr>(t);
318 }
319};
320
321template <typename T>
322struct object_pointer<T, std::enable_if_t<trait::is_weak_ptr_v<T>>> {
323 static obj_ptr get(const T &t) {
324 auto p = t.lock();
325 return get_object_ptr(p);
326 }
327};
328
329template <typename T>
330struct object_pointer<T, std::enable_if_t<!trait::is_pointer_v<T> &&
331 !trait::is_weak_ptr_v<T> &&
332 trait::is_weak_ptr_compatible_v<T>>>
333{
334 static obj_ptr get(const T &t) {
335 return t ? reinterpret_cast<obj_ptr>(t.get()) : nullptr;
336 }
337};
338
339template <typename T>
341 return object_pointer<T>::get(t);
342}
343
344
345// noop mutex for thread-unsafe use
347 null_mutex() noexcept = default;
348 ~null_mutex() noexcept = default;
349 null_mutex(const null_mutex &) = delete;
350 null_mutex& operator=(const null_mutex &) = delete;
351 null_mutex(null_mutex &&) = delete;
352 null_mutex& operator=(null_mutex &&) = delete;
353
354 inline bool try_lock() noexcept { return true; }
355 inline void lock() noexcept {}
356 inline void unlock() noexcept {}
357};
358
365 spin_mutex() noexcept = default;
366 ~spin_mutex() noexcept = default;
367 spin_mutex(spin_mutex const&) = delete;
368 spin_mutex& operator=(const spin_mutex &) = delete;
369 spin_mutex(spin_mutex &&) = delete;
370 spin_mutex& operator=(spin_mutex &&) = delete;
371
372 void lock() noexcept {
373 while (true) {
374 while (!state.load(std::memory_order_relaxed)) {
375 std::this_thread::yield();
376 }
377
378 if (try_lock()) {
379 break;
380 }
381 }
382 }
383
384 bool try_lock() noexcept {
385 return state.exchange(false, std::memory_order_acquire);
386 }
387
388 void unlock() noexcept {
389 state.store(true, std::memory_order_release);
390 }
391
392private:
393 std::atomic<bool> state {true};
394};
395
400template <typename T>
402 struct payload {
403 payload() = default;
404
405 template <typename... Args>
406 explicit payload(Args && ...args)
407 : value(std::forward<Args>(args)...)
408 {}
409
410 std::atomic<std::size_t> count{1};
411 T value;
412 };
413
414public:
415 using element_type = T;
416
418 : m_data(new payload)
419 {}
420
421 template <typename U>
422 explicit copy_on_write(U && x, std::enable_if_t<!std::is_same<std::decay_t<U>,
423 copy_on_write>::value>* = nullptr)
424 : m_data(new payload(std::forward<U>(x)))
425 {}
426
427 copy_on_write(const copy_on_write &x) noexcept
428 : m_data(x.m_data)
429 {
430 ++m_data->count;
431 }
432
434 : m_data(x.m_data)
435 {
436 x.m_data = nullptr;
437 }
438
440 if (m_data && (--m_data->count == 0)) {
441 delete m_data;
442 }
443 }
444
446 if (&x != this) {
447 *this = copy_on_write(x);
448 }
449 return *this;
450 }
451
453 auto tmp = std::move(x);
454 swap(*this, tmp);
455 return *this;
456 }
457
459 if (!unique()) {
460 *this = copy_on_write(read());
461 }
462 return m_data->value;
463 }
464
465 const element_type& read() const noexcept {
466 return m_data->value;
467 }
468
469 friend inline void swap(copy_on_write &x, copy_on_write &y) noexcept {
470 using std::swap;
471 swap(x.m_data, y.m_data);
472 }
473
474private:
475 bool unique() const noexcept {
476 return m_data->count == 1;
477 }
478
479private:
480 payload *m_data;
481};
482
486template <typename T>
487const T& cow_read(const T &v) {
488 return v;
489}
490
491template <typename T>
493 return v.read();
494}
495
496template <typename T>
497T& cow_write(T &v) {
498 return v;
499}
500
501template <typename T>
503 return v.write();
504}
505
513#ifdef SIGSLOT_REDUCE_COMPILE_TIME
514template <typename B, typename D, typename ...Arg>
515inline std::shared_ptr<B> make_shared(Arg && ... arg) {
516 return std::shared_ptr<B>(static_cast<B*>(new D(std::forward<Arg>(arg)...)));
517}
518#else
519template <typename B, typename D, typename ...Arg>
520inline std::shared_ptr<B> make_shared(Arg && ... arg) {
521 return std::static_pointer_cast<B>(std::make_shared<D>(std::forward<Arg>(arg)...));
522}
523#endif
524
525/* slot_state holds slot type independent state, to be used to interact with
526 * slots indirectly through connection and scoped_connection objects.
527 */
529public:
530 constexpr slot_state(group_id gid) noexcept
531 : m_index(0)
532 , m_group(gid)
533 , m_connected(true)
534 , m_blocked(false)
535 {}
536
537 virtual ~slot_state() = default;
538
539 virtual bool connected() const noexcept { return m_connected; }
540
541 bool disconnect() noexcept {
542 bool ret = m_connected.exchange(false);
543 if (ret) {
544 do_disconnect();
545 }
546 return ret;
547 }
548
549 bool blocked() const noexcept { return m_blocked.load(); }
550 void block() noexcept { m_blocked.store(true); }
551 void unblock() noexcept { m_blocked.store(false); }
552
553protected:
554 virtual void do_disconnect() {}
555
556 auto index() const {
557 return m_index;
558 }
559
560 auto& index() {
561 return m_index;
562 }
563
564 group_id group() const {
565 return m_group;
566 }
567
568private:
569 template <typename, typename...>
570 friend class ::sigslot::signal_base;
571
572 std::size_t m_index; // index into the array of slot pointers inside the signal
573 const group_id m_group; // slot group this slot belongs to
574 std::atomic<bool> m_connected;
575 std::atomic<bool> m_blocked;
576};
577
578} // namespace detail
579
584public:
586 ~connection_blocker() noexcept { release(); }
587
590
592 : m_state{std::move(o.m_state)}
593 {}
594
596 release();
597 m_state.swap(o.m_state);
598 return *this;
599 }
600
601private:
602 friend class connection;
603 explicit connection_blocker(std::weak_ptr<detail::slot_state> s) noexcept
604 : m_state{std::move(s)}
605 {
606 if (auto d = m_state.lock()) {
607 d->block();
608 }
609 }
610
611 void release() noexcept {
612 if (auto d = m_state.lock()) {
613 d->unblock();
614 }
615 }
616
617private:
618 std::weak_ptr<detail::slot_state> m_state;
619};
620
621
630public:
631 connection() = default;
632 virtual ~connection() = default;
633
634 connection(const connection &) noexcept = default;
635 connection & operator=(const connection &) noexcept = default;
636 connection(connection &&) noexcept = default;
637 connection & operator=(connection &&) noexcept = default;
638
639 bool valid() const noexcept {
640 return !m_state.expired();
641 }
642
643 bool connected() const noexcept {
644 const auto d = m_state.lock();
645 return d && d->connected();
646 }
647
648 bool disconnect() noexcept {
649 auto d = m_state.lock();
650 return d && d->disconnect();
651 }
652
653 bool blocked() const noexcept {
654 const auto d = m_state.lock();
655 return d && d->blocked();
656 }
657
658 void block() noexcept {
659 if (auto d = m_state.lock()) {
660 d->block();
661 }
662 }
663
664 void unblock() noexcept {
665 if (auto d = m_state.lock()) {
666 d->unblock();
667 }
668 }
669
670 connection_blocker blocker() const noexcept {
671 return connection_blocker{m_state};
672 }
673
674protected:
675 template <typename, typename...> friend class signal_base;
676 explicit connection(std::weak_ptr<detail::slot_state> s) noexcept
677 : m_state{std::move(s)}
678 {}
679
680protected:
681 std::weak_ptr<detail::slot_state> m_state;
682};
683
688class scoped_connection final : public connection {
689public:
690 scoped_connection() = default;
692 disconnect();
693 }
694
695 /*implicit*/ scoped_connection(const connection &c) noexcept : connection(c) {}
696 /*implicit*/ scoped_connection(connection &&c) noexcept : connection(std::move(c)) {}
697
698 scoped_connection(const scoped_connection &) noexcept = delete;
699 scoped_connection & operator=(const scoped_connection &) noexcept = delete;
700
702 : connection{std::move(o.m_state)}
703 {}
704
706 disconnect();
707 m_state.swap(o.m_state);
708 return *this;
709 }
710
711private:
712 template <typename, typename...> friend class signal_base;
713 explicit scoped_connection(std::weak_ptr<detail::slot_state> s) noexcept
714 : connection{std::move(s)}
715 {}
716};
717
726template <typename Lockable>
728 virtual ~observer_base() = default;
729
730protected:
739 std::unique_lock<Lockable> _{m_mutex};
740 m_connections.clear();
741 }
742
743private:
744 template <typename, typename ...>
745 friend class signal_base;
746
747 void add_connection(connection conn) {
748 std::unique_lock<Lockable> _{m_mutex};
749 m_connections.emplace_back(std::move(conn));
750 }
751
752 Lockable m_mutex;
753 std::vector<scoped_connection> m_connections;
754};
755
760
765
766
767namespace detail {
768
769// interface for cleanable objects, used to cleanup disconnected slots
770struct cleanable {
771 virtual ~cleanable() = default;
772 virtual void clean(slot_state *) = 0;
773};
774
775template <typename...>
776class slot_base;
777
778template <typename... T>
779using slot_ptr = std::shared_ptr<slot_base<T...>>;
780
781
782/* A base class for slot objects. This base type only depends on slot argument
783 * types, it will be used as an element in an intrusive singly-linked list of
784 * slots, hence the public next member.
785 */
786template <typename... Args>
787class slot_base : public slot_state {
788public:
789 using base_types = trait::typelist<Args...>;
790
791 explicit slot_base(cleanable &c, group_id gid)
792 : slot_state(gid)
793 , cleaner(c)
794 {}
795 ~slot_base() override = default;
796
797 // method effectively responsible for calling the "slot" function with
798 // supplied arguments whenever emission happens.
799 virtual void call_slot(Args...) = 0;
800
801 template <typename... U>
802 void operator()(U && ...u) {
803 if (slot_state::connected() && !slot_state::blocked()) {
804 call_slot(std::forward<U>(u)...);
805 }
806 }
807
808 // check if we are storing callable c
809 template <typename C>
810 bool has_callable(const C &c) const {
811 auto cp = get_function_ptr(c);
812 auto p = get_callable();
813 return cp && p && cp == p;
814 }
815
816 template <typename C>
817 std::enable_if_t<function_traits<C>::must_check_object, bool>
818 has_full_callable(const C &c) const {
819 return has_callable(c) && check_class_type<std::decay_t<C>>();
820 }
821
822 template <typename C>
823 std::enable_if_t<!function_traits<C>::must_check_object, bool>
824 has_full_callable(const C &c) const {
825 return has_callable(c);
826 }
827
828 // check if we are storing object o
829 template <typename O>
830 bool has_object(const O &o) const {
831 return get_object() == get_object_ptr(o);
832 }
833
834protected:
835 void do_disconnect() final {
836 cleaner.clean(this);
837 }
838
839 // retieve a pointer to the object embedded in the slot
840 virtual obj_ptr get_object() const noexcept {
841 return nullptr;
842 }
843
844 // retieve a pointer to the callable embedded in the slot
845 virtual func_ptr get_callable() const noexcept {
846 return get_function_ptr(nullptr);
847 }
848
849#ifdef SIGSLOT_RTTI_ENABLED
850 // retieve a pointer to the callable embedded in the slot
851 virtual const std::type_info& get_callable_type() const noexcept {
852 return typeid(nullptr);
853 }
854
855private:
856 template <typename U>
857 bool check_class_type() const {
858 return typeid(U) == get_callable_type();
859 }
860
861#else
862 template <typename U>
863 bool check_class_type() const {
864 return false;
865 }
866#endif
867
868private:
869 cleanable &cleaner;
870};
871
872/*
873 * A slot object holds state information, and a callable to to be called
874 * whenever the function call operator of its slot_base base class is called.
875 */
876template <typename Func, typename... Args>
877class slot final : public slot_base<Args...> {
878public:
879 template <typename F, typename Gid>
880 constexpr slot(cleanable &c, F && f, Gid gid)
881 : slot_base<Args...>(c, gid)
882 , func{std::forward<F>(f)} {}
883
884protected:
885 void call_slot(Args ...args) override {
886 func(args...);
887 }
888
889 func_ptr get_callable() const noexcept override {
890 return get_function_ptr(func);
891 }
892
893#ifdef SIGSLOT_RTTI_ENABLED
894 const std::type_info& get_callable_type() const noexcept override {
895 return typeid(func);
896 }
897#endif
898
899private:
900 std::decay_t<Func> func;
901};
902
903/*
904 * Variation of slot that prepends a connection object to the callable
905 */
906template <typename Func, typename... Args>
907class slot_extended final : public slot_base<Args...> {
908public:
909 template <typename F>
910 constexpr slot_extended(cleanable &c, F && f, group_id gid)
911 : slot_base<Args...>(c, gid)
912 , func{std::forward<F>(f)} {}
913
915
916protected:
917 void call_slot(Args ...args) override {
918 func(conn, args...);
919 }
920
921 func_ptr get_callable() const noexcept override {
922 return get_function_ptr(func);
923 }
924
925#ifdef SIGSLOT_RTTI_ENABLED
926 const std::type_info& get_callable_type() const noexcept override {
927 return typeid(func);
928 }
929#endif
930
931private:
932 std::decay_t<Func> func;
933};
934
935/*
936 * A slot object holds state information, an object and a pointer over member
937 * function to be called whenever the function call operator of its slot_base
938 * base class is called.
939 */
940template <typename Pmf, typename Ptr, typename... Args>
941class slot_pmf final : public slot_base<Args...> {
942public:
943 template <typename F, typename P>
944 constexpr slot_pmf(cleanable &c, F && f, P && p, group_id gid)
945 : slot_base<Args...>(c, gid)
946 , pmf{std::forward<F>(f)}
947 , ptr{std::forward<P>(p)} {}
948
949protected:
950 void call_slot(Args ...args) override {
951 ((*ptr).*pmf)(args...);
952 }
953
954 func_ptr get_callable() const noexcept override {
955 return get_function_ptr(pmf);
956 }
957
958 obj_ptr get_object() const noexcept override {
959 return get_object_ptr(ptr);
960 }
961
962#ifdef SIGSLOT_RTTI_ENABLED
963 const std::type_info& get_callable_type() const noexcept override {
964 return typeid(pmf);
965 }
966#endif
967
968private:
969 std::decay_t<Pmf> pmf;
970 std::decay_t<Ptr> ptr;
971};
972
973/*
974 * Variation of slot that prepends a connection object to the callable
975 */
976template <typename Pmf, typename Ptr, typename... Args>
977class slot_pmf_extended final : public slot_base<Args...> {
978public:
979 template <typename F, typename P>
980 constexpr slot_pmf_extended(cleanable &c, F && f, P && p, group_id gid)
981 : slot_base<Args...>(c, gid)
982 , pmf{std::forward<F>(f)}
983 , ptr{std::forward<P>(p)} {}
984
986
987protected:
988 void call_slot(Args ...args) override {
989 ((*ptr).*pmf)(conn, args...);
990 }
991
992 func_ptr get_callable() const noexcept override {
993 return get_function_ptr(pmf);
994 }
995 obj_ptr get_object() const noexcept override {
996 return get_object_ptr(ptr);
997 }
998
999#ifdef SIGSLOT_RTTI_ENABLED
1000 const std::type_info& get_callable_type() const noexcept override {
1001 return typeid(pmf);
1002 }
1003#endif
1004
1005private:
1006 std::decay_t<Pmf> pmf;
1007 std::decay_t<Ptr> ptr;
1008};
1009
1010/*
1011 * An implementation of a slot that tracks the life of a supplied object
1012 * through a weak pointer in order to automatically disconnect the slot
1013 * on said object destruction.
1014 */
1015template <typename Func, typename WeakPtr, typename... Args>
1016class slot_tracked final : public slot_base<Args...> {
1017public:
1018 template <typename F, typename P>
1019 constexpr slot_tracked(cleanable &c, F && f, P && p, group_id gid)
1020 : slot_base<Args...>(c, gid)
1021 , func{std::forward<F>(f)}
1022 , ptr{std::forward<P>(p)}
1023 {}
1024
1025 bool connected() const noexcept override {
1026 return !ptr.expired() && slot_state::connected();
1027 }
1028
1029protected:
1030 void call_slot(Args ...args) override {
1031 auto sp = ptr.lock();
1032 if (!sp) {
1033 slot_state::disconnect();
1034 return;
1035 }
1036 if (slot_state::connected()) {
1037 func(args...);
1038 }
1039 }
1040
1041 func_ptr get_callable() const noexcept override {
1042 return get_function_ptr(func);
1043 }
1044
1045 obj_ptr get_object() const noexcept override {
1046 return get_object_ptr(ptr);
1047 }
1048
1049#ifdef SIGSLOT_RTTI_ENABLED
1050 const std::type_info& get_callable_type() const noexcept override {
1051 return typeid(func);
1052 }
1053#endif
1054
1055private:
1056 std::decay_t<Func> func;
1057 std::decay_t<WeakPtr> ptr;
1058};
1059
1060/*
1061 * An implementation of a slot as a pointer over member function, that tracks
1062 * the life of a supplied object through a weak pointer in order to automatically
1063 * disconnect the slot on said object destruction.
1064 */
1065template <typename Pmf, typename WeakPtr, typename... Args>
1066class slot_pmf_tracked final : public slot_base<Args...> {
1067public:
1068 template <typename F, typename P>
1069 constexpr slot_pmf_tracked(cleanable &c, F && f, P && p, group_id gid)
1070 : slot_base<Args...>(c, gid)
1071 , pmf{std::forward<F>(f)}
1072 , ptr{std::forward<P>(p)}
1073 {}
1074
1075 bool connected() const noexcept override {
1076 return !ptr.expired() && slot_state::connected();
1077 }
1078
1079protected:
1080 void call_slot(Args ...args) override {
1081 auto sp = ptr.lock();
1082 if (!sp) {
1083 slot_state::disconnect();
1084 return;
1085 }
1086 if (slot_state::connected()) {
1087 ((*sp).*pmf)(args...);
1088 }
1089 }
1090
1091 func_ptr get_callable() const noexcept override {
1092 return get_function_ptr(pmf);
1093 }
1094
1095 obj_ptr get_object() const noexcept override {
1096 return get_object_ptr(ptr);
1097 }
1098
1099#ifdef SIGSLOT_RTTI_ENABLED
1100 const std::type_info& get_callable_type() const noexcept override {
1101 return typeid(pmf);
1102 }
1103#endif
1104
1105private:
1106 std::decay_t<Pmf> pmf;
1107 std::decay_t<WeakPtr> ptr;
1108};
1109
1110} // namespace detail
1111
1112
1133template <typename Lockable, typename... T>
1134class signal_base final : public detail::cleanable {
1135 template <typename L>
1136 using is_thread_safe = std::integral_constant<bool, !std::is_same<L, detail::null_mutex>::value>;
1137
1138 template <typename U, typename L>
1139 using cow_type = std::conditional_t<is_thread_safe<L>::value,
1141
1142 template <typename U, typename L>
1143 using cow_copy_type = std::conditional_t<is_thread_safe<L>::value,
1144 detail::copy_on_write<U>, const U&>;
1145
1146 using lock_type = std::unique_lock<Lockable>;
1147 using slot_base = detail::slot_base<T...>;
1148 using slot_ptr = detail::slot_ptr<T...>;
1149 using slots_type = std::vector<slot_ptr>;
1150 struct group_type { slots_type slts; group_id gid; };
1151 using list_type = std::vector<group_type>; // kept ordered by ascending gid
1152
1153public:
1156
1157 signal_base() noexcept : m_block(false) {}
1158 ~signal_base() override {
1160 }
1161
1162 signal_base(const signal_base&) = delete;
1164
1165 signal_base(signal_base && o) /* not noexcept */
1166 : m_block{o.m_block.load()}
1167 {
1168 lock_type lock(o.m_mutex);
1169 using std::swap;
1170 swap(m_slots, o.m_slots);
1171 }
1172
1173 signal_base & operator=(signal_base && o) /* not noexcept */ {
1174 lock_type lock1(m_mutex, std::defer_lock);
1175 lock_type lock2(o.m_mutex, std::defer_lock);
1176 std::lock(lock1, lock2);
1177
1178 using std::swap;
1179 swap(m_slots, o.m_slots);
1180 m_block.store(o.m_block.exchange(m_block.load()));
1181 return *this;
1182 }
1183
1196 template <typename... U>
1197 void operator()(U && ...a) {
1198 if (m_block) {
1199 return;
1200 }
1201
1202 // Reference to the slots to execute them out of the lock
1203 // a copy may occur if another thread writes to it.
1204 cow_copy_type<list_type, Lockable> ref = slots_reference();
1205
1206 for (const auto &group : detail::cow_read(ref)) {
1207 for (const auto &s : group.slts) {
1208 s->operator()(a...);
1209 }
1210 }
1211 }
1212
1224 template <typename Callable>
1225 std::enable_if_t<trait::is_callable_v<arg_list, Callable>, connection>
1226 connect(Callable && c, group_id gid = 0) {
1227 using slot_t = detail::slot<Callable, T...>;
1228 auto s = make_slot<slot_t>(std::forward<Callable>(c), gid);
1229 connection conn(s);
1230 add_slot(std::move(s));
1231 return conn;
1232 }
1233
1244 template <typename Callable>
1245 std::enable_if_t<trait::is_callable_v<ext_arg_list, Callable>, connection>
1246 connect_extended(Callable && c, group_id gid = 0) {
1247 using slot_t = detail::slot_extended<Callable, T...>;
1248 auto s = make_slot<slot_t>(std::forward<Callable>(c), gid);
1249 connection conn(s);
1250 std::static_pointer_cast<slot_t>(s)->conn = conn;
1251 add_slot(std::move(s));
1252 return conn;
1253 }
1254
1264 template <typename Pmf, typename Ptr>
1265 std::enable_if_t<trait::is_callable_v<arg_list, Pmf, Ptr> &&
1266 trait::is_observer_v<Ptr>, connection>
1267 connect(Pmf && pmf, Ptr && ptr, group_id gid = 0) {
1268 using slot_t = detail::slot_pmf<Pmf, Ptr, T...>;
1269 auto s = make_slot<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr), gid);
1270 connection conn(s);
1271 add_slot(std::move(s));
1272 ptr->add_connection(conn);
1273 return conn;
1274 }
1275
1284 template <typename Pmf, typename Ptr>
1285 std::enable_if_t<trait::is_callable_v<arg_list, Pmf, Ptr> &&
1286 !trait::is_observer_v<Ptr> &&
1287 !trait::is_weak_ptr_compatible_v<Ptr>, connection>
1288 connect(Pmf && pmf, Ptr && ptr, group_id gid = 0) {
1289 using slot_t = detail::slot_pmf<Pmf, Ptr, T...>;
1290 auto s = make_slot<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr), gid);
1291 connection conn(s);
1292 add_slot(std::move(s));
1293 return conn;
1294 }
1295
1304 template <typename Pmf, typename Ptr>
1305 std::enable_if_t<trait::is_callable_v<ext_arg_list, Pmf, Ptr> &&
1306 !trait::is_weak_ptr_compatible_v<Ptr>, connection>
1307 connect_extended(Pmf && pmf, Ptr && ptr, group_id gid = 0) {
1308 using slot_t = detail::slot_pmf_extended<Pmf, Ptr, T...>;
1309 auto s = make_slot<slot_t>(std::forward<Pmf>(pmf), std::forward<Ptr>(ptr), gid);
1310 connection conn(s);
1311 std::static_pointer_cast<slot_t>(s)->conn = conn;
1312 add_slot(std::move(s));
1313 return conn;
1314 }
1315
1333 template <typename Pmf, typename Ptr>
1334 std::enable_if_t<!trait::is_callable_v<arg_list, Pmf> &&
1335 trait::is_weak_ptr_compatible_v<Ptr>, connection>
1336 connect(Pmf && pmf, Ptr && ptr, group_id gid = 0) {
1337 using trait::to_weak;
1338 auto w = to_weak(std::forward<Ptr>(ptr));
1339 using slot_t = detail::slot_pmf_tracked<Pmf, decltype(w), T...>;
1340 auto s = make_slot<slot_t>(std::forward<Pmf>(pmf), w, gid);
1341 connection conn(s);
1342 add_slot(std::move(s));
1343 return conn;
1344 }
1345
1363 template <typename Callable, typename Trackable>
1364 std::enable_if_t<trait::is_callable_v<arg_list, Callable> &&
1365 trait::is_weak_ptr_compatible_v<Trackable>, connection>
1366 connect(Callable && c, Trackable && ptr, group_id gid = 0) {
1367 using trait::to_weak;
1368 auto w = to_weak(std::forward<Trackable>(ptr));
1369 using slot_t = detail::slot_tracked<Callable, decltype(w), T...>;
1370 auto s = make_slot<slot_t>(std::forward<Callable>(c), w, gid);
1371 connection conn(s);
1372 add_slot(std::move(s));
1373 return conn;
1374 }
1375
1380 template <typename... CallArgs>
1381 scoped_connection connect_scoped(CallArgs && ...args) {
1382 return connect(std::forward<CallArgs>(args)...);
1383 }
1384
1399 template <typename Callable>
1400 std::enable_if_t<(trait::is_callable_v<arg_list, Callable> ||
1401 trait::is_callable_v<ext_arg_list, Callable> ||
1402 trait::is_pmf_v<Callable>) &&
1404 disconnect(const Callable &c) {
1405 return disconnect_if([&] (const auto &s) {
1406 return s->has_full_callable(c);
1407 });
1408 }
1409
1422 template <typename Obj>
1423 std::enable_if_t<!trait::is_callable_v<arg_list, Obj> &&
1424 !trait::is_callable_v<ext_arg_list, Obj> &&
1425 !trait::is_pmf_v<Obj>, size_t>
1426 disconnect(const Obj &obj) {
1427 return disconnect_if([&] (const auto &s) {
1428 return s->has_object(obj);
1429 });
1430 }
1431
1445 template <typename Callable, typename Obj>
1446 size_t disconnect(const Callable &c, const Obj &obj) {
1447 return disconnect_if([&] (const auto &s) {
1448 return s->has_object(obj) && s->has_callable(c);
1449 });
1450 }
1451
1461 size_t disconnect(group_id gid) {
1462 lock_type lock(m_mutex);
1463 for (auto &group : detail::cow_write(m_slots)) {
1464 if (group.gid == gid) {
1465 size_t count = group.slts.size();
1466 group.slts.clear();
1467 return count;
1468 }
1469 }
1470 return 0;
1471 }
1472
1478 lock_type lock(m_mutex);
1479 clear();
1480 }
1481
1486 void block() noexcept {
1487 m_block.store(true);
1488 }
1489
1494 void unblock() noexcept {
1495 m_block.store(false);
1496 }
1497
1501 bool blocked() const noexcept {
1502 return m_block.load();
1503 }
1504
1509 size_t slot_count() noexcept {
1510 cow_copy_type<list_type, Lockable> ref = slots_reference();
1511 size_t count = 0;
1512 for (const auto &g : detail::cow_read(ref)) {
1513 count += g.slts.size();
1514 }
1515 return count;
1516 }
1517
1518protected:
1522 void clean(detail::slot_state *state) override {
1523 lock_type lock(m_mutex);
1524 const auto idx = state->index();
1525 const auto gid = state->group();
1526
1527 // find the group
1528 for (auto &group : detail::cow_write(m_slots)) {
1529 if (group.gid == gid) {
1530 auto &slts = group.slts;
1531
1532 // ensure we have the right slot, in case of concurrent cleaning
1533 if (idx < slts.size() && slts[idx] && slts[idx].get() == state) {
1534 std::swap(slts[idx], slts.back());
1535 slts[idx]->index() = idx;
1536 slts.pop_back();
1537 }
1538
1539 return;
1540 }
1541 }
1542 }
1543
1544private:
1545 // used to get a reference to the slots for reading
1546 inline cow_copy_type<list_type, Lockable> slots_reference() {
1547 lock_type lock(m_mutex);
1548 return m_slots;
1549 }
1550
1551 // create a new slot
1552 template <typename Slot, typename... A>
1553 inline auto make_slot(A && ...a) {
1554 return detail::make_shared<slot_base, Slot>(*this, std::forward<A>(a)...);
1555 }
1556
1557 // add the slot to the list of slots of the right group
1558 void add_slot(slot_ptr &&s) {
1559 const group_id gid = s->group();
1560
1561 lock_type lock(m_mutex);
1562 auto &groups = detail::cow_write(m_slots);
1563
1564 // find the group
1565 auto it = groups.begin();
1566 while (it != groups.end() && it->gid < gid) {
1567 it++;
1568 }
1569
1570 // create a new group if necessary
1571 if (it == groups.end() || it->gid != gid) {
1572 it = groups.insert(it, {{}, gid});
1573 }
1574
1575 // add the slot
1576 s->index() = it->slts.size();
1577 it->slts.push_back(std::move(s));
1578 }
1579
1580 // disconnect a slot if a condition occurs
1581 template <typename Cond>
1582 size_t disconnect_if(Cond && cond) {
1583 lock_type lock(m_mutex);
1584 auto &groups = detail::cow_write(m_slots);
1585
1586 size_t count = 0;
1587
1588 for (auto &group : groups) {
1589 auto &slts = group.slts;
1590 size_t i = 0;
1591 while (i < slts.size()) {
1592 if (cond(slts[i])) {
1593 std::swap(slts[i], slts.back());
1594 slts[i]->index() = i;
1595 slts.pop_back();
1596 ++count;
1597 } else {
1598 ++i;
1599 }
1600 }
1601 }
1602
1603 return count;
1604 }
1605
1606 // to be called under lock: remove all the slots
1607 void clear() {
1608 detail::cow_write(m_slots).clear();
1609 }
1610
1611private:
1612 Lockable m_mutex;
1613 cow_type<list_type, Lockable> m_slots;
1614 std::atomic<bool> m_block;
1615};
1616
1623template <typename... T>
1625
1632template <typename... T>
1633using signal = signal_base<std::mutex, T...>;
1634
1635} // namespace sigslot
Definition: sigslot.hpp:583
~connection_blocker() noexcept
Definition: sigslot.hpp:586
connection_blocker(const connection_blocker &)=delete
connection_blocker(connection_blocker &&o) noexcept
Definition: sigslot.hpp:591
connection_blocker & operator=(connection_blocker &&o) noexcept
Definition: sigslot.hpp:595
friend class connection
Definition: sigslot.hpp:602
connection_blocker & operator=(const connection_blocker &)=delete
Definition: sigslot.hpp:629
void block() noexcept
Definition: sigslot.hpp:658
void unblock() noexcept
Definition: sigslot.hpp:664
bool blocked() const noexcept
Definition: sigslot.hpp:653
connection(const connection &) noexcept=default
bool disconnect() noexcept
Definition: sigslot.hpp:648
virtual ~connection()=default
connection(std::weak_ptr< detail::slot_state > s) noexcept
Definition: sigslot.hpp:676
connection(connection &&) noexcept=default
connection_blocker blocker() const noexcept
Definition: sigslot.hpp:670
bool connected() const noexcept
Definition: sigslot.hpp:643
friend class signal_base
Definition: sigslot.hpp:675
std::weak_ptr< detail::slot_state > m_state
Definition: sigslot.hpp:681
connection & operator=(const connection &) noexcept=default
Definition: sigslot.hpp:401
friend void swap(copy_on_write &x, copy_on_write &y) noexcept
Definition: sigslot.hpp:469
copy_on_write(copy_on_write &&x) noexcept
Definition: sigslot.hpp:433
copy_on_write()
Definition: sigslot.hpp:417
copy_on_write & operator=(const copy_on_write &x) noexcept
Definition: sigslot.hpp:445
element_type & write()
Definition: sigslot.hpp:458
copy_on_write(const copy_on_write &x) noexcept
Definition: sigslot.hpp:427
const element_type & read() const noexcept
Definition: sigslot.hpp:465
copy_on_write(U &&x, std::enable_if_t<!std::is_same< std::decay_t< U >, copy_on_write >::value > *=nullptr)
Definition: sigslot.hpp:422
copy_on_write & operator=(copy_on_write &&x) noexcept
Definition: sigslot.hpp:452
T element_type
Definition: sigslot.hpp:415
~copy_on_write()
Definition: sigslot.hpp:439
Definition: sigslot.hpp:787
virtual obj_ptr get_object() const noexcept
Definition: sigslot.hpp:840
bool has_object(const O &o) const
Definition: sigslot.hpp:830
std::enable_if_t< function_traits< C >::must_check_object, bool > has_full_callable(const C &c) const
Definition: sigslot.hpp:818
bool has_callable(const C &c) const
Definition: sigslot.hpp:810
void do_disconnect() final
Definition: sigslot.hpp:835
slot_base(cleanable &c, group_id gid)
Definition: sigslot.hpp:791
~slot_base() override=default
void operator()(U &&...u)
Definition: sigslot.hpp:802
bool check_class_type() const
Definition: sigslot.hpp:863
virtual func_ptr get_callable() const noexcept
Definition: sigslot.hpp:845
std::enable_if_t<!function_traits< C >::must_check_object, bool > has_full_callable(const C &c) const
Definition: sigslot.hpp:824
virtual void call_slot(Args...)=0
Definition: sigslot.hpp:907
func_ptr get_callable() const noexcept override
Definition: sigslot.hpp:921
connection conn
Definition: sigslot.hpp:914
constexpr slot_extended(cleanable &c, F &&f, group_id gid)
Definition: sigslot.hpp:910
void call_slot(Args ...args) override
Definition: sigslot.hpp:917
Definition: sigslot.hpp:977
connection conn
Definition: sigslot.hpp:985
func_ptr get_callable() const noexcept override
Definition: sigslot.hpp:992
obj_ptr get_object() const noexcept override
Definition: sigslot.hpp:995
constexpr slot_pmf_extended(cleanable &c, F &&f, P &&p, group_id gid)
Definition: sigslot.hpp:980
void call_slot(Args ...args) override
Definition: sigslot.hpp:988
Definition: sigslot.hpp:1066
void call_slot(Args ...args) override
Definition: sigslot.hpp:1080
obj_ptr get_object() const noexcept override
Definition: sigslot.hpp:1095
bool connected() const noexcept override
Definition: sigslot.hpp:1075
constexpr slot_pmf_tracked(cleanable &c, F &&f, P &&p, group_id gid)
Definition: sigslot.hpp:1069
func_ptr get_callable() const noexcept override
Definition: sigslot.hpp:1091
Definition: sigslot.hpp:941
obj_ptr get_object() const noexcept override
Definition: sigslot.hpp:958
func_ptr get_callable() const noexcept override
Definition: sigslot.hpp:954
constexpr slot_pmf(cleanable &c, F &&f, P &&p, group_id gid)
Definition: sigslot.hpp:944
void call_slot(Args ...args) override
Definition: sigslot.hpp:950
Definition: sigslot.hpp:528
auto index() const
Definition: sigslot.hpp:556
group_id group() const
Definition: sigslot.hpp:564
void unblock() noexcept
Definition: sigslot.hpp:551
auto & index()
Definition: sigslot.hpp:560
void block() noexcept
Definition: sigslot.hpp:550
virtual void do_disconnect()
Definition: sigslot.hpp:554
bool disconnect() noexcept
Definition: sigslot.hpp:541
virtual bool connected() const noexcept
Definition: sigslot.hpp:539
constexpr slot_state(group_id gid) noexcept
Definition: sigslot.hpp:530
bool blocked() const noexcept
Definition: sigslot.hpp:549
virtual ~slot_state()=default
Definition: sigslot.hpp:1016
bool connected() const noexcept override
Definition: sigslot.hpp:1025
constexpr slot_tracked(cleanable &c, F &&f, P &&p, group_id gid)
Definition: sigslot.hpp:1019
void call_slot(Args ...args) override
Definition: sigslot.hpp:1030
obj_ptr get_object() const noexcept override
Definition: sigslot.hpp:1045
func_ptr get_callable() const noexcept override
Definition: sigslot.hpp:1041
Definition: sigslot.hpp:877
void call_slot(Args ...args) override
Definition: sigslot.hpp:885
constexpr slot(cleanable &c, F &&f, Gid gid)
Definition: sigslot.hpp:880
func_ptr get_callable() const noexcept override
Definition: sigslot.hpp:889
Definition: sigslot.hpp:688
scoped_connection(scoped_connection &&o) noexcept
Definition: sigslot.hpp:701
~scoped_connection() override
Definition: sigslot.hpp:691
scoped_connection(const connection &c) noexcept
Definition: sigslot.hpp:695
scoped_connection & operator=(scoped_connection &&o) noexcept
Definition: sigslot.hpp:705
scoped_connection(const scoped_connection &) noexcept=delete
scoped_connection & operator=(const scoped_connection &) noexcept=delete
friend class signal_base
Definition: sigslot.hpp:712
scoped_connection(connection &&c) noexcept
Definition: sigslot.hpp:696
Definition: sigslot.hpp:1134
std::enable_if_t<(trait::is_callable_v< arg_list, Callable >||trait::is_callable_v< ext_arg_list, Callable >||trait::is_pmf_v< Callable >) &&detail::function_traits< Callable >::is_disconnectable, size_t > disconnect(const Callable &c)
Definition: sigslot.hpp:1404
signal_base & operator=(signal_base &&o)
Definition: sigslot.hpp:1173
bool blocked() const noexcept
Definition: sigslot.hpp:1501
size_t slot_count() noexcept
Definition: sigslot.hpp:1509
std::enable_if_t< trait::is_callable_v< arg_list, Pmf, Ptr > &&!trait::is_observer_v< Ptr > &&!trait::is_weak_ptr_compatible_v< Ptr >, connection > connect(Pmf &&pmf, Ptr &&ptr, group_id gid=0)
Definition: sigslot.hpp:1288
void disconnect_all()
Definition: sigslot.hpp:1477
void operator()(U &&...a)
Definition: sigslot.hpp:1197
signal_base & operator=(const signal_base &)=delete
signal_base(signal_base &&o)
Definition: sigslot.hpp:1165
void clean(detail::slot_state *state) override
Definition: sigslot.hpp:1522
signal_base() noexcept
Definition: sigslot.hpp:1157
scoped_connection connect_scoped(CallArgs &&...args)
Definition: sigslot.hpp:1381
std::enable_if_t< trait::is_callable_v< arg_list, Callable >, connection > connect(Callable &&c, group_id gid=0)
Definition: sigslot.hpp:1226
std::enable_if_t<!trait::is_callable_v< arg_list, Pmf > &&trait::is_weak_ptr_compatible_v< Ptr >, connection > connect(Pmf &&pmf, Ptr &&ptr, group_id gid=0)
Definition: sigslot.hpp:1336
std::enable_if_t< trait::is_callable_v< ext_arg_list, Callable >, connection > connect_extended(Callable &&c, group_id gid=0)
Definition: sigslot.hpp:1246
signal_base(const signal_base &)=delete
std::enable_if_t<!trait::is_callable_v< arg_list, Obj > &&!trait::is_callable_v< ext_arg_list, Obj > &&!trait::is_pmf_v< Obj >, size_t > disconnect(const Obj &obj)
Definition: sigslot.hpp:1426
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, group_id gid=0)
Definition: sigslot.hpp:1307
void block() noexcept
Definition: sigslot.hpp:1486
~signal_base() override
Definition: sigslot.hpp:1158
size_t disconnect(group_id gid)
Definition: sigslot.hpp:1461
std::enable_if_t< trait::is_callable_v< arg_list, Callable > &&trait::is_weak_ptr_compatible_v< Trackable >, connection > connect(Callable &&c, Trackable &&ptr, group_id gid=0)
Definition: sigslot.hpp:1366
void unblock() noexcept
Definition: sigslot.hpp:1494
std::enable_if_t< trait::is_callable_v< arg_list, Pmf, Ptr > &&trait::is_observer_v< Ptr >, connection > connect(Pmf &&pmf, Ptr &&ptr, group_id gid=0)
Definition: sigslot.hpp:1267
size_t disconnect(const Callable &c, const Obj &obj)
Definition: sigslot.hpp:1446
func_ptr get_function_ptr(const T &t)
Definition: sigslot.hpp:290
std::shared_ptr< slot_base< T... > > slot_ptr
Definition: sigslot.hpp:779
const T & cow_read(const T &v)
Definition: sigslot.hpp:487
T & cow_write(T &v)
Definition: sigslot.hpp:497
std::shared_ptr< B > make_shared(Arg &&... arg)
Definition: sigslot.hpp:520
obj_ptr get_object_ptr(const T &t)
Definition: sigslot.hpp:340
const void * obj_ptr
Definition: sigslot.hpp:302
typename detail::voider< T... >::type void_t
Definition: sigslot.hpp:83
constexpr bool is_pointer_v
Definition: sigslot.hpp:146
std::weak_ptr< T > to_weak(std::weak_ptr< T > w)
Definition: sigslot.hpp:66
constexpr bool is_func_v
Definition: sigslot.hpp:149
constexpr bool is_weak_ptr_compatible_v
determine if a pointer is convertible into a "weak" pointer
Definition: sigslot.hpp:133
constexpr bool has_call_operator_v
Definition: sigslot.hpp:143
constexpr bool is_observer_v
Definition: sigslot.hpp:155
constexpr bool is_pmf_v
Definition: sigslot.hpp:152
constexpr bool is_callable_v
determine if a type T (Callable or Pmf) is callable with supplied arguments
Definition: sigslot.hpp:137
constexpr bool is_weak_ptr_v
Definition: sigslot.hpp:140
Definition: sigslot.hpp:45
std::int32_t group_id
Definition: sigslot.hpp:166
Definition: crossguid.cpp:388
void swap(xg::Guid &lhs, xg::Guid &rhs) noexcept
Definition: crossguid.cpp:390
#define SIGSLOT_MAY_ALIAS
Definition: sigslot.hpp:37
Definition: sigslot.hpp:770
virtual ~cleanable()=default
virtual void clean(slot_state *)=0
decltype(&std::remove_reference< T >::type::operator()) call_type
Definition: sigslot.hpp:279
static void ptr(const T &, func_ptr &d)
Definition: sigslot.hpp:281
static void ptr(T &t, func_ptr &d)
Definition: sigslot.hpp:248
static void ptr(const T &t, func_ptr &d)
Definition: sigslot.hpp:268
static void ptr(T *t, func_ptr &d)
Definition: sigslot.hpp:258
Definition: sigslot.hpp:237
static void ptr(const T &, func_ptr &d)
Definition: sigslot.hpp:238
Definition: sigslot.hpp:185
Definition: sigslot.hpp:186
Definition: sigslot.hpp:187
Definition: sigslot.hpp:346
null_mutex() noexcept=default
void unlock() noexcept
Definition: sigslot.hpp:356
void lock() noexcept
Definition: sigslot.hpp:355
Definition: sigslot.hpp:308
static obj_ptr get(const T &)
Definition: sigslot.hpp:309
Definition: sigslot.hpp:50
Definition: sigslot.hpp:364
bool try_lock() noexcept
Definition: sigslot.hpp:384
spin_mutex() noexcept=default
void unlock() noexcept
Definition: sigslot.hpp:388
Definition: sigslot.hpp:727
friend class signal_base
Definition: sigslot.hpp:745
void disconnect_all()
Definition: sigslot.hpp:738
virtual ~observer_base()=default
Definition: sigslot.hpp:93
Definition: sigslot.hpp:107
Definition: sigslot.hpp:79
void type
Definition: sigslot.hpp:79
represent a list of types
Definition: sigslot.hpp:57
Definition: sigslot.hpp:204
mock::fun_types _
Definition: sigslot.hpp:231
bool operator==(const func_ptr &o) const
Definition: sigslot.hpp:227
const void * value() const
Definition: sigslot.hpp:209
void * value()
Definition: sigslot.hpp:205
T & value()
Definition: sigslot.hpp:214
const T & value() const
Definition: sigslot.hpp:219
char data[sizeof(mock::fun_types)]
Definition: sigslot.hpp:232
Definition: sigslot.hpp:189
void * o
Definition: sigslot.hpp:194