VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gslot.h
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
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_H
22 #define G_SLOT_H
23 
24 #include "gdef.h"
25 #include "gexception.h"
26 #include "gnoncopyable.h"
27 
28 namespace G
29 {
30 
31 /// \namespace G::Slot
32 ///
33 /// A typesafe callback library that isolates event sinks from event sources.
34 ///
35 /// The slot/signal pattern is used in several C++ libraries including
36 /// libsigc++, Qt and boost; it is completely unrelated to ANSI-C or POSIX
37 /// signals (signal(), sigaction(2)).
38 ///
39 /// This implementation was inspired by libsigc++, but simplified by:
40 /// * not doing multicast
41 /// * not detecting dangling references
42 /// * not supporting global function callbacks
43 /// * using only void returns
44 ///
45 /// Event-generating classes expose a "signal" object which client objects
46 /// can connect() to in order to receive events. The client receives events
47 /// through a "slot" member function.
48 ///
49 /// The key to the implementation is that the slot implementation class
50 /// (eg. G::Slot::SlotImp1) is templated on the callback parameter P and
51 /// the callback sink class T, whereas the slot public interface (eg.
52 /// G::Slot::Slot1) is templated only on P. This means that the event
53 /// source, using the public interface, does not need to know the type of
54 /// the event sink. It is the slot callback classes (eg. G::Slot::Callback1)
55 /// that resolve the mismatch, by doing a down-cast to the T-specific
56 /// slot implementation class.
57 ///
58 /// A slot is a reference-counting handle to a slot implementation body,
59 /// with the G::Slot::SlotImpBase class doing the reference counting.
60 /// Slots also hold a function pointer pointing to the relevant downcasting
61 /// method provided by one of the callback classes.
62 ///
63 /// The overloaded factory function G::Slot::slot() creates a slot handle,
64 /// passing it a suitable SlotImpBase body and callback casting function.
65 /// The dynamic type of the SlotImpBase pointer is a slot implementation
66 /// class that knows about the specific sink class T, and the callback
67 /// casting function knows how to access the derived class. The combination
68 /// of SlotImpBase polymorphism and casting function isolates the slot
69 /// class from the sink class.
70 ///
71 /// The signal classes contain a slot that is initially uninitialised;
72 /// the connect() method is used to initialise the slot so that it points
73 /// to the sink class's event handling function.
74 ///
75 /// Usage:
76 /// \code
77 /// class Source
78 /// {
79 /// public:
80 /// G::Slot::Signal1<int> m_signal ;
81 /// private:
82 /// void Source::raiseEvent()
83 /// {
84 /// int n = 123 ;
85 /// m_signal.emit( n ) ;
86 /// }
87 /// } ;
88 ///
89 /// class Sink
90 /// {
91 /// public:
92 /// void onEvent( int n ) ;
93 /// Sink( Source & source )
94 /// {
95 /// source.m_signal.connect( G::Slot::slot(*this,&Sink::onEvent) ) ;
96 /// }
97 /// } ;
98 /// \endcode
99 ///
100 namespace Slot
101 {
102 
103 /// \class G::Slot::SlotImpBase
104 /// Used as a base class to all slot implementation classes (such as G::Slot::SlotImp1),
105 /// allowing them to be used as bodies to the associated slot reference-counting handle
106 /// class (eg. G::Slot::Slot1).
107 ///
109 {
110 public:
111  SlotImpBase() ;
112  ///< Default constuctor.
113 
114  virtual ~SlotImpBase() ;
115  ///< Destructor.
116 
117  void up() ;
118  ///< Increments the reference count.
119 
120  void down() ;
121  ///< Decrements the reference count
122  ///< and does "delete this" on zero.
123 
124 private:
125  SlotImpBase( const SlotImpBase & ) ; // not implemented
126  void operator=( const SlotImpBase & ) ; // not implemented
127 
128 private:
129  unsigned long m_ref_count ;
130 } ;
131 
132 /// \class G::Slot::SignalImp
133 /// A static helper class used by G::Slot signal classes.
134 ///
136 {
137 public:
138  G_EXCEPTION( AlreadyConnected , "signal already connected to a slot" ) ;
139  static void check( const SlotImpBase * p ) ;
140 private:
141  SignalImp() ; // not implemented
142 } ;
143 
144 //
145 
146 /// \class G::Slot::SlotImp0
147 /// A slot implementation class for zero-parameter callbacks.
148 ///
149 template <typename T>
150 class SlotImp0 : public SlotImpBase
151 {
152 public:
153  SlotImp0( T & object , void (T::*fn)() ) : m_object(object) , m_fn(fn) {}
154  void callback() { (m_object.*m_fn)() ; }
155 private:
156  T & m_object ;
157  void (T::*m_fn)() ;
158 } ;
159 
160 /// \class G::Slot::SlotCallback0
161 /// Provides a function to down-cast from SlotImpBase to SlotImp0.
162 ///
163 template <typename T>
165 {
166 public:
167  static void callback( SlotImpBase * imp )
168  { static_cast<SlotImp0<T>*>(imp)->callback() ; }
169 } ;
170 
171 /// \class G::Slot::Slot0
172 /// A slot class for zero-parameter callbacks.
173 ///
174 class Slot0
175 {
176 private:
177  SlotImpBase * m_imp ;
178  void (*m_callback_fn)( SlotImpBase * ) ;
179 public:
180  Slot0() : m_imp(0) , m_callback_fn(0) {}
181  Slot0( SlotImpBase * imp , void (*op)(SlotImpBase*) ) : m_imp(imp) , m_callback_fn(op) {}
182  ~Slot0() { if(m_imp) m_imp->down() ; }
183  void callback() { if( m_imp ) (*m_callback_fn)( m_imp ) ; }
184  Slot0( const Slot0 & other ) : m_imp(other.m_imp) , m_callback_fn(other.m_callback_fn) { if(m_imp) m_imp->up() ; }
185  void swap( Slot0 & rhs ) { using std::swap ; swap(m_imp,rhs.m_imp) ; swap(m_callback_fn,rhs.m_callback_fn) ; }
186  void operator=( const Slot0 & rhs ) { Slot0 tmp(rhs) ; swap(tmp) ; }
187  const SlotImpBase * base() const { return m_imp ; }
188 } ;
189 
190 /// \class G::Slot::Signal0
191 /// A signal class for zero-parameter callbacks.
192 ///
193 class Signal0 : public noncopyable
194 {
195 private:
196  bool m_emitted ;
197  bool m_once ;
198  Slot0 m_slot ;
199 public:
200  explicit Signal0( bool once = false ) : m_emitted(false) , m_once(once) {}
201  void emit() { if(!m_once||!m_emitted) { m_emitted = true ; m_slot.callback() ; } }
202  void connect( Slot0 slot ) { SignalImp::check(m_slot.base()) ; m_slot = slot ; }
203  void disconnect() { m_slot = Slot0() ; }
204  void reset() { m_emitted = false ; }
205 } ;
206 
207 /// A slot factory function overloaded for a zero-parameter callback.
208 template <typename T>
209 Slot0 slot( T & object , void (T::*fn)() )
210 {
211  return Slot0( new SlotImp0<T>(object,fn) , SlotCallback0<T>::callback ) ;
212 }
213 
214 //
215 
216 /// \class G::Slot::SlotImp1
217 /// A slot implementation class for one-parameter callbacks.
218 ///
219 template <typename T, typename P>
220 class SlotImp1 : public SlotImpBase
221 {
222 private:
223  T & m_object ;
224  void (T::*m_fn)( P ) ;
225 public:
226  SlotImp1( T & object , void (T::*fn)(P) ) : m_object(object) , m_fn(fn) {}
227  void callback( P p ) { (m_object.*m_fn)(p) ; }
228 } ;
229 
230 /// \class G::Slot::SlotCallback1
231 /// Provides a function to down-cast from SlotImpBase to SlotImp1.
232 ///
233 template <typename T, typename P>
235 {
236 public:
237  static void callback( SlotImpBase * imp , P p )
238  { static_cast<SlotImp1<T,P>*>(imp)->callback( p ) ; }
239 } ;
240 
241 /// \class G::Slot::Slot1
242 /// A slot class for one-parameter callbacks.
243 ///
244 template <typename P>
245 class Slot1
246 {
247 private:
248  SlotImpBase * m_imp ;
249  void (*m_callback_fn)( SlotImpBase * , P ) ;
250 public:
251  Slot1() : m_imp(0) , m_callback_fn(0) {}
252  Slot1( SlotImpBase * imp , void (*op)(SlotImpBase*,P) ) : m_imp(imp) , m_callback_fn(op) {}
253  ~Slot1() { if( m_imp ) m_imp->down() ; }
254  void callback( P p ) { if( m_imp ) (*m_callback_fn)( m_imp , p ) ; }
255  Slot1( const Slot1<P> & other ) : m_imp(other.m_imp) , m_callback_fn(other.m_callback_fn) { if(m_imp) m_imp->up() ; }
256  void swap( Slot1<P> & rhs ) { using std::swap ; swap(m_imp,rhs.m_imp) ; swap(m_callback_fn,rhs.m_callback_fn) ; }
257  void operator=( const Slot1<P> & rhs ) { Slot1 tmp(rhs) ; swap(tmp) ; }
258  const SlotImpBase * base() const { return m_imp ; }
259 } ;
260 
261 /// \class G::Slot::Signal1
262 /// A signal class for one-parameter callbacks.
263 ///
264 template <typename P>
265 class Signal1 : public noncopyable
266 {
267 private:
268  bool m_emitted ;
269  bool m_once ;
270  Slot1<P> m_slot ;
271 public:
272  explicit Signal1( bool once = false ) : m_emitted(false) , m_once(once) {}
273  void emit( P p ) { if(!m_once||!m_emitted) { m_emitted = true ; m_slot.callback( p ) ; } }
274  void connect( Slot1<P> slot ) { SignalImp::check(m_slot.base()) ; m_slot = slot ; }
275  void disconnect() { m_slot = Slot1<P>() ; }
276  void reset() { m_emitted = false ; }
277 } ;
278 
279 /// A slot factory function overloaded for a one-parameter callback.
280 template <typename T,typename P>
281 Slot1<P> slot( T & object , void (T::*fn)(P) )
282 {
283  return Slot1<P>( new SlotImp1<T,P>(object,fn) , SlotCallback1<T,P>::callback ) ;
284 }
285 
286 //
287 
288 /// \class G::Slot::SlotImp2
289 /// A slot implementation class for two-parameter callbacks.
290 ///
291 template <typename T, typename P1, typename P2>
292 class SlotImp2 : public SlotImpBase
293 {
294 private:
295  T & m_object ;
296  void (T::*m_fn)( P1 , P2 ) ;
297 public:
298  SlotImp2( T & object , void (T::*fn)(P1,P2) ) : m_object(object) , m_fn(fn) {}
299  void callback( P1 p1 , P2 p2 ) { (m_object.*m_fn)(p1,p2) ; }
300 } ;
301 
302 /// \class G::Slot::SlotCallback2
303 /// Provides a function to down-cast from SlotImpBase to SlotImp2.
304 ///
305 template <typename T, typename P1 , typename P2>
307 {
308 public:
309  static void callback( SlotImpBase * imp , P1 p1 , P2 p2 )
310  { static_cast<SlotImp2<T,P1,P2>*>(imp)->callback( p1 , p2 ) ; }
311 } ;
312 
313 /// \class G::Slot::Slot2
314 /// A slot class for two-parameter callbacks.
315 ///
316 template <typename P1, typename P2>
317 class Slot2
318 {
319 private:
320  SlotImpBase * m_imp ;
321  void (*m_callback_fn)( SlotImpBase * , P1 , P2 ) ;
322 public:
323  Slot2() : m_imp(0) , m_callback_fn(0) {}
324  Slot2( SlotImpBase * imp , void (*op)(SlotImpBase*,P1,P2) ) : m_imp(imp) , m_callback_fn(op) {}
325  ~Slot2() { if( m_imp ) m_imp->down() ; }
326  void callback( P1 p1 , P2 p2 ) { if( m_imp ) (*m_callback_fn)( m_imp , p1 , p2 ) ; }
327  Slot2( const Slot2<P1,P2> & other ) : m_imp(other.m_imp) , m_callback_fn(other.m_callback_fn) { if(m_imp) m_imp->up() ; }
328  void swap( Slot2<P1,P2> & rhs ) { using std::swap ; swap(m_imp,rhs.m_imp) ; swap(m_callback_fn,rhs.m_callback_fn) ; }
329  void operator=( const Slot2<P1,P2> & rhs ) { Slot2 tmp(rhs) ; swap(tmp) ; }
330  const SlotImpBase * base() const { return m_imp ; }
331 } ;
332 
333 /// \class G::Slot::Signal2
334 /// A signal class for two-parameter callbacks.
335 ///
336 template <typename P1, typename P2>
337 class Signal2 : public noncopyable
338 {
339 private:
340  bool m_emitted ;
341  bool m_once ;
342  Slot2<P1,P2> m_slot ;
343 public:
344  explicit Signal2( bool once = false ) : m_emitted(false) , m_once(once) {}
345  void emit( P1 p1 , P2 p2 ) { if(!m_once||!m_emitted) { m_emitted = true ; m_slot.callback( p1 , p2 ) ; } }
346  void connect( Slot2<P1,P2> slot ) { SignalImp::check(m_slot.base()) ; m_slot = slot ; }
347  void disconnect() { m_slot = Slot2<P1,P2>() ; }
348  void reset() { m_emitted = false ; }
349 } ;
350 
351 /// A slot factory function overloaded for a two-parameter callback.
352 template <typename T, typename P1, typename P2>
353 Slot2<P1,P2> slot( T & object , void (T::*fn)(P1,P2) )
354 {
356 }
357 
358 //
359 
360 /// \class G::Slot::SlotImp3
361 /// A slot implementation class for three-parameter callbacks.
362 ///
363 template <typename T, typename P1, typename P2, typename P3>
364 class SlotImp3 : public SlotImpBase
365 {
366 private:
367  T & m_object ;
368  void (T::*m_fn)( P1 , P2 , P3 ) ;
369 public:
370  SlotImp3( T & object , void (T::*fn)(P1,P2,P3) ) : m_object(object) , m_fn(fn) {}
371  void callback( P1 p1 , P2 p2 , P3 p3 ) { (m_object.*m_fn)(p1,p2,p3) ; }
372 } ;
373 
374 /// \class G::Slot::SlotCallback3
375 /// Provides a function to down-cast from SlotImpBase to SlotImp3.
376 ///
377 template <typename T, typename P1 , typename P2, typename P3>
379 {
380 public:
381  static void callback( SlotImpBase * imp , P1 p1 , P2 p2 , P3 p3 )
382  { static_cast<SlotImp3<T,P1,P2,P3>*>(imp)->callback( p1 , p2 , p3 ) ; }
383 } ;
384 
385 /// \class G::Slot::Slot3
386 /// A slot class for three-parameter callbacks.
387 ///
388 template <typename P1, typename P2, typename P3>
389 class Slot3
390 {
391 private:
392  SlotImpBase * m_imp ;
393  void (*m_callback_fn)( SlotImpBase * , P1 , P2 , P3 ) ;
394 public:
395  Slot3() : m_imp(0) , m_callback_fn(0) {}
396  Slot3( SlotImpBase * imp , void (*op)(SlotImpBase*,P1,P2,P3) ) : m_imp(imp) , m_callback_fn(op) {}
397  ~Slot3() { if( m_imp ) m_imp->down() ; }
398  void callback( P1 p1 , P2 p2 , P3 p3 ) { if( m_imp ) (*m_callback_fn)( m_imp , p1 , p2 , p3 ) ; }
399  Slot3( const Slot3<P1,P2,P3> & other ) : m_imp(other.m_imp) , m_callback_fn(other.m_callback_fn) { if(m_imp) m_imp->up() ; }
400  void swap( Slot3<P1,P2,P3> & rhs ) { using std::swap ; swap(m_imp,rhs.m_imp) ; swap(m_callback_fn,rhs.m_callback_fn) ; }
401  void operator=( const Slot3<P1,P2,P3> & rhs ) { Slot3 tmp(rhs) ; swap(tmp) ; }
402  const SlotImpBase * base() const { return m_imp ; }
403 } ;
404 
405 /// \class G::Slot::Signal3
406 /// A signal class for three-parameter callbacks.
407 ///
408 template <typename P1, typename P2, typename P3>
409 class Signal3 : public noncopyable
410 {
411 private:
412  bool m_emitted ;
413  bool m_once ;
414  Slot3<P1,P2,P3> m_slot ;
415 public:
416  explicit Signal3( bool once = false ) : m_emitted(false) , m_once(once) {}
417  void emit( P1 p1 , P2 p2 , P3 p3 ) { if(!m_once||!m_emitted) { m_emitted = true ; m_slot.callback( p1 , p2 , p3 ) ; }}
418  void connect( Slot3<P1,P2,P3> slot ) { SignalImp::check(m_slot.base()) ; m_slot = slot ; }
419  void disconnect() { m_slot = Slot3<P1,P2,P3>() ; }
420  void reset() { m_emitted = false ; }
421 } ;
422 
423 /// A slot factory function overloaded for a three-parameter callback.
424 template <typename T, typename P1, typename P2, typename P3>
425 Slot3<P1,P2,P3> slot( T & object , void (T::*fn)(P1,P2,P3) )
426 {
428 }
429 
430 }
431 }
432 
433 #endif
SlotImpBase()
Default constuctor.
Definition: gslot.cpp:28
A noncopyable base class (a la boost).
Definition: gnoncopyable.h:34
A signal class for two-parameter callbacks.
Definition: gslot.h:337
A slot implementation class for one-parameter callbacks.
Definition: gslot.h:220
A slot class for zero-parameter callbacks.
Definition: gslot.h:174
Used as a base class to all slot implementation classes (such as G::Slot::SlotImp1), allowing them to be used as bodies to the associated slot reference-counting handle class (eg.
Definition: gslot.h:108
A slot implementation class for zero-parameter callbacks.
Definition: gslot.h:150
Provides a function to down-cast from SlotImpBase to SlotImp2.
Definition: gslot.h:306
Slot0 slot(T &object, void(T::*fn)())
A slot factory function overloaded for a zero-parameter callback.
Definition: gslot.h:209
void up()
Increments the reference count.
Definition: gslot.cpp:32
Provides a function to down-cast from SlotImpBase to SlotImp1.
Definition: gslot.h:234
A slot class for one-parameter callbacks.
Definition: gslot.h:245
A signal class for zero-parameter callbacks.
Definition: gslot.h:193
A static helper class used by G::Slot signal classes.
Definition: gslot.h:135
A slot implementation class for three-parameter callbacks.
Definition: gslot.h:364
Provides a function to down-cast from SlotImpBase to SlotImp0.
Definition: gslot.h:164
A signal class for three-parameter callbacks.
Definition: gslot.h:409
A slot class for two-parameter callbacks.
Definition: gslot.h:317
A signal class for one-parameter callbacks.
Definition: gslot.h:265
virtual ~SlotImpBase()
Destructor.
Definition: gslot.cpp:24
Provides a function to down-cast from SlotImpBase to SlotImp3.
Definition: gslot.h:378
A slot implementation class for two-parameter callbacks.
Definition: gslot.h:292
A slot class for three-parameter callbacks.
Definition: gslot.h:389
void down()
Decrements the reference count and does "delete this" on zero.
Definition: gslot.cpp:37