E-MailRelay
gslot.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gslot.h
19///
20
21#ifndef G_SLOT_NEW_H
22#define G_SLOT_NEW_H
23
24#include "gdef.h"
25#include "gexception.h"
26#include "gassert.h"
27#include <functional>
28#include <memory>
29
30namespace G
31{
32 //| \namespace G::Slot
33 /// A callback mechanism that isolates event sinks from event sources.
34 ///
35 /// The slot/signal pattern has been used in several C++ libraries including
36 /// libsigc++, Qt and boost, although it is largely redudant with modern C++.
37 /// The pattern is completely unrelated to ANSI-C or POSIX signals (signal(),
38 /// sigaction(2)).
39 ///
40 /// Usage:
41 /// \code
42 /// struct Source
43 /// {
44 /// G::Slot::Signal<int> m_signal ;
45 /// void Source::raiseEvent()
46 /// {
47 /// m_signal.emit( 123 ) ;
48 /// }
49 /// } ;
50 ///
51 /// struct Sink
52 /// {
53 /// void onEvent( int n ) ;
54 /// Sink( Source & source ) : m_source(source)
55 /// {
56 /// source.m_signal.connect( G::Slot::slot(*this,&Sink::onEvent) ) ;
57 /// }
58 /// ~Sink()
59 /// {
60 /// m_source.m_signal.disconnect() ;
61 /// }
62 /// Source & m_source ;
63 /// } ;
64 /// \endcode
65 ///
66 /// For comparison the equivalent modern C++ looks like this:
67 /// \code
68 /// struct Source
69 /// {
70 /// std::function<void(int)> m_signal ;
71 /// void Source::raiseEvent()
72 /// {
73 /// if( m_signal ) m_signal( 123 ) ; // emit()
74 /// }
75 /// } ;
76 ///
77 /// struct Sink
78 /// {
79 /// void onEvent( int n ) ;
80 /// Sink( Source & source ) : m_source(source)
81 /// {
82 /// check( !source.m_signal ) ; // throw if already connected
83 /// source.m_signal = std::bind_front(&Sink::onEvent,this) ; // connect(slot())
84 /// }
85 /// ~Sink()
86 /// {
87 /// m_source.m_signal = nullptr ; // disconnect()
88 /// }
89 /// Source & m_source ;
90 /// } ;
91 /// \endcode
92 ///
93 /// Slot methods should take parameters by value or const reference but
94 /// beware of emit()ing references to data members of objects that might
95 /// get deleted. Use temporaries in the emit() call if in doubt.
96 ///
97 namespace Slot
98 {
99 //| \class G::Slot::Binder
100 /// A functor class template that contains the target object pointer
101 /// and method pointer, similar to c++20 bind_front(&T::fn,tp).
102 /// These objects are hidden in the std::function data member of
103 /// the Slot class so that the Slot is not dependent on the target
104 /// type. Maybe replace with a lambda.
105 ///
106 template <typename T, typename... Args>
107 struct Binder
108 {
109 using Mf = void (T::*)(Args...) ;
110 T * m_sink ;
111 Mf m_mf ;
112 Binder( T * sink , Mf mf ) :
113 m_sink(sink) ,
114 m_mf(mf)
115 {
116 }
117 void operator()( Args... args )
118 {
119 return (m_sink->*m_mf)( args... ) ;
120 }
121 } ;
122
123 //| \class G::Slot::Slot
124 /// A slot class template that is parameterised only on the target method's
125 /// signature (with an implicit void return) and not on the target class.
126 /// The implementation uses std::function to hide the type of the target.
127 ///
128 template <typename... Args>
129 struct Slot
130 {
131 std::function<void(Args...)> m_fn ;
132 Slot() noexcept = default;
133 template <typename T> Slot( T & sink , void (T::*mf)(Args...) ) :
134 m_fn(std::function<void(Args...)>(Binder<T,Args...>(&sink,mf)))
135 {
136 }
137 void invoke( Args... args )
138 {
139 m_fn( args... ) ;
140 }
141 } ;
142
143 //| \class G::Slot::SignalImp
144 /// A slot/signal scoping class.
145 ///
147 {
148 G_EXCEPTION_CLASS( AlreadyConnected , "already connected" ) ;
149 SignalImp() = delete ;
150 } ;
151
152 //| \class G::Slot::Signal
153 /// A slot holder, with connect() and emit() methods.
154 ///
155 template <typename... SlotArgs>
156 struct Signal
157 {
158 Slot<SlotArgs...> m_slot ;
159 bool m_once ;
160 bool m_emitted{false} ;
161 explicit Signal( bool once = false ) :
162 m_once(once)
163 {
164 }
165 void connect( Slot<SlotArgs...> slot )
166 {
167 if( m_slot.m_fn ) throw SignalImp::AlreadyConnected() ;
168 m_slot = slot ;
169 }
170 void disconnect() noexcept
171 {
172 m_slot.m_fn = nullptr ;
173 G_ASSERT( !connected() ) ;
174 }
175 void emit( SlotArgs... args )
176 {
177 if( !m_once || !m_emitted )
178 {
179 m_emitted = true ;
180 if( connected() )
181 m_slot.m_fn( args... ) ;
182 }
183 }
184 void reset()
185 {
186 m_emitted = false ;
187 }
188 bool connected() const
189 {
190 return !! m_slot.m_fn ;
191 }
192 ~Signal() = default ;
193 Signal( const Signal & ) = delete ;
194 Signal( Signal && ) = delete ;
195 void operator=( const Signal & ) = delete ;
196 void operator=( Signal && ) = delete ;
197 } ;
198
199 /// A factory function for Slot objects.
200 ///
201 template <typename TSink,typename... Args> Slot<Args...> slot( TSink & sink , void (TSink::*method)(Args...) )
202 {
203 // or c++20: return std::function<void(Args...)>( std::bind_front(method,&sink) )
204 return Slot<Args...>( sink , method ) ;
205 }
206
207 // backwards compatible names...
208 using Signal0 = Signal<> ;
209 template <typename T> using Signal1 = Signal<T> ;
210 template <typename T1, typename T2> using Signal2 = Signal<T1,T2> ;
211 template <typename T1, typename T2, typename T3> using Signal3 = Signal<T1,T2,T3> ;
212 template <typename T1, typename T2, typename T3, typename T4> using Signal4 = Signal<T1,T2,T3,T4> ;
213 template <typename T1, typename T2, typename T3, typename T4, typename T5> using Signal5 = Signal<T1,T2,T3,T4,T5> ;
214 using Slot0 = Slot<> ;
215 template <typename T> using Slot1 = Slot<T> ;
216 template <typename T1, typename T2> using Slot2 = Slot<T1,T2> ;
217 template <typename T1, typename T2, typename T3> using Slot3 = Slot<T1,T2,T3> ;
218 template <typename T1, typename T2, typename T3, typename T4> using Slot4 = Slot<T1,T2,T3,T4> ;
219 template <typename T1, typename T2, typename T3, typename T4, typename T5> using Slot5 = Slot<T1,T2,T3,T4,T5> ;
220 }
221}
222#endif
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
Definition: gslot.h:201
Low-level classes.
Definition: galign.h:28
A functor class template that contains the target object pointer and method pointer,...
Definition: gslot.h:108
A slot/signal scoping class.
Definition: gslot.h:147
A slot holder, with connect() and emit() methods.
Definition: gslot.h:157
A slot class template that is parameterised only on the target method's signature (with an implicit v...
Definition: gslot.h:130