1 /*
2  * Copyright 2002, The libsigc++ Development Team
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2.1 of the License, or (at your option) any later version.
8  *
9  *  This library 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 GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 #ifndef _SIGC_SIGNAL_BASE_H_
20 #define _SIGC_SIGNAL_BASE_H_
21 
22 #include <cstddef>
23 #include <list>
24 #include <sigc++config.h>
25 #include <sigc++/type_traits.h>
26 #include <sigc++/trackable.h>
27 #include <sigc++/functors/slot.h>
28 #include <sigc++/functors/mem_fun.h>
29 
30 /** The libsigc++ namespace.
31  */
32 namespace sigc
33 {
34 
35 namespace internal
36 {
37 
38 /** Implementation of the signal interface.
39  * signal_impl manages a list of slots. When a slot becomes
40  * invalid (because some referred object dies), notify() is executed.
41  * notify() either calls slots_.erase() directly or defers the execution of
42  * erase() to sweep() when the signal is being emitted. sweep() removes all
43  * invalid slots from the list.
44  */
45 struct SIGC_API signal_impl
46 {
47   typedef std::size_t size_type;
48   typedef std::list<slot_base> slot_list;
49   typedef slot_list::iterator       iterator_type;
50   typedef slot_list::const_iterator const_iterator_type;
51 
52   signal_impl();
53 
54   // only MSVC needs this to guarantee that all new/delete are executed from the DLL module
55 #ifdef SIGC_NEW_DELETE_IN_LIBRARY_ONLY
56   void* operator new(size_t size_);
57   void operator delete(void* p);
58 #endif
59 
60   /// Increments the reference counter.
referencesignal_impl61   inline void reference()
62     { ++ref_count_; }
63 
64   /// Increments the reference and execution counter.
reference_execsignal_impl65   inline void reference_exec()
66     { ++ref_count_; ++exec_count_; }
67 
68   /** Decrements the reference counter.
69    * The object is deleted when the reference counter reaches zero.
70    */
unreferencesignal_impl71   inline void unreference()
72     { if (!(--ref_count_)) delete this; }
73 
74   /** Decrements the reference and execution counter.
75    * Invokes sweep() if the execution counter reaches zero and the
76    * removal of one or more slots has been deferred.
77    */
unreference_execsignal_impl78   inline void unreference_exec()
79     {
80       if (!(--ref_count_)) delete this;
81       else if (!(--exec_count_) && deferred_) sweep();
82     }
83 
84   /** Returns whether the list of slots is empty.
85    * @return @p true if the list of slots is empty.
86    */
emptysignal_impl87   inline bool empty() const
88     { return slots_.empty(); }
89 
90   /// Empties the list of slots.
91   void clear();
92 
93   /** Returns the number of slots in the list.
94    * @return The number of slots in the list.
95    */
96   size_type size() const;
97 
98   /** Returns whether all slots in the list are blocked.
99    * @return @p true if all slots are blocked or the list is empty.
100    *
101    * @newin{2,4}
102    */
103   bool blocked() const;
104 
105   /** Sets the blocking state of all slots in the list.
106    * If @e should_block is @p true then the blocking state is set.
107    * Subsequent emissions of the signal don't invoke the functors
108    * contained in the slots until block() with @e should_block = @p false is called.
109    * sigc::slot_base::block() and sigc::slot_base::unblock() can change the
110    * blocking state of individual slots.
111    * @param should_block Indicates whether the blocking state should be set or unset.
112    *
113    * @newin{2,4}
114    */
115   void block(bool should_block = true);
116 
117   /** Adds a slot at the bottom of the list of slots.
118    * @param slot_ The slot to add to the list of slots.
119    * @return An iterator pointing to the new slot in the list.
120    */
121   iterator_type connect(const slot_base& slot_);
122 
123   /** Adds a slot at the given position into the list of slots.
124    * @param i An iterator indicating the position where @p slot_ should be inserted.
125    * @param slot_ The slot to add to the list of slots.
126    * @return An iterator pointing to the new slot in the list.
127    */
128   iterator_type insert(iterator_type i, const slot_base& slot_);
129 
130   /** Removes the slot at the given position from the list of slots.
131    * @param i An iterator pointing to the slot to be removed.
132    * @return An iterator pointing to the slot in the list after the one removed.
133    */
134   iterator_type erase(iterator_type i);
135 
136   /// Removes invalid slots from the list of slots.
137   void sweep();
138 
139   /** Callback that is executed when some slot becomes invalid.
140    * This callback is registered in every slot when inserted into
141    * the list of slots. It is executed when a slot becomes invalid
142    * because of some referred object being destroyed.
143    * It either calls slots_.erase() directly or defers the execution of
144    * erase() to sweep() when the signal is being emitted.
145    * @param d A local structure, created in insert().
146    */
147   static void* notify(void* d);
148 
149   /** Reference counter.
150    * The object is destroyed when @em ref_count_ reaches zero.
151    */
152   short ref_count_;
153 
154   /** Execution counter.
155    * Indicates whether the signal is being emitted.
156    */
157   short exec_count_;
158 
159   /// Indicates whether the execution of sweep() is being deferred.
160   bool deferred_;
161 
162   /// The list of slots.
163   std::list<slot_base> slots_;
164 };
165 
166 /// Exception safe sweeper for cleaning up invalid slots on the slot list.
167 struct SIGC_API signal_exec
168 {
169   /// The parent sigc::signal_impl object.
170   signal_impl* sig_;
171 
172   /** Increments the reference and execution counter of the parent sigc::signal_impl object.
173    * @param sig The parent sigc::signal_impl object.
174    */
signal_execsignal_exec175   inline signal_exec(const signal_impl* sig)
176     : sig_(const_cast<signal_impl*>(sig) )
177     { sig_->reference_exec(); }
178 
179   /// Decrements the reference and execution counter of the parent sigc::signal_impl object.
~signal_execsignal_exec180   inline ~signal_exec()
181     { sig_->unreference_exec(); }
182 };
183 
184 /** Temporary slot list used during signal emission.
185  *  Through evolution this class is slightly misnamed.  It is now
186  *  an index into the slot_list passed into it.  It simply keeps track
187  *  of where the end of this list was at construction, and pretends that's
188  *  the end of your list.  This way you may connect during emission without
189  *  inadvertently entering an infinite loop, as well as make other
190  *  modifications to the slot_list at your own risk.
191  */
192 struct temp_slot_list
193 {
194   typedef signal_impl::slot_list slot_list;
195   typedef signal_impl::iterator_type iterator;
196   typedef signal_impl::const_iterator_type const_iterator;
197 
temp_slot_listtemp_slot_list198   temp_slot_list(slot_list &slots) : slots_(slots)
199   {
200     placeholder = slots_.insert(slots_.end(), slot_base());
201   }
202 
~temp_slot_listtemp_slot_list203   ~temp_slot_list()
204   {
205     slots_.erase(placeholder);
206   }
207 
begintemp_slot_list208   iterator begin() { return slots_.begin(); }
endtemp_slot_list209   iterator end() { return placeholder; }
begintemp_slot_list210   const_iterator begin() const { return slots_.begin(); }
endtemp_slot_list211   const_iterator end() const { return placeholder; }
212 
213 private:
214   slot_list &slots_;
215   slot_list::iterator placeholder;
216 };
217 
218 } /* namespace internal */
219 
220 
221 /** @defgroup signal Signals
222  * Use sigc::signal::connect() with sigc::mem_fun() and sigc::ptr_fun() to connect a method or function with a signal.
223  *
224  * @code
225  * signal_clicked.connect( sigc::mem_fun(*this, &MyWindow::on_clicked) );
226  * @endcode
227  *
228  * When the signal is emitted your method will be called.
229  *
230  * signal::connect() returns a connection, which you can later use to disconnect your method.
231  * If the type of your object inherits from sigc::trackable the method is disconnected
232  * automatically when your object is destroyed.
233  *
234  * When signals are copied they share the underlying information,
235  * so you can have a protected/private sigc::signal member and a public accessor method.
236  * A sigc::signal is a kind of reference-counting pointer. It's similar to
237  * std::shared_ptr<>, although sigc::signal is restricted to holding a pointer to
238  * a sigc::internal::signal_impl object that contains the implementation of the signal.
239  *
240  * @code
241  * class MyClass
242  * {
243  * public:
244  *   typedef sigc::signal<void> MySignalType;
245  *   MySignalType get_my_signal() { return m_my_signal; }
246  * private:
247  *   MySignalType m_my_signal;
248  * };
249  * @endcode
250  *
251  * signal and slot objects provide the core functionality of this
252  * library. A slot is a container for an arbitrary functor.
253  * A signal is a list of slots that are executed on emission.
254  * For compile time type safety a list of template arguments
255  * must be provided for the signal template that determines the
256  * parameter list for emission. Functors and closures are converted
257  * into slots implicitly on connection, triggering compiler errors
258  * if the given functor or closure cannot be invoked with the
259  * parameter list of the signal to connect to.
260  *
261  * Almost any functor with the correct signature can be converted to a sigc::slot
262  * and connected to a signal. See @ref slot "Slots" and sigc::signal::connect().
263  */
264 
265 /** Base class for the sigc::signal# templates.
266  * signal_base integrates most of the interface of the derived sigc::signal#
267  * templates. The implementation, however, resides in sigc::internal::signal_impl.
268  * A sigc::internal::signal_impl object is dynamically allocated from signal_base
269  * when first connecting a slot to the signal. This ensures that empty signals
270  * don't waste memory.
271  *
272  * sigc::internal::signal_impl is reference-counted. When a sigc::signal# object
273  * is copied, the reference count of its sigc::internal::signal_impl object is
274  * incremented. Both sigc::signal# objects then refer to the same
275  * sigc::internal::signal_impl object.
276  *
277  * @ingroup signal
278  */
279 struct SIGC_API signal_base : public trackable
280 {
281   typedef std::size_t size_type;
282 
283   signal_base();
284 
285   signal_base(const signal_base& src);
286 
287   ~signal_base();
288 
289   signal_base& operator = (const signal_base& src);
290 
291   /** Returns whether the list of slots is empty.
292    * @return @p true if the list of slots is empty.
293    */
emptysignal_base294   inline bool empty() const
295     { return (!impl_ || impl_->empty()); }
296 
297   /// Empties the list of slots.
298   void clear();
299 
300   /** Returns the number of slots in the list.
301    * @return The number of slots in the list.
302    */
303   size_type size() const;
304 
305   /** Returns whether all slots in the list are blocked.
306    * @return @p true if all slots are blocked or the list is empty.
307    *
308    * @newin{2,4}
309    */
310   bool blocked() const;
311 
312   /** Sets the blocking state of all slots in the list.
313    * If @e should_block is @p true then the blocking state is set.
314    * Subsequent emissions of the signal don't invoke the functors
315    * contained in the slots until unblock() or block() with
316    * @e should_block = @p false is called.
317    * sigc::slot_base::block() and sigc::slot_base::unblock() can change the
318    * blocking state of individual slots.
319    * @param should_block Indicates whether the blocking state should be set or unset.
320    *
321    * @newin{2,4}
322    */
323   void block(bool should_block = true);
324 
325   /** Unsets the blocking state of all slots in the list.
326    *
327    * @newin{2,4}
328    */
329   void unblock();
330 
331 protected:
332   typedef internal::signal_impl::iterator_type iterator_type;
333 
334   /** Adds a slot at the end of the list of slots.
335    * With connect(), slots can also be added during signal emission.
336    * In this case, they won't be executed until the next emission occurs.
337    * @param slot_ The slot to add to the list of slots.
338    * @return An iterator pointing to the new slot in the list.
339    */
340   iterator_type connect(const slot_base& slot_);
341 
342   /** Adds a slot at the given position into the list of slots.
343    * Note that this function does not work during signal emission!
344    * @param i An iterator indicating the position where @e slot_ should be inserted.
345    * @param slot_ The slot to add to the list of slots.
346    * @return An iterator pointing to the new slot in the list.
347    */
348   iterator_type insert(iterator_type i, const slot_base& slot_);
349 
350   /** Removes the slot at the given position from the list of slots.
351    * Note that this function does not work during signal emission!
352    * @param i An iterator pointing to the slot to be removed.
353    * @return An iterator pointing to the slot in the list after the one removed.
354    */
355   iterator_type erase(iterator_type i);
356 
357   /** Returns the signal_impl object encapsulating the list of slots.
358    * @return The signal_impl object encapsulating the list of slots.
359    */
360   internal::signal_impl* impl() const;
361 
362   /// The signal_impl object encapsulating the slot list.
363   mutable internal::signal_impl* impl_;
364 };
365 
366 } //namespace sigc
367 
368 #endif /* _SIGC_SIGNAL_BASE_H_ */
369