]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/signals/doc/rationale.xml
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / signals / doc / rationale.xml
CommitLineData
7c673cae
FG
1<?xml version="1.0" encoding="utf-8"?>
2<!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
3 "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
4<section last-revision="$Date$">
5 <title>Design Rationale</title>
6
7 <using-namespace name="boost"/>
8 <using-namespace name="boost::signals"/>
9 <using-class name="boost::signalN"/>
10
11 <section>
12 <title>Choice of Slot Definitions</title>
13
14 <para> The definition of a slot differs amongst signals and slots
15 libraries. Within Boost.Signals, a slot is defined in a very loose
16 manner: it can be any function object that is callable given
17 parameters of the types specified by the signal, and whose return
18 value is convertible to the result type expected by the
19 signal. However, alternative definitions have associated pros and
20 cons that were considered prior to the construction of
21 Boost.Signals.</para>
22
23 <itemizedlist>
24 <listitem>
25 <para><emphasis role="bold">Slots derive from a specific base
26 class</emphasis>: generally a scheme such as this will require
27 all user-defined slots to derive from some library-specified
28 <code>Slot</code> abstract class that defines a virtual
29 function calling the slot. Adaptors can be used to convert a
30 definition such as this to a definition similar to that used
31 by Boost.Signals, but the use of a large number of small
32 adaptor classes containing virtual functions has been found to
33 cause an unacceptable increase in the size of executables
34 (polymorphic class types require more code than
35 non-polymorphic types).</para>
36
37 <para> This approach does have the benefit of simplicity of
38 implementation and user interface, from an object-oriented
39 perspective.</para>
40 </listitem>
41
42 <listitem>
43 <para><emphasis role="bold">Slots constructed from a set of
44 primitives</emphasis>: in this scheme the slot can have a
45 limited set of types (often derived from a common abstract
46 base class) that are constructed from some library-defined set
47 of primitives that often include conversions from free
48 function pointers and member function pointers, and a limited
49 set of binding capabilities. Such an approach is reasonably
50 simple and cover most common cases, but it does not allow a
51 large degree of flexibility in slot construction. Libraries
52 for function object composition have become quite advanced and
53 it is out of the scope of a signals and slots library to
54 encorporate such enhancements. Thus Boost.Signals does not
55 include argument binding or function object composition
56 primitives, but instead provides a hook (via the
57 <code><functionname>visit_each</functionname></code>
58 mechanism) that allows existing binder/composition libraries
59 to provide the necessary information to Signals.</para>
60 </listitem>
61 </itemizedlist>
62
63 <para> Users not satisfied with the slot definition choice may opt
64 to replace the default slot function type with an alternative that
65 meets their specific needs.</para>
66 </section>
67
68 <section>
69 <title>User-level Connection Management</title>
70
71 <para> Users need to have fine control over the connection of
72 signals to slots and their eventual disconnection. The approach
73 taken by Boost.Signals is to return a
74 <code><classname>connection</classname></code> object that enables
75 connected/disconnected query, manual disconnection, and an
76 automatic disconnection on destruction mode. Some other possible
77 interfaces include:</para>
78
79 <itemizedlist>
80 <listitem>
81 <para><emphasis role="bold">Pass slot to
82 disconnect</emphasis>: in this interface model, the
83 disconnection of a slot connected with
84 <code>sig.<methodname>connect</methodname>(slot)</code> is
85 performed via
86 <code>sig.<methodname>disconnect</methodname>(slot)</code>. Internally,
87 a linear search using slot comparison is performed and the
88 slot, if found, is removed from the list. Unfortunately,
89 querying connectedness will generally also end up as
90 linear-time operations. This model also fails for
91 implementation reasons when slots become more complex than
92 simple function pointers, member function pointers and a
93 limited set of compositions and argument binders: to match the
94 slot given in the call to
95 <code><methodname>disconnect</methodname></code> with an
96 existing slot we would need to be able to compare arbitrary
97 function objects, which is not feasible.</para>
98 </listitem>
99
100 <listitem>
101 <para><emphasis role="bold">Pass a token to
102 disconnect</emphasis>: this approach identifies slots with a
103 token that is easily comparable (e.g., a string), enabling
104 slots to be arbitrary function objects. While this approach is
105 essentially equivalent to the approach taken by Boost.Signals,
106 it is possibly more error-prone for several reasons:</para>
107
108 <itemizedlist>
109 <listitem>
110 <para>Connections and disconnections must be paired, so
111 the problem becomes similar to the problems incurred when
112 pairing <code>new</code> and <code>delete</code> for
113 dynamic memory allocation. While errors of this sort would
114 not be catastrophic for a signals and slots
115 implementation, their detection is generally
116 nontrivial.</para>
117 </listitem>
118
119 <listitem>
120 <para>Tokens must be unique, otherwise two slots will have
121 the same name and will be indistinguishable. In
122 environments where many connections will be made
123 dynamically, name generation becomes an additional task
124 for the user. Uniqueness of tokens also results in an
125 additional failure mode when attempting to connect a slot
126 using a token that has already been used.</para>
127 </listitem>
128
129 <listitem>
130 <para>More parameterization would be required, because the
131 token type must be user-defined. Additional
132 parameterization steepens the learning curver and
133 overcomplicates a simple interface.</para>
134 </listitem>
135 </itemizedlist>
136
137 <para> This type of interface is supported in Boost.Signals
138 via the slot grouping mechanism. It augments the
139 <code><classname>connection</classname></code> object-based
140 connection management scheme.</para>
141 </listitem>
142 </itemizedlist>
143 </section>
144
145 <section>
146 <title>Combiner Interface</title>
147
148 <para> The Combiner interface was chosen to mimic a call to an
149 algorithm in the C++ standard library. It is felt that by viewing
150 slot call results as merely a sequence of values accessed by input
151 iterators, the combiner interface would be most natural to a
152 proficient C++ programmer. Competing interface design generally
153 required the combiners to be constructed to conform to an
154 interface that would be customized for (and limited to) the
155 Signals library. While these interfaces are generally enable more
156 straighforward implementation of the signals &amp; slots
157 libraries, the combiners are unfortunately not reusable (either in
158 other signals &amp; slots libraries or within other generic
159 algorithms), and the learning curve is steepened slightly to learn
160 the specific combiner interface.</para>
161
162 <para> The Signals formulation of combiners is based on the
163 combiner using the "pull" mode of communication, instead of the
164 more complex "push" mechanism. With a "pull" mechanism, the
165 combiner's state can be kept on the stack and in the program
166 counter, because whenever new data is required (i.e., calling the
167 next slot to retrieve its return value), there is a simple
168 interface to retrieve that data immediately and without returning
169 from the combiner's code. Contrast this with the "push" mechanism,
170 where the combiner must keep all state in class members because
171 the combiner's routines will be invoked for each signal
172 called. Compare, for example, a combiner that returns the maximum
173 element from calling the slots. If the maximum element ever
174 exceeds 100, no more slots are to be called.</para>
175
176 <informaltable>
177 <tgroup cols="2" align="left">
178 <thead>
179 <row>
180 <entry><para>Pull</para></entry>
181 <entry><para>Push</para></entry>
182 </row>
183 </thead>
184 <tbody>
185 <row>
186 <entry>
187<programlisting>
188struct pull_max {
189 typedef int result_type;
190
191 template&lt;typename InputIterator&gt;
192 result_type operator()(InputIterator first,
193 InputIterator last)
194 {
195 if (first == last)
196 throw std::runtime_error("Empty!");
197
198 int max_value = *first++;
199 while(first != last &amp;&amp; *first &lt;= 100) {
200 if (*first &gt; max_value)
201 max_value = *first;
202 ++first;
203 }
204
205 return max_value;
206 }
207};
208</programlisting>
209</entry>
210 <entry>
211<programlisting>
212struct push_max {
213 typedef int result_type;
214
215 push_max() : max_value(), got_first(false) {}
216
217 // returns false when we want to stop
218 bool operator()(int result) {
219 if (result &gt; 100)
220 return false;
221
222 if (!got_first) {
223 got_first = true;
224 max_value = result;
225 return true;
226 }
227
228 if (result &gt; max_value)
229 max_value = result;
230
231 return true;
232 }
233
234 int get_value() const
235 {
236 if (!got_first)
237 throw std::runtime_error("Empty!");
238 return max_value;
239 }
240
241private:
242 int max_value;
243 bool got_first;
244};
245</programlisting>
246</entry>
247 </row>
248 </tbody>
249 </tgroup>
250 </informaltable>
251
252 <para>There are several points to note in these examples. The
253 "pull" version is a reusable function object that is based on an
254 input iterator sequence with an integer <code>value_type</code>,
255 and is very straightforward in design. The "push" model, on the
256 other hand, relies on an interface specific to the caller and is
257 not generally reusable. It also requires extra state values to
258 determine, for instance, if any elements have been
259 received. Though code quality and ease-of-use is generally
260 subjective, the "pull" model is clearly shorter and more reusable
261 and will often be construed as easier to write and understand,
262 even outside the context of a signals &amp; slots library.</para>
263
264 <para> The cost of the "pull" combiner interface is paid in the
265 implementation of the Signals library itself. To correctly handle
266 slot disconnections during calls (e.g., when the dereference
267 operator is invoked), one must construct the iterator to skip over
268 disconnected slots. Additionally, the iterator must carry with it
269 the set of arguments to pass to each slot (although a reference to
270 a structure containing those arguments suffices), and must cache
271 the result of calling the slot so that multiple dereferences don't
272 result in multiple calls. This apparently requires a large degree
273 of overhead, though if one considers the entire process of
274 invoking slots one sees that the overhead is nearly equivalent to
275 that in the "push" model, but we have inverted the control
276 structures to make iteration and dereference complex (instead of
277 making combiner state-finding complex).</para>
278 </section>
279
280 <section>
281 <title>Connection Interfaces: += operator</title>
282
283 <para> Boost.Signals supports a connection syntax with the form
284 <code>sig.<methodname>connect</methodname>(slot)</code>, but a
285 more terse syntax <code>sig += slot</code> has been suggested (and
286 has been used by other signals &amp; slots implementations). There
287 are several reasons as to why this syntax has been
288 rejected:</para>
289
290 <itemizedlist>
291 <listitem>
292 <para><emphasis role="bold">It's unnecessary</emphasis>: the
293 connection syntax supplied by Boost.Signals is no less
294 powerful that that supplied by the <code>+=</code>
295 operator. The savings in typing (<code>connect()</code>
296 vs. <code>+=</code>) is essentially negligible. Furthermore,
297 one could argue that calling <code>connect()</code> is more
298 readable than an overload of <code>+=</code>.</para>
299 </listitem>
300 <listitem>
301 <para><emphasis role="bold">Ambiguous return type</emphasis>:
302 there is an ambiguity concerning the return value of the
303 <code>+=</code> operation: should it be a reference to the
304 signal itself, to enable <code>sig += slot1 += slot2</code>,
305 or should it return a
306 <code><classname>connection</classname></code> for the
307 newly-created signal/slot connection?</para>
308 </listitem>
309
310 <listitem>
311 <para><emphasis role="bold">Gateway to operators -=,
312 +</emphasis>: when one has added a connection operator
313 <code>+=</code>, it seems natural to have a disconnection
314 operator <code>-=</code>. However, this presents problems when
315 the library allows arbitrary function objects to implicitly
316 become slots, because slots are no longer comparable. <!--
317 (see the discussion on this topic in User-level Connection
318 Management). --></para>
319
320 <para> The second obvious addition when one has
321 <code>operator+=</code> would be to add a <code>+</code>
322 operator that supports addition of multiple slots, followed by
323 assignment to a signal. However, this would require
324 implementing <code>+</code> such that it can accept any two
325 function objects, which is technically infeasible.</para>
326 </listitem>
327 </itemizedlist>
328 </section>
329
330 <section>
331 <title><code>trackable</code> rationale</title>
332
333 <para> The <code><classname>trackable</classname></code>
334 class is the primary user interface to automatic connection
335 lifetime management, and its design affects users directly. Two
336 issues stick out most: the odd copying behavior of
337 <code>trackable</code>, and the limitation requiring users to
338 derive from <code>trackable</code> to create types that can
339 participate in automatic connection management.</para>
340
341 <section>
342 <title><code>trackable</code> copying behavior</title>
343
344 <para> The copying behavior of
345 <code><classname>trackable</classname></code> is essentially
346 that <code><classname>trackable</classname></code> subobjects
347 are never copied; instead, the copy operation is merely a
348 no-op. To understand this, we look at the nature of a
349 signal-slot connection and note that the connection is based on
350 the entities that are being connected; when one of the entities
351 is destroyed, the connection is destroyed. Therefore, when a
352 <code><classname>trackable</classname></code> subobject is
353 copied, we cannot copy the connections because the connections
354 don't refer to the target entity - they refer to the source
355 entity. This reason is dual to the reason signals are
356 noncopyable: the slots connected to them are connected to that
357 particular signal, not the data contained in the signal.</para>
358 </section>
359
360 <section>
361 <title>Why derivation from <code>trackable</code>?</title>
362
363 <para> For <code><classname>trackable</classname></code> to work
364 properly, there are two constraints:</para>
365
366 <itemizedlist>
367 <listitem>
368 <para><code><classname>trackable</classname></code> must
369 have storage space to keep track of all connections made to
370 this object.</para>
371 </listitem>
372
373 <listitem>
374 <para><code><classname>trackable</classname></code> must be
375 notified when the object is being destructed so that it can
376 disconnect its connections.</para>
377 </listitem>
378 </itemizedlist>
379
380 <para>Clearly, deriving from
381 <code><classname>trackable</classname></code> meets these two
382 guidelines. We have not yet found a superior solution.</para>
383 </section>
384 </section>
385
386 <section>
387 <title>Comparison with other Signal/Slot implementations</title>
388
389 <section>
390 <title>libsigc++</title>
391
392 <para> <ulink
393 url="http://libsigc.sourceforge.net">libsigc++</ulink> is a C++
394 signals &amp; slots library that originally started as part of
395 an initiative to wrap the C interfaces to <ulink
396 url="http://www.gtk.org">GTK</ulink> libraries in C++, and has
397 grown to be a separate library maintained by Karl Nelson. There
398 are many similarities between libsigc++ and Boost.Signals, and
399 indeed Boost.Signals was strongly influenced by Karl Nelson and
400 libsigc++. A cursory inspection of each library will find a
401 similar syntax for the construction of signals and in the use of
402 connections and automatic connection lifetime management. There
403 are some major differences in design that separate these
404 libraries:</para>
405
406 <itemizedlist>
407 <listitem>
408 <para><emphasis role="bold">Slot definitions</emphasis>:
409 slots in libsigc++ are created using a set of primitives
410 defined by the library. These primitives allow binding of
411 objects (as part of the library), explicit adaptation from
412 the argument and return types of the signal to the argument
413 and return types of the slot (libsigc++ is, by default, more
414 strict about types than Boost.Signals). A discussion of this
415 approach with a comparison against the approach taken by
416 Boost.Signals is given in Choice of Slot Definitions.</para>
417 </listitem>
418
419 <listitem>
420 <para><emphasis role="bold">Combiner/Marshaller
421 interface</emphasis>: the equivalent to Boost.Signals
422 combiners in libsigc++ are the marshallers. Marshallers are
423 similar to the "push" interface described in Combiner
424 Interface, and a proper treatment of the topic is given
425 there.</para>
426 </listitem>
427 </itemizedlist>
428 </section>
429
430 <section>
431 <title>.NET delegates</title>
432
433 <para> <ulink url="http://www.microsoft.com">Microsoft</ulink>
434 has introduced the .NET Framework and an associated set of
435 languages and language extensions, one of which is the
436 delegate. Delegates are similar to signals and slots, but they
437 are more limited than most C++ signals and slots implementations
438 in that they:</para>
439
440 <itemizedlist>
441 <listitem>
442 <para>Require exact type matches between a delegate and what
443 it is calling.</para>
444 </listitem>
445
446 <listitem><para>Only return the result of the last target called, with no option for customization.</para></listitem>
447 <listitem>
448 <para>Must call a method with <code>this</code> already
449 bound.</para>
450 </listitem>
451 </itemizedlist>
452 </section>
453 </section>
454</section>