Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/signals/doc/tutorial.xml @ 35

Last change on this file since 35 was 29, checked in by landauf, 16 years ago

updated boost from 1_33_1 to 1_34_1

File size: 36.4 KB
Line 
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: 2007/01/29 20:04:57 $" id="signals.tutorial">
5  <title>Tutorial</title>
6
7  <using-namespace name="boost"/>
8  <using-namespace name="boost::signals"/>
9  <using-class name="boost::signalN"/>
10
11  <section>
12    <title>How to Read this Tutorial</title>
13<para>This tutorial is not meant to be read linearly. Its top-level
14structure roughly separates different concepts in the library
15(e.g., handling calling multiple slots, passing values to and from
16slots) and in each of these concepts the basic ideas are presented
17first and then more complex uses of the library are described
18later. Each of the sections is marked <emphasis>Beginner</emphasis>,
19<emphasis>Intermediate</emphasis>, or <emphasis>Advanced</emphasis> to help guide the
20reader. The <emphasis>Beginner</emphasis> sections include information that all
21library users should know; one can make good use of the Signals
22library after having read only the <emphasis>Beginner</emphasis> sections. The
23<emphasis>Intermediate</emphasis> sections build on the <emphasis>Beginner</emphasis>
24sections with slightly more complex uses of the library. Finally,
25the <emphasis>Advanced</emphasis> sections detail very advanced uses of the
26Signals library, that often require a solid working knowledge of
27the <emphasis>Beginner</emphasis> and <emphasis>Intermediate</emphasis> topics; most users
28will not need to read the <emphasis>Advanced</emphasis> sections.</para>
29</section>
30
31<section><title>Compatibility Note</title> 
32
33<para>Boost.Signals has two syntactical forms: the preferred form and
34the compatibility form. The preferred form fits more closely with the
35C++ language and reduces the number of separate template parameters
36that need to be considered, often improving readability; however, the
37preferred form is not supported on all platforms due to compiler
38bugs. The compatible form will work on all compilers supported by
39Boost.Signals. Consult the table below to determine which syntactic
40form to use for your compiler. Users of Boost.Function, please note
41that the preferred syntactic form in Signals is equivalent to that of
42Function's preferred syntactic form.</para>
43
44<para>If your compiler does not appear in this list, please try the
45preferred syntax and report your results to the Boost list so that
46we can keep this table up-to-date.</para>
47
48  <informaltable>
49    <tgroup cols="2" align="left">
50      <thead>
51        <row>
52          <entry>Preferred syntax</entry>
53          <entry>Portable syntax</entry>
54        </row>
55      </thead>
56      <tbody>
57        <row>
58          <entry>
59            <itemizedlist>
60              <listitem><para>GNU C++ 2.95.x, 3.0.x, 3.1.x</para></listitem>
61              <listitem><para>Comeau C++ 4.2.45.2</para></listitem>
62              <listitem><para>SGI MIPSpro 7.3.0</para></listitem>
63              <listitem><para>Intel C++ 5.0, 6.0</para></listitem>
64              <listitem><para>Compaq's cxx 6.2</para></listitem>
65              <listitem><para>Microsoft Visual C++ 7.1</para></listitem>
66            </itemizedlist>
67          </entry>
68          <entry>
69            <itemizedlist>
70              <listitem><para><emphasis>Any compiler supporting the preferred syntax</emphasis></para></listitem>
71              <listitem><para>Microsoft Visual C++ 6.0, 7.0</para></listitem>
72              <listitem><para>Borland C++ 5.5.1</para></listitem>
73              <listitem><para>Sun WorkShop 6 update 2 C++ 5.3</para></listitem>
74              <listitem><para>Metrowerks CodeWarrior 8.1</para></listitem>
75            </itemizedlist>
76          </entry>
77        </row>
78      </tbody>
79    </tgroup>
80  </informaltable>
81</section>
82
83<section><title>Hello, World! (Beginner)</title>
84<para>The following example writes "Hello, World!" using signals and
85slots. First, we create a signal <code>sig</code>, a signal that
86takes no arguments and has a void return value. Next, we connect
87the <code>hello</code> function object to the signal using the
88<code>connect</code> method. Finally, use the signal
89<code>sig</code> like a function to call the slots, which in turns
90invokes <code>HelloWorld::operator()</code> to print "Hello,
91World!".</para>
92  <informaltable>
93    <tgroup cols="2" align="left">
94      <thead>
95        <row>
96          <entry>Preferred syntax</entry>
97          <entry>Portable syntax</entry>
98        </row>
99      </thead>
100      <tbody>
101        <row>
102          <entry>
103<programlisting>
104struct HelloWorld
105{
106  void operator()() const
107  {
108    std::cout &lt;&lt; "Hello, World!" &lt;&lt; std::endl;
109  }
110};
111
112// ...
113
114// Signal with no arguments and a void return value
115<classname>boost::signal</classname>&lt;void ()&gt; sig;
116
117// Connect a HelloWorld slot
118HelloWorld hello;
119sig.<methodname>connect</methodname>(hello);
120
121// Call all of the slots
122sig();
123</programlisting>
124</entry>
125<entry>
126<programlisting>
127struct HelloWorld
128{
129  void operator()() const
130  {
131    std::cout &lt;&lt; "Hello, World!" &lt;&lt; std::endl;
132  }
133};
134
135// ...
136
137// Signal with no arguments and a void return value
138<classname alt="boost::signalN">boost::signal0</classname>&lt;void&gt; sig;
139
140// Connect a HelloWorld slot
141HelloWorld hello;
142sig.<methodname>connect</methodname>(hello);
143
144// Call all of the slots
145sig();
146</programlisting>
147</entry>
148          </row>
149        </tbody>
150      </tgroup>
151    </informaltable>
152</section>
153
154<section><title>Calling multiple slots</title>
155<section><title>Connecting multiple slots (Beginner)</title>
156<para>Calling a single slot from a signal isn't very interesting, so
157we can make the Hello, World program more interesting by splitting
158the work of printing "Hello, World!" into two completely separate
159slots. The first slot will print "Hello" and may look like
160this:</para>
161<programlisting>
162struct Hello
163{
164  void operator()() const
165  {
166    std::cout &lt;&lt; "Hello";
167  }
168};
169</programlisting>
170<para>The second slot will print ", World!" and a newline, to complete
171the program. The second slot may look like this:</para>
172<programlisting>
173struct World
174{
175  void operator()() const
176  {
177    std::cout &lt;&lt; ", World!" &lt;&lt; std::endl;
178  }
179};
180</programlisting>
181<para>Like in our previous example, we can create a signal
182<code>sig</code> that takes no arguments and has a
183<code>void</code> return value. This time, we connect both a
184<code>hello</code> and a <code>world</code> slot to the same
185signal, and when we call the signal both slots will be called.</para>
186  <informaltable>
187    <tgroup cols="2" align="left">
188      <thead>
189        <row>
190          <entry>Preferred syntax</entry>
191          <entry>Portable syntax</entry>
192        </row>
193      </thead>
194      <tbody>
195        <row>
196          <entry>
197<programlisting>
198<classname>boost::signal</classname>&lt;void ()&gt; sig;
199
200sig.<methodname>connect</methodname>(Hello());
201sig.<methodname>connect</methodname>(World());
202
203sig();
204</programlisting>
205</entry>
206<entry>
207<programlisting>
208<classname alt="boost::signalN">boost::signal0</classname>&lt;void&gt; sig;
209
210sig.<methodname>connect</methodname>(Hello());
211sig.<methodname>connect</methodname>(World());
212
213sig();
214</programlisting>
215</entry>
216            </row>
217          </tbody>
218        </tgroup>
219      </informaltable>
220<para>By default, slots are called in first-in first-out (FIFO) order,
221so the output of this program will be as expected:</para>
222<programlisting>
223Hello, World!
224</programlisting>
225</section>
226
227<section><title>Ordering slot call groups (Intermediate)</title>
228<para>Slots are free to have side effects, and that can mean that some
229slots will have to be called before others even if they are not connected in that order. The Boost.Signals
230library allows slots to be placed into groups that are ordered in
231some way. For our Hello, World program, we want "Hello" to be
232printed before ", World!", so we put "Hello" into a group that must
233be executed before the group that ", World!" is in. To do this, we
234can supply an extra parameter at the beginning of the
235<code>connect</code> call that specifies the group. Group values
236are, by default, <code>int</code>s, and are ordered by the integer
237&lt; relation. Here's how we construct Hello, World:</para>
238  <informaltable>
239    <tgroup cols="2" align="left">
240      <thead>
241        <row>
242          <entry>Preferred syntax</entry>
243          <entry>Portable syntax</entry>
244        </row>
245      </thead>
246      <tbody>
247        <row>
248          <entry>
249<programlisting>
250<classname>boost::signal</classname>&lt;void ()&gt; sig;
251sig.<methodname>connect</methodname>(1, World());
252sig.<methodname>connect</methodname>(0, Hello());
253sig();
254</programlisting>
255</entry>
256            <entry>
257<programlisting>
258<classname alt="boost::signalN">boost::signal0</classname>&lt;void&gt; sig;
259sig.<methodname>connect</methodname>(1, World());
260sig.<methodname>connect</methodname>(0, Hello());
261sig();
262</programlisting>
263</entry>
264            </row>
265          </tbody>
266        </tgroup>
267      </informaltable>
268
269<para>This program will correctly print "Hello, World!", because the
270<code>Hello</code> object is in group 0, which precedes group 1 where
271the <code>World</code> object resides. The group
272parameter is, in fact, optional. We omitted it in the first Hello,
273World example because it was unnecessary when all of the slots are
274independent. So what happens if we mix calls to connect that use the
275group parameter and those that don't? The "unnamed" slots (i.e., those
276that have been connected without specifying a group name) can be
277placed at the front or back of the slot list (by passing
278<code>boost::signals::at_front</code> or <code>boost::signals::at_back</code>
279as the last parameter to <code><methodname
280alt="boost::signalN::connect">connect</methodname></code>, respectively), and defaults to the end of the list. When
281a group is specified, the final parameter describes where the slot
282will be placed within the group ordering. If we add a new slot
283to our example like this:</para>
284<programlisting>
285struct GoodMorning
286{
287  void operator()() const
288  {
289    std::cout &lt;&lt; "... and good morning!" &lt;&lt; std::endl;
290  }
291};
292
293sig.<methodname>connect</methodname>(GoodMorning());
294</programlisting>
295<para>... we will get the result we wanted:</para>
296<programlisting>
297Hello, World!
298... and good morning!
299</programlisting>
300</section>
301</section>
302
303<section><title>Passing values to and from slots</title>
304<section><title>Slot Arguments (Beginner)</title>
305<para>Signals can propagate arguments to each of the slots they call.
306For instance, a signal that propagates mouse motion events might
307want to pass along the new mouse coordinates and whether the mouse
308buttons are pressed.</para>
309<para>As an example, we'll create a signal that passes two
310<code>float</code> arguments to its slots. Then we'll create a few
311slots that print the results of various arithmetic operations on
312these values.</para>
313<programlisting>
314void print_sum(float x, float y)
315{
316  std::cout &lt;&lt; "The sum is " &lt;&lt; x+y &lt;&lt; std::endl;
317}
318
319void print_product(float x, float y)
320{
321  std::cout &lt;&lt; "The product is " &lt;&lt; x*y &lt;&lt; std::endl;
322}
323
324void print_difference(float x, float y)
325{
326  std::cout &lt;&lt; "The difference is " &lt;&lt; x-y &lt;&lt; std::endl;
327}
328
329void print_quotient(float x, float y)
330{
331  std::cout &lt;&lt; "The quotient is " &lt;&lt; x/y &lt;&lt; std::endl;
332}
333</programlisting>
334
335  <informaltable>
336    <tgroup cols="2" align="left">
337      <thead>
338        <row>
339          <entry>Preferred syntax</entry>
340          <entry>Portable syntax</entry>
341        </row>
342      </thead>
343      <tbody>
344        <row>
345          <entry>
346<programlisting>
347<classname>boost::signal</classname>&lt;void (float, float)&gt; sig;
348
349sig.<methodname>connect</methodname>(&amp;print_sum);
350sig.<methodname>connect</methodname>(&amp;print_product);
351sig.<methodname>connect</methodname>(&amp;print_difference);
352sig.<methodname>connect</methodname>(&amp;print_quotient);
353
354sig(5, 3);
355</programlisting>
356</entry>
357<entry>
358<programlisting>
359<classname alt="boost::signalN">boost::signal2</classname>&lt;void, float, float&gt; sig;
360
361sig.<methodname>connect</methodname>(&amp;print_sum);
362sig.<methodname>connect</methodname>(&amp;print_product);
363sig.<methodname>connect</methodname>(&amp;print_difference);
364sig.<methodname>connect</methodname>(&amp;print_quotient);
365
366sig(5, 3);
367</programlisting>
368</entry>
369              </row>
370            </tbody>
371          </tgroup>
372        </informaltable>
373
374<para>This program will print out the following:</para>
375<programlisting>
376The sum is 8
377The product is 15
378The difference is 2
379The quotient is 1.66667
380</programlisting>
381<para>So any values that are given to <code>sig</code> when it is
382called like a function are passed to each of the slots. We have to
383declare the types of these values up front when we create the
384signal. The type <code><classname>boost::signal</classname>&lt;void (float,
385float)&gt;</code> means that the signal has a <code>void</code>
386return value and takes two <code>float</code> values. Any slot
387connected to <code>sig</code> must therefore be able to take two
388<code>float</code> values.</para>
389</section>
390
391<section><title>Signal Return Values (Advanced)</title>
392<para>Just as slots can receive arguments, they can also return
393values. These values can then be returned back to the caller of the
394signal through a <firstterm>combiner</firstterm>. The combiner is a mechanism
395that can take the results of calling slots (there many be no
396results or a hundred; we don't know until the program runs) and
397coalesces them into a single result to be returned to the caller.
398The single result is often a simple function of the results of the
399slot calls: the result of the last slot call, the maximum value
400returned by any slot, or a container of all of the results are some
401possibilities.</para>
402<para>We can modify our previous arithmetic operations example
403slightly so that the slots all return the results of computing the
404product, quotient, sum, or difference. Then the signal itself can
405return a value based on these results to be printed:</para>
406  <informaltable>
407    <tgroup cols="2" align="left">
408      <thead>
409        <row>
410          <entry>Preferred syntax</entry>
411          <entry>Portable syntax</entry>
412        </row>
413      </thead>
414      <tbody>
415        <row>
416          <entry>
417<programlisting>
418float product(float x, float y) { return x*y; }
419float quotient(float x, float y) { return x/y; }
420float sum(float x, float y) { return x+y; }
421float difference(float x, float y) { return x-y; }
422
423<classname>boost::signal</classname>&lt;float (float x, float y)&gt; sig;
424
425sig.<methodname>connect</methodname>(&amp;product);
426sig.<methodname>connect</methodname>(&amp;quotient);
427sig.<methodname>connect</methodname>(&amp;sum);
428sig.<methodname>connect</methodname>(&amp;difference);
429
430std::cout &lt;&lt; sig(5, 3) &lt;&lt; std::endl;
431</programlisting>
432</entry>
433<entry>
434<programlisting>
435float product(float x, float y) { return x*y; }
436float quotient(float x, float y) { return x/y; }
437float sum(float x, float y) { return x+y; }
438float difference(float x, float y) { return x-y; }
439
440<classname alt="boost::signalN">boost::signal2</classname>&lt;float, float, float&gt; sig;
441
442sig.<methodname>connect</methodname>(&amp;product);
443sig.<methodname>connect</methodname>(&amp;quotient);
444sig.<methodname>connect</methodname>(&amp;sum);
445sig.<methodname>connect</methodname>(&amp;difference);
446
447std::cout &lt;&lt; sig(5, 3) &lt;&lt; std::endl;
448</programlisting>
449</entry>
450            </row>
451          </tbody>
452        </tgroup>
453      </informaltable>
454
455<para>This example program will output <code>2</code>. This is because the
456default behavior of a signal that has a return type
457(<code>float</code>, the first template argument given to the
458<code><classname>boost::signal</classname></code> class template) is to call all slots and
459then return the result returned by the last slot called. This
460behavior is admittedly silly for this example, because slots have
461no side effects and the result is the last slot connect.</para>
462<para>A more interesting signal result would be the maximum of the
463values returned by any slot. To do this, we create a custom
464combiner that looks like this:</para>
465<programlisting>
466template&lt;typename T&gt;
467struct maximum
468{
469  typedef T result_type;
470
471  template&lt;typename InputIterator&gt;
472  T operator()(InputIterator first, InputIterator last) const
473  {
474    // If there are no slots to call, just return the
475    // default-constructed value
476    if (first == last)
477      return T();
478
479    T max_value = *first++;
480    while (first != last) {
481      if (max_value &lt; *first)
482        max_value = *first;
483      ++first;
484    }
485 
486    return max_value;
487  }
488};
489</programlisting>
490<para>The <code>maximum</code> class template acts as a function
491object. Its result type is given by its template parameter, and
492this is the type it expects to be computing the maximum based on
493(e.g., <code>maximum&lt;float&gt;</code> would find the maximum
494<code>float</code> in a sequence of <code>float</code>s). When a
495<code>maximum</code> object is invoked, it is given an input
496iterator sequence <code>[first, last)</code> that includes the
497results of calling all of the slots. <code>maximum</code> uses this
498input iterator sequence to calculate the maximum element, and
499returns that maximum value.</para>
500<para>We actually use this new function object type by installing it
501as a combiner for our signal. The combiner template argument
502follows the signal's calling signature:</para>
503  <informaltable>
504    <tgroup cols="2" align="left">
505      <thead>
506        <row>
507          <entry>Preferred syntax</entry>
508          <entry>Portable syntax</entry>
509        </row>
510      </thead>
511      <tbody>
512        <row>
513          <entry>
514<programlisting>
515<classname>boost::signal</classname>&lt;float (float x, float y),
516              maximum&lt;float&gt; &gt; sig;
517</programlisting>
518</entry>
519<entry>
520<programlisting>
521<classname alt="boost::signalN">boost::signal2</classname>&lt;float, float, float,
522               maximum&lt;float&gt; &gt; sig;
523</programlisting>
524</entry>
525            </row>
526          </tbody>
527        </tgroup>
528      </informaltable>
529
530<para>Now we can connect slots that perform arithmetic functions and
531use the signal:</para>
532<programlisting>
533sig.<methodname>connect</methodname>(&amp;quotient);
534sig.<methodname>connect</methodname>(&amp;product);
535sig.<methodname>connect</methodname>(&amp;sum);
536sig.<methodname>connect</methodname>(&amp;difference);
537
538std::cout &lt;&lt; sig(5, 3) &lt;&lt; std::endl;
539</programlisting>
540<para>The output of this program will be <code>15</code>, because
541regardless of the order in which the slots are connected, the product
542of 5 and 3 will be larger than the quotient, sum, or
543difference.</para>
544<para>In other cases we might want to return all of the values
545computed by the slots together, in one large data structure. This
546is easily done with a different combiner:</para>
547<programlisting>
548template&lt;typename Container&gt;
549struct aggregate_values
550{
551  typedef Container result_type;
552
553  template&lt;typename InputIterator&gt;
554  Container operator()(InputIterator first, InputIterator last) const
555  {
556    return Container(first, last);
557  }
558};
559</programlisting>
560<para>
561Again, we can create a signal with this new combiner:
562</para>
563  <informaltable>
564    <tgroup cols="2" align="left">
565      <thead>
566        <row>
567          <entry>Preferred syntax</entry>
568          <entry>Portable syntax</entry>
569        </row>
570      </thead>
571      <tbody>
572        <row>
573          <entry>
574<programlisting>
575<classname>boost::signal</classname>&lt;float (float, float),
576    aggregate_values&lt;std::vector&lt;float&gt; &gt; &gt; sig;
577
578sig.<methodname>connect</methodname>(&amp;quotient);
579sig.<methodname>connect</methodname>(&amp;product);
580sig.<methodname>connect</methodname>(&amp;sum);
581sig.<methodname>connect</methodname>(&amp;difference);
582
583std::vector&lt;float&gt; results = sig(5, 3);
584std::copy(results.begin(), results.end(),
585    std::ostream_iterator&lt;float&gt;(cout, " "));
586</programlisting>
587</entry>
588<entry>
589<programlisting>
590<classname alt="boost::signalN">boost::signal2</classname>&lt;float, float, float,
591    aggregate_values&lt;std::vector&lt;float&gt; &gt; &gt; sig;
592
593sig.<methodname>connect</methodname>(&amp;quotient);
594sig.<methodname>connect</methodname>(&amp;product);
595sig.<methodname>connect</methodname>(&amp;sum);
596sig.<methodname>connect</methodname>(&amp;difference);
597
598std::vector&lt;float&gt; results = sig(5, 3);
599std::copy(results.begin(), results.end(),
600    std::ostream_iterator&lt;float&gt;(cout, " "));
601</programlisting>
602</entry>
603            </row>
604          </tbody>
605        </tgroup>
606      </informaltable>
607
608<para>The output of this program will contain 15, 8, 1.6667, and 2. It
609is interesting here that
610the first template argument for the <code>signal</code> class,
611<code>float</code>, is not actually the return type of the signal.
612Instead, it is the return type used by the connected slots and will
613also be the <code>value_type</code> of the input iterators passed
614to the combiner. The combiner itself is a function object and its
615<code>result_type</code> member type becomes the return type of the
616signal.</para>
617<para>The input iterators passed to the combiner transform dereference
618operations into slot calls. Combiners therefore have the option to
619invoke only some slots until some particular criterion is met. For
620instance, in a distributed computing system, the combiner may ask
621each remote system whether it will handle the request. Only one
622remote system needs to handle a particular request, so after a
623remote system accepts the work we do not want to ask any other
624remote systems to perform the same task. Such a combiner need only
625check the value returned when dereferencing the iterator, and
626return when the value is acceptable. The following combiner returns
627the first non-NULL pointer to a <code>FulfilledRequest</code> data
628structure, without asking any later slots to fulfill the
629request:</para>
630<programlisting>
631struct DistributeRequest {
632  typedef FulfilledRequest* result_type;
633
634  template&lt;typename InputIterator&gt;
635  result_type operator()(InputIterator first, InputIterator last) const
636  {
637    while (first != last) {
638      if (result_type fulfilled = *first)
639        return fulfilled;
640      ++first;
641    }
642    return 0;
643  }
644};
645</programlisting>
646</section>
647</section>
648
649<section><title>Connection Management</title>
650<section><title>Disconnecting Slots (Beginner)</title>
651<para>Slots aren't expected to exist indefinately after they are
652connected. Often slots are only used to receive a few events and
653are then disconnected, and the programmer needs control to decide
654when a slot should no longer be connected.</para>
655<para>The entry point for managing connections explicitly is the
656<code><classname>boost::signals::connection</classname></code> class. The
657<code><classname>connection</classname></code> class uniquely represents the connection
658between a particular signal and a particular slot. The
659<code><methodname alt="connection::connected">connected</methodname>()</code> method checks if the signal and slot are
660still connected, and the <code><methodname alt="connection::disconnect">disconnect()</methodname></code> method
661disconnects the signal and slot if they are connected before it is
662called. Each call to the signal's <code>connect()</code> method
663returns a connection object, which can be used to determine if the
664connection still exists or to disconnect the signal and slot.</para>
665<programlisting>
666boost::signals::connection c = sig.<methodname>connect</methodname>(HelloWorld());
667if (c.<methodname>connected</methodname>()) {
668<emphasis>// c is still connected to the signal</emphasis>
669  sig(); <emphasis>// Prints "Hello, World!"</emphasis>
670}
671
672c.disconnect(); <emphasis>// Disconnect the HelloWorld object</emphasis>
673assert(!c.<methodname>connected</methodname>()); <emphasis>c isn't connected any more</emphasis>
674
675sig(); <emphasis>// Does nothing: there are no connected slots</emphasis>
676</programlisting>
677</section>
678
679<section><title>Blocking Slots (Beginner)</title> 
680
681<para>Slots can be temporarily "blocked", meaning that they will be
682ignored when the signal is invoked but have not been disconnected. The
683<code><methodname>block</methodname></code> member function
684temporarily blocks a slot, which can be unblocked via
685<code><methodname>unblock</methodname></code>. Here is an example of
686blocking/unblocking slots:</para>
687
688<programlisting>
689boost::signals::connection c = sig.<methodname>connect</methodname>(HelloWorld());
690sig(); <emphasis>// Prints "Hello, World!"</emphasis>
691
692c.<methodname>block</methodname>(); <emphasis>// block the slot</emphasis>
693assert(c.<methodname>blocked</methodname>());
694sig(); <emphasis>// No output: the slot is blocked</emphasis>
695
696c.<methodname>unblock</methodname>(); <emphasis>// unblock the slot</emphasis>
697sig(); <emphasis>// Prints "Hello, World!"</emphasis>
698</programlisting>
699
700</section>
701
702<section><title>Scoped connections (Intermediate)</title>
703<para>The <code>boost::signals::scoped_connection</code> class
704references a signal/slot connection that will be disconnected when
705the <code>scoped_connection</code> class goes out of scope. This
706ability is useful when a connection need only be temporary,
707e.g.,</para>
708<programlisting>
709{
710  boost::signals::scoped_connection c = sig.<methodname>connect</methodname>(ShortLived());
711  sig(); <emphasis>// will call ShortLived function object</emphasis>
712}
713sig(); <emphasis>// ShortLived function object no longer connected to sig</emphasis>
714</programlisting>
715</section>
716
717<section><title>Disconnecting equivalent slots (Intermediate)</title>
718<para>One can disconnect slots that are equivalent to a given function
719object using a form of the
720<code><methodname>disconnect</methodname></code> method, so long as
721the type of the function object has an accessible <code>==</code>
722operator. For instance:
723
724</para>
725  <informaltable>
726    <tgroup cols="2" align="left">
727      <thead>
728        <row>
729          <entry>Preferred syntax</entry>
730          <entry>Portable syntax</entry>
731        </row>
732      </thead>
733      <tbody>
734        <row>
735          <entry>
736<programlisting>
737void foo();
738void bar();
739
740signal&lt;void()&gt; sig;
741
742sig.connect(&amp;foo);
743sig.connect(&amp;bar);
744
745// disconnects foo, but not bar
746sig.disconnect(&amp;foo);
747</programlisting>
748</entry>
749<entry>
750<programlisting>
751void foo();
752void bar();
753
754signal0&lt;void&gt; sig;
755
756sig.connect(&amp;foo);
757sig.connect(&amp;bar);
758
759// disconnects foo, but not bar
760sig.disconnect(&amp;foo);
761</programlisting>
762</entry>
763            </row>
764          </tbody>
765        </tgroup>
766      </informaltable>
767
768</section>
769
770<section><title>Automatic connection management (Intermediate)</title>
771<para>Boost.Signals can automatically track the lifetime of objects
772involved in signal/slot connections, including automatic
773disconnection of slots when objects involved in the slot call are
774destroyed. For instance, consider a simple news delivery service,
775where clients connect to a news provider that then sends news to
776all connected clients as information arrives. The news delivery
777service may be constructed like this: </para>
778  <informaltable>
779    <tgroup cols="2" align="left">
780      <thead>
781        <row>
782          <entry>Preferred syntax</entry>
783          <entry>Portable syntax</entry>
784        </row>
785      </thead>
786      <tbody>
787        <row>
788          <entry>
789<programlisting>
790class NewsItem { /* ... */ };
791
792boost::signal&lt;void (const NewsItem&amp;)&gt; deliverNews;
793</programlisting>
794</entry>
795<entry>
796<programlisting>
797class NewsItem { /* ... */ };
798
799boost::signal1&lt;void, const NewsItem&amp;&gt; deliverNews;
800</programlisting>
801</entry>
802            </row>
803          </tbody>
804        </tgroup>
805      </informaltable>
806
807<para>Clients that wish to receive news updates need only connect a
808function object that can receive news items to the
809<code>deliverNews</code> signal. For instance, we may have a
810special message area in our application specifically for news,
811e.g.,:</para>
812<programlisting>
813struct NewsMessageArea : public MessageArea
814{
815public:
816  // ...
817
818  void displayNews(const NewsItem&amp; news) const
819  {
820    messageText = news.text();
821    update();
822  }
823};
824
825// ...
826NewsMessageArea newsMessageArea = new NewsMessageArea(/* ... */);
827// ...
828deliverNews.<methodname>connect</methodname>(boost::bind(&amp;NewsMessageArea::displayNews,
829                                newsMessageArea, _1));
830</programlisting>
831<para>However, what if the user closes the news message area,
832destroying the <code>newsMessageArea</code> object that
833<code>deliverNews</code> knows about? Most likely, a segmentation
834fault will occur. However, with Boost.Signals one need only make
835<code>NewsMessageArea</code> <emphasis>trackable</emphasis>, and the slot
836involving <code>newsMessageArea</code> will be disconnected when
837<code>newsMessageArea</code> is destroyed. The
838<code>NewsMessageArea</code> class is made trackable by deriving
839publicly from the <code>boost::signals::trackable</code> class,
840e.g.:</para>
841<programlisting>
842struct NewsMessageArea : public MessageArea, public boost::signals::trackable
843{
844  // ...
845};
846</programlisting>
847<para>At this time there is a significant limitation to the use of
848<code>trackable</code> objects in making slot connections: function
849objects built using Boost.Bind are understood, such that pointers
850or references to <code>trackable</code> objects passed to
851<code>boost::bind</code> will be found and tracked.</para>
852<para><emphasis role="bold">Warning</emphasis>: User-defined function objects and function
853objects from other libraries (e.g., Boost.Function or Boost.Lambda)
854do not implement the required interfaces for <code>trackable</code>
855object detection, and <emphasis>will silently ignore any bound trackable
856objects</emphasis>. Future versions of the Boost libraries will address
857this limitation.</para>
858</section>
859
860<section><title>When can disconnections occur? (Intermediate)</title>
861<para>Signal/slot disconnections occur when any of these conditions
862occur:</para>
863<itemizedlist>
864<listitem><para>The connection is explicitly disconnected via the connection's
865<code>disconnect</code> method directly, or indirectly via the
866signal's <code>disconnect</code> method or
867<code>scoped_connection</code>'s destructor.</para></listitem>
868<listitem><para>A <code>trackable</code> object bound to the slot is
869destroyed.</para></listitem>
870<listitem><para>The signal is destroyed.</para></listitem></itemizedlist>
871<para>These events can occur at any time without disrupting a signal's
872calling sequence. If a signal/slot connection is disconnected at
873any time during a signal's calling sequence, the calling sequence
874will still continue but will not invoke the disconnected slot.
875Additionally, a signal may be destroyed while it is in a calling
876sequence, and which case it will complete its slot call sequence
877but may not be accessed directly.</para>
878<para>Signals may be invoked recursively (e.g., a signal A calls a
879slot B that invokes signal A...). The disconnection behavior does
880not change in the recursive case, except that the slot calling
881sequence includes slot calls for all nested invocations of the
882signal.</para>
883</section>
884
885<section><title>Passing slots (Intermediate)</title>
886<para>Slots in the Boost.Signals library are created from arbitrary
887function objects, and therefore have no fixed type. However, it is
888commonplace to require that slots be passed through interfaces that
889cannot be templates. Slots can be passed via the
890<code>slot_type</code> for each particular signal type and any
891function object compatible with the signature of the signal can be
892passed to a <code>slot_type</code> parameter. For instance:</para>
893  <informaltable>
894    <tgroup cols="2" align="left">
895      <thead>
896        <row>
897          <entry>Preferred syntax</entry>
898          <entry>Portable syntax</entry>
899        </row>
900      </thead>
901      <tbody>
902        <row>
903          <entry>
904<programlisting>
905class Button
906{
907  typedef boost::signal&lt;void (int x, int y)&gt; OnClick;
908
909public:
910  void doOnClick(const OnClick::slot_type&amp; slot);
911
912private:
913  OnClick onClick;
914};
915
916void Button::doOnClick(
917      const OnClick::slot_type&amp; slot
918    )
919{
920  onClick.<methodname>connect</methodname>(slot);
921}
922
923void printCoordinates(long x, long y)
924{
925  std::cout &lt;&lt; "(" &lt;&lt; x &lt;&lt; ", " &lt;&lt; y &lt;&lt; ")\n";
926}
927
928void f(Button&amp; button)
929{
930  button.doOnClick(&amp;printCoordinates);
931}
932</programlisting>
933</entry>
934<entry>
935<programlisting>
936class Button
937{
938  typedef <classname alt="boost::signalN">boost::signal2</classname>&lt;void,int,int&gt; OnClick;
939
940public:
941  void doOnClick(const OnClick::slot_type&amp; slot);
942
943private:
944  OnClick onClick;
945};
946
947void Button::doOnClick(
948      const OnClick::slot_type&amp; slot
949    )
950{
951  onClick.<methodname>connect</methodname>(slot);
952}
953
954void printCoordinates(long x, long y)
955{
956  std::cout &lt;&lt; "(" &lt;&lt; x &lt;&lt; ", " &lt;&lt; y &lt;&lt; ")\n";
957}
958
959void f(Button&amp; button)
960{
961  button.doOnClick(&amp;printCoordinates);
962}
963</programlisting>
964</entry>
965            </row>
966          </tbody>
967        </tgroup>
968      </informaltable>
969
970<para>The <code>doOnClick</code> method is now functionally equivalent
971to the <code>connect</code> method of the <code>onClick</code>
972signal, but the details of the <code>doOnClick</code> method can be
973hidden in an implementation detail file.</para>
974</section>
975</section>
976
977<section>
978  <title>Example: Document-View</title>
979 
980  <para>Signals can be used to implement flexible Document-View
981  architectures. The document will contain a signal to which each of
982  the views can connect. The following <code>Document</code> class
983  defines a simple text document that supports mulitple views. Note
984  that it stores a single signal to which all of the views will be
985  connected.</para>
986
987  <programlisting>class Document
988{
989public:
990    typedef boost::signal&lt;void (bool)&gt;  signal_t;
991    typedef boost::signals::connection  connection_t;
992
993public:
994    Document()
995    {}
996
997    connection_t connect(signal_t::slot_function_type subscriber)
998    {
999        return m_sig.connect(subscriber);
1000    }
1001
1002    void disconnect(connection_t subscriber)
1003    {
1004        subscriber.disconnect();
1005    }
1006
1007    void append(const char* s)
1008    {
1009        m_text += s;
1010        m_sig(true);
1011    }
1012
1013    const std::string&amp; getText() const
1014    {
1015        return m_text;
1016    }
1017
1018private:
1019    signal_t    m_sig;
1020    std::string m_text;
1021};</programlisting>
1022
1023  <para>Next, we can define a <code>View</code> base class from which
1024  views can derive. This isn't strictly required, but it keeps the
1025  Document-View logic separate from the logic itself. Note that the
1026  constructor just connects the view to the document and the
1027  destructor disconnects the view.</para>
1028
1029  <programlisting>
1030class View
1031{
1032public:
1033    View(Document&amp; m)
1034        : m_document(m)
1035    {
1036        m_connection = m_document.connect(boost::bind(&amp;View::refresh, this, _1));
1037    }
1038
1039    virtual ~View()
1040    {
1041        m_document.disconnect(m_connection);
1042    }
1043
1044    virtual void refresh(bool bExtended) const = 0;
1045
1046protected:
1047    Document&amp;               m_document;
1048
1049private:
1050    Document::connection_t  m_connection;
1051};
1052  </programlisting>
1053
1054  <para>Finally, we can begin to define views. The
1055  following <code>TextView</code> class provides a simple view of the
1056    document text.</para>
1057
1058  <programlisting>class TextView : public View
1059{
1060public:
1061    TextView(Document&amp; doc)
1062        : View(doc)
1063    {}
1064
1065    virtual void refresh(bool bExtended) const
1066    {
1067        std::cout &lt;&lt; "TextView: " &lt;&lt; m_document.getText() &lt;&lt; std::endl;
1068    }
1069};</programlisting>
1070
1071  <para>Alternatively, we can provide a view of the document
1072    translated into hex values using the <code>HexView</code>
1073    view:</para>
1074
1075  <programlisting>class HexView : public View
1076{
1077public:
1078    HexView(Document&amp; doc)
1079        : View(doc)
1080    {}
1081
1082    virtual void refresh(bool bExtended) const
1083    {
1084        const std::string&amp;  s = m_document.getText();
1085
1086        std::cout &lt;&lt; "HexView:";
1087
1088        for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
1089            std::cout &lt;&lt; ' ' &lt;&lt; std::hex &lt;&lt; static_cast&lt;int&gt;(*it);
1090
1091        std::cout &lt;&lt; std::endl;
1092    }
1093};</programlisting>
1094
1095  <para>To tie the example together, here is a
1096  simple <code>main</code> function that sets up two views and then
1097    modifies the document:</para>
1098
1099  <programlisting>int main(int argc, char* argv[])
1100{
1101    Document    doc;
1102    TextView    v1(doc);
1103    HexView     v2(doc);
1104
1105    doc.append(argc == 2 ? argv[1] : "Hello world!");
1106    return 0;
1107}</programlisting>
1108
1109  <para>The complete example source, contributed by Keith MacDonald,
1110    is available in <ulink
1111    url="../../libs/signals/example/doc_view.cpp"><code>libs/signals/example/doc_view.cpp</code></ulink>.</para>
1112</section>
1113
1114<section>
1115  <title>Linking against the Signals library</title>
1116
1117  <para>Part of the Boost.Signals library is compiled into a binary
1118  library that must be linked into your application to use
1119  Signals. Please refer to
1120    the <ulink url="../../more/getting_started.html">Getting Started</ulink>
1121  guide. You will need to link against the <code>boost_signals</code>
1122  library.</para>
1123</section>
1124
1125</section>
Note: See TracBrowser for help on using the repository browser.