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$" id="signals.tutorial">
5 <title>Tutorial</title>
7 <using-namespace name="boost"/>
8 <using-namespace name="boost::signals"/>
9 <using-class name="boost::signalN"/>
12 <title>How to Read this Tutorial</title>
13 <para>This tutorial is not meant to be read linearly. Its top-level
14 structure roughly separates different concepts in the library
15 (e.g., handling calling multiple slots, passing values to and from
16 slots) and in each of these concepts the basic ideas are presented
17 first and then more complex uses of the library are described
18 later. Each of the sections is marked <emphasis>Beginner</emphasis>,
19 <emphasis>Intermediate</emphasis>, or <emphasis>Advanced</emphasis> to help guide the
20 reader. The <emphasis>Beginner</emphasis> sections include information that all
21 library users should know; one can make good use of the Signals
22 library after having read only the <emphasis>Beginner</emphasis> sections. The
23 <emphasis>Intermediate</emphasis> sections build on the <emphasis>Beginner</emphasis>
24 sections with slightly more complex uses of the library. Finally,
25 the <emphasis>Advanced</emphasis> sections detail very advanced uses of the
26 Signals library, that often require a solid working knowledge of
27 the <emphasis>Beginner</emphasis> and <emphasis>Intermediate</emphasis> topics; most users
28 will not need to read the <emphasis>Advanced</emphasis> sections.</para>
31 <section><title>Compatibility Note</title>
33 <para>Boost.Signals has two syntactical forms: the preferred form and
34 the compatibility form. The preferred form fits more closely with the
35 C++ language and reduces the number of separate template parameters
36 that need to be considered, often improving readability; however, the
37 preferred form is not supported on all platforms due to compiler
38 bugs. The compatible form will work on all compilers supported by
39 Boost.Signals. Consult the table below to determine which syntactic
40 form to use for your compiler. Users of Boost.Function, please note
41 that the preferred syntactic form in Signals is equivalent to that of
42 Function's preferred syntactic form.</para>
44 <para>If your compiler does not appear in this list, please try the
45 preferred syntax and report your results to the Boost list so that
46 we can keep this table up-to-date.</para>
49 <tgroup cols="2" align="left">
52 <entry>Preferred syntax</entry>
53 <entry>Portable syntax</entry>
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>
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>
83 <section><title>Hello, World! (Beginner)</title>
84 <para>The following example writes "Hello, World!" using signals and
85 slots. First, we create a signal <code>sig</code>, a signal that
86 takes no arguments and has a void return value. Next, we connect
87 the <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
90 invokes <code>HelloWorld::operator()</code> to print "Hello,
93 <tgroup cols="2" align="left">
96 <entry>Preferred syntax</entry>
97 <entry>Portable syntax</entry>
106 void operator()() const
108 std::cout << "Hello, World!" << std::endl;
114 // Signal with no arguments and a void return value
115 <classname>boost::signal</classname><void ()> sig;
117 // Connect a HelloWorld slot
119 sig.<methodname>connect</methodname>(hello);
121 // Call all of the slots
129 void operator()() const
131 std::cout << "Hello, World!" << std::endl;
137 // Signal with no arguments and a void return value
138 <classname alt="boost::signalN">boost::signal0</classname><void> sig;
140 // Connect a HelloWorld slot
142 sig.<methodname>connect</methodname>(hello);
144 // Call all of the slots
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
157 we can make the Hello, World program more interesting by splitting
158 the work of printing "Hello, World!" into two completely separate
159 slots. The first slot will print "Hello" and may look like
164 void operator()() const
166 std::cout << "Hello";
170 <para>The second slot will print ", World!" and a newline, to complete
171 the program. The second slot may look like this:</para>
175 void operator()() const
177 std::cout << ", World!" << std::endl;
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
185 signal, and when we call the signal both slots will be called.</para>
187 <tgroup cols="2" align="left">
190 <entry>Preferred syntax</entry>
191 <entry>Portable syntax</entry>
198 <classname>boost::signal</classname><void ()> sig;
200 sig.<methodname>connect</methodname>(Hello());
201 sig.<methodname>connect</methodname>(World());
208 <classname alt="boost::signalN">boost::signal0</classname><void> sig;
210 sig.<methodname>connect</methodname>(Hello());
211 sig.<methodname>connect</methodname>(World());
220 <para>By default, slots are called in first-in first-out (FIFO) order,
221 so the output of this program will be as expected:</para>
227 <section><title>Ordering slot call groups (Intermediate)</title>
228 <para>Slots are free to have side effects, and that can mean that some
229 slots will have to be called before others even if they are not connected in that order. The Boost.Signals
230 library allows slots to be placed into groups that are ordered in
231 some way. For our Hello, World program, we want "Hello" to be
232 printed before ", World!", so we put "Hello" into a group that must
233 be executed before the group that ", World!" is in. To do this, we
234 can supply an extra parameter at the beginning of the
235 <code>connect</code> call that specifies the group. Group values
236 are, by default, <code>int</code>s, and are ordered by the integer
237 < relation. Here's how we construct Hello, World:</para>
239 <tgroup cols="2" align="left">
242 <entry>Preferred syntax</entry>
243 <entry>Portable syntax</entry>
250 <classname>boost::signal</classname><void ()> sig;
251 sig.<methodname>connect</methodname>(1, World());
252 sig.<methodname>connect</methodname>(0, Hello());
258 <classname alt="boost::signalN">boost::signal0</classname><void> sig;
259 sig.<methodname>connect</methodname>(1, World());
260 sig.<methodname>connect</methodname>(0, Hello());
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
271 the <code>World</code> object resides. The group
272 parameter is, in fact, optional. We omitted it in the first Hello,
273 World example because it was unnecessary when all of the slots are
274 independent. So what happens if we mix calls to connect that use the
275 group parameter and those that don't? The "unnamed" slots (i.e., those
276 that have been connected without specifying a group name) can be
277 placed 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>
279 as the last parameter to <code><methodname
280 alt="boost::signalN::connect">connect</methodname></code>, respectively), and defaults to the end of the list. When
281 a group is specified, the final parameter describes where the slot
282 will be placed within the group ordering. If we add a new slot
283 to our example like this:</para>
287 void operator()() const
289 std::cout << "... and good morning!" << std::endl;
293 sig.<methodname>connect</methodname>(GoodMorning());
295 <para>... we will get the result we wanted:</para>
298 ... and good morning!
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.
306 For instance, a signal that propagates mouse motion events might
307 want to pass along the new mouse coordinates and whether the mouse
308 buttons 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
311 slots that print the results of various arithmetic operations on
314 void print_sum(float x, float y)
316 std::cout << "The sum is " << x+y << std::endl;
319 void print_product(float x, float y)
321 std::cout << "The product is " << x*y << std::endl;
324 void print_difference(float x, float y)
326 std::cout << "The difference is " << x-y << std::endl;
329 void print_quotient(float x, float y)
331 std::cout << "The quotient is " << x/y << std::endl;
336 <tgroup cols="2" align="left">
339 <entry>Preferred syntax</entry>
340 <entry>Portable syntax</entry>
347 <classname>boost::signal</classname><void (float, float)> sig;
349 sig.<methodname>connect</methodname>(&print_sum);
350 sig.<methodname>connect</methodname>(&print_product);
351 sig.<methodname>connect</methodname>(&print_difference);
352 sig.<methodname>connect</methodname>(&print_quotient);
359 <classname alt="boost::signalN">boost::signal2</classname><void, float, float> sig;
361 sig.<methodname>connect</methodname>(&print_sum);
362 sig.<methodname>connect</methodname>(&print_product);
363 sig.<methodname>connect</methodname>(&print_difference);
364 sig.<methodname>connect</methodname>(&print_quotient);
374 <para>This program will print out the following:</para>
379 The quotient is 1.66667
381 <para>So any values that are given to <code>sig</code> when it is
382 called like a function are passed to each of the slots. We have to
383 declare the types of these values up front when we create the
384 signal. The type <code><classname>boost::signal</classname><void (float,
385 float)></code> means that the signal has a <code>void</code>
386 return value and takes two <code>float</code> values. Any slot
387 connected to <code>sig</code> must therefore be able to take two
388 <code>float</code> values.</para>
391 <section><title>Signal Return Values (Advanced)</title>
392 <para>Just as slots can receive arguments, they can also return
393 values. These values can then be returned back to the caller of the
394 signal through a <firstterm>combiner</firstterm>. The combiner is a mechanism
395 that can take the results of calling slots (there many be no
396 results or a hundred; we don't know until the program runs) and
397 coalesces them into a single result to be returned to the caller.
398 The single result is often a simple function of the results of the
399 slot calls: the result of the last slot call, the maximum value
400 returned by any slot, or a container of all of the results are some
401 possibilities.</para>
402 <para>We can modify our previous arithmetic operations example
403 slightly so that the slots all return the results of computing the
404 product, quotient, sum, or difference. Then the signal itself can
405 return a value based on these results to be printed:</para>
407 <tgroup cols="2" align="left">
410 <entry>Preferred syntax</entry>
411 <entry>Portable syntax</entry>
418 float product(float x, float y) { return x*y; }
419 float quotient(float x, float y) { return x/y; }
420 float sum(float x, float y) { return x+y; }
421 float difference(float x, float y) { return x-y; }
423 <classname>boost::signal</classname><float (float x, float y)> sig;
425 sig.<methodname>connect</methodname>(&product);
426 sig.<methodname>connect</methodname>(&quotient);
427 sig.<methodname>connect</methodname>(&sum);
428 sig.<methodname>connect</methodname>(&difference);
430 std::cout << sig(5, 3) << std::endl;
435 float product(float x, float y) { return x*y; }
436 float quotient(float x, float y) { return x/y; }
437 float sum(float x, float y) { return x+y; }
438 float difference(float x, float y) { return x-y; }
440 <classname alt="boost::signalN">boost::signal2</classname><float, float, float> sig;
442 sig.<methodname>connect</methodname>(&product);
443 sig.<methodname>connect</methodname>(&quotient);
444 sig.<methodname>connect</methodname>(&sum);
445 sig.<methodname>connect</methodname>(&difference);
447 std::cout << sig(5, 3) << std::endl;
455 <para>This example program will output <code>2</code>. This is because the
456 default 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
459 then return the result returned by the last slot called. This
460 behavior is admittedly silly for this example, because slots have
461 no side effects and the result is the last slot connect.</para>
462 <para>A more interesting signal result would be the maximum of the
463 values returned by any slot. To do this, we create a custom
464 combiner that looks like this:</para>
466 template<typename T>
469 typedef T result_type;
471 template<typename InputIterator>
472 T operator()(InputIterator first, InputIterator last) const
474 // If there are no slots to call, just return the
475 // default-constructed value
479 T max_value = *first++;
480 while (first != last) {
481 if (max_value < *first)
490 <para>The <code>maximum</code> class template acts as a function
491 object. Its result type is given by its template parameter, and
492 this is the type it expects to be computing the maximum based on
493 (e.g., <code>maximum<float></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
496 iterator sequence <code>[first, last)</code> that includes the
497 results of calling all of the slots. <code>maximum</code> uses this
498 input iterator sequence to calculate the maximum element, and
499 returns that maximum value.</para>
500 <para>We actually use this new function object type by installing it
501 as a combiner for our signal. The combiner template argument
502 follows the signal's calling signature:</para>
504 <tgroup cols="2" align="left">
507 <entry>Preferred syntax</entry>
508 <entry>Portable syntax</entry>
515 <classname>boost::signal</classname><float (float x, float y),
516 maximum<float> > sig;
521 <classname alt="boost::signalN">boost::signal2</classname><float, float, float,
522 maximum<float> > sig;
530 <para>Now we can connect slots that perform arithmetic functions and
531 use the signal:</para>
533 sig.<methodname>connect</methodname>(&quotient);
534 sig.<methodname>connect</methodname>(&product);
535 sig.<methodname>connect</methodname>(&sum);
536 sig.<methodname>connect</methodname>(&difference);
538 std::cout << sig(5, 3) << std::endl;
540 <para>The output of this program will be <code>15</code>, because
541 regardless of the order in which the slots are connected, the product
542 of 5 and 3 will be larger than the quotient, sum, or
544 <para>In other cases we might want to return all of the values
545 computed by the slots together, in one large data structure. This
546 is easily done with a different combiner:</para>
548 template<typename Container>
549 struct aggregate_values
551 typedef Container result_type;
553 template<typename InputIterator>
554 Container operator()(InputIterator first, InputIterator last) const
556 return Container(first, last);
561 Again, we can create a signal with this new combiner:
564 <tgroup cols="2" align="left">
567 <entry>Preferred syntax</entry>
568 <entry>Portable syntax</entry>
575 <classname>boost::signal</classname><float (float, float),
576 aggregate_values<std::vector<float> > > sig;
578 sig.<methodname>connect</methodname>(&quotient);
579 sig.<methodname>connect</methodname>(&product);
580 sig.<methodname>connect</methodname>(&sum);
581 sig.<methodname>connect</methodname>(&difference);
583 std::vector<float> results = sig(5, 3);
584 std::copy(results.begin(), results.end(),
585 std::ostream_iterator<float>(cout, " "));
590 <classname alt="boost::signalN">boost::signal2</classname><float, float, float,
591 aggregate_values<std::vector<float> > > sig;
593 sig.<methodname>connect</methodname>(&quotient);
594 sig.<methodname>connect</methodname>(&product);
595 sig.<methodname>connect</methodname>(&sum);
596 sig.<methodname>connect</methodname>(&difference);
598 std::vector<float> results = sig(5, 3);
599 std::copy(results.begin(), results.end(),
600 std::ostream_iterator<float>(cout, " "));
608 <para>The output of this program will contain 15, 8, 1.6667, and 2. It
609 is interesting here that
610 the first template argument for the <code>signal</code> class,
611 <code>float</code>, is not actually the return type of the signal.
612 Instead, it is the return type used by the connected slots and will
613 also be the <code>value_type</code> of the input iterators passed
614 to the combiner. The combiner itself is a function object and its
615 <code>result_type</code> member type becomes the return type of the
617 <para>The input iterators passed to the combiner transform dereference
618 operations into slot calls. Combiners therefore have the option to
619 invoke only some slots until some particular criterion is met. For
620 instance, in a distributed computing system, the combiner may ask
621 each remote system whether it will handle the request. Only one
622 remote system needs to handle a particular request, so after a
623 remote system accepts the work we do not want to ask any other
624 remote systems to perform the same task. Such a combiner need only
625 check the value returned when dereferencing the iterator, and
626 return when the value is acceptable. The following combiner returns
627 the first non-NULL pointer to a <code>FulfilledRequest</code> data
628 structure, without asking any later slots to fulfill the
631 struct DistributeRequest {
632 typedef FulfilledRequest* result_type;
634 template<typename InputIterator>
635 result_type operator()(InputIterator first, InputIterator last) const
637 while (first != last) {
638 if (result_type fulfilled = *first)
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
652 connected. Often slots are only used to receive a few events and
653 are then disconnected, and the programmer needs control to decide
654 when 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
658 between 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
660 still connected, and the <code><methodname alt="connection::disconnect">disconnect()</methodname></code> method
661 disconnects the signal and slot if they are connected before it is
662 called. Each call to the signal's <code>connect()</code> method
663 returns a connection object, which can be used to determine if the
664 connection still exists or to disconnect the signal and slot.</para>
666 boost::signals::connection c = sig.<methodname>connect</methodname>(HelloWorld());
667 if (c.<methodname>connected</methodname>()) {
668 <emphasis>// c is still connected to the signal</emphasis>
669 sig(); <emphasis>// Prints "Hello, World!"</emphasis>
672 c.disconnect(); <emphasis>// Disconnect the HelloWorld object</emphasis>
673 assert(!c.<methodname>connected</methodname>()); <emphasis>c isn't connected any more</emphasis>
675 sig(); <emphasis>// Does nothing: there are no connected slots</emphasis>
679 <section><title>Blocking Slots (Beginner)</title>
681 <para>Slots can be temporarily "blocked", meaning that they will be
682 ignored when the signal is invoked but have not been disconnected. The
683 <code><methodname>block</methodname></code> member function
684 temporarily blocks a slot, which can be unblocked via
685 <code><methodname>unblock</methodname></code>. Here is an example of
686 blocking/unblocking slots:</para>
689 boost::signals::connection c = sig.<methodname>connect</methodname>(HelloWorld());
690 sig(); <emphasis>// Prints "Hello, World!"</emphasis>
692 c.<methodname>block</methodname>(); <emphasis>// block the slot</emphasis>
693 assert(c.<methodname>blocked</methodname>());
694 sig(); <emphasis>// No output: the slot is blocked</emphasis>
696 c.<methodname>unblock</methodname>(); <emphasis>// unblock the slot</emphasis>
697 sig(); <emphasis>// Prints "Hello, World!"</emphasis>
702 <section><title>Scoped connections (Intermediate)</title>
703 <para>The <code>boost::signals::scoped_connection</code> class
704 references a signal/slot connection that will be disconnected when
705 the <code>scoped_connection</code> class goes out of scope. This
706 ability is useful when a connection need only be temporary,
710 boost::signals::scoped_connection c = sig.<methodname>connect</methodname>(ShortLived());
711 sig(); <emphasis>// will call ShortLived function object</emphasis>
713 sig(); <emphasis>// ShortLived function object no longer connected to sig</emphasis>
717 <section><title>Disconnecting equivalent slots (Intermediate)</title>
718 <para>One can disconnect slots that are equivalent to a given function
719 object using a form of the
720 <code><methodname>disconnect</methodname></code> method, so long as
721 the type of the function object has an accessible <code>==</code>
722 operator. For instance:
726 <tgroup cols="2" align="left">
729 <entry>Preferred syntax</entry>
730 <entry>Portable syntax</entry>
740 signal<void()> sig;
742 sig.connect(&foo);
743 sig.connect(&bar);
745 // disconnects foo, but not bar
746 sig.disconnect(&foo);
754 signal0<void> sig;
756 sig.connect(&foo);
757 sig.connect(&bar);
759 // disconnects foo, but not bar
760 sig.disconnect(&foo);
770 <section><title>Automatic connection management (Intermediate)</title>
771 <para>Boost.Signals can automatically track the lifetime of objects
772 involved in signal/slot connections, including automatic
773 disconnection of slots when objects involved in the slot call are
774 destroyed. For instance, consider a simple news delivery service,
775 where clients connect to a news provider that then sends news to
776 all connected clients as information arrives. The news delivery
777 service may be constructed like this: </para>
779 <tgroup cols="2" align="left">
782 <entry>Preferred syntax</entry>
783 <entry>Portable syntax</entry>
790 class NewsItem { /* ... */ };
792 boost::signal<void (const NewsItem&)> deliverNews;
797 class NewsItem { /* ... */ };
799 boost::signal1<void, const NewsItem&> deliverNews;
807 <para>Clients that wish to receive news updates need only connect a
808 function object that can receive news items to the
809 <code>deliverNews</code> signal. For instance, we may have a
810 special message area in our application specifically for news,
813 struct NewsMessageArea : public MessageArea
818 void displayNews(const NewsItem& news) const
820 messageText = news.text();
826 NewsMessageArea newsMessageArea = new NewsMessageArea(/* ... */);
828 deliverNews.<methodname>connect</methodname>(boost::bind(&NewsMessageArea::displayNews,
829 newsMessageArea, _1));
831 <para>However, what if the user closes the news message area,
832 destroying the <code>newsMessageArea</code> object that
833 <code>deliverNews</code> knows about? Most likely, a segmentation
834 fault will occur. However, with Boost.Signals one need only make
835 <code>NewsMessageArea</code> <emphasis>trackable</emphasis>, and the slot
836 involving <code>newsMessageArea</code> will be disconnected when
837 <code>newsMessageArea</code> is destroyed. The
838 <code>NewsMessageArea</code> class is made trackable by deriving
839 publicly from the <code>boost::signals::trackable</code> class,
842 struct NewsMessageArea : public MessageArea, public boost::signals::trackable
847 <para>At this time there is a significant limitation to the use of
848 <code>trackable</code> objects in making slot connections: function
849 objects built using Boost.Bind are understood, such that pointers
850 or 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
853 objects from other libraries (e.g., Boost.Function or Boost.Lambda)
854 do not implement the required interfaces for <code>trackable</code>
855 object detection, and <emphasis>will silently ignore any bound trackable
856 objects</emphasis>. Future versions of the Boost libraries will address
857 this limitation.</para>
860 <section><title>When can disconnections occur? (Intermediate)</title>
861 <para>Signal/slot disconnections occur when any of these conditions
864 <listitem><para>The connection is explicitly disconnected via the connection's
865 <code>disconnect</code> method directly, or indirectly via the
866 signal'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
869 destroyed.</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
872 calling sequence. If a signal/slot connection is disconnected at
873 any time during a signal's calling sequence, the calling sequence
874 will still continue but will not invoke the disconnected slot.
875 Additionally, a signal may be destroyed while it is in a calling
876 sequence, and which case it will complete its slot call sequence
877 but may not be accessed directly.</para>
878 <para>Signals may be invoked recursively (e.g., a signal A calls a
879 slot B that invokes signal A...). The disconnection behavior does
880 not change in the recursive case, except that the slot calling
881 sequence includes slot calls for all nested invocations of the
885 <section><title>Passing slots (Intermediate)</title>
886 <para>Slots in the Boost.Signals library are created from arbitrary
887 function objects, and therefore have no fixed type. However, it is
888 commonplace to require that slots be passed through interfaces that
889 cannot be templates. Slots can be passed via the
890 <code>slot_type</code> for each particular signal type and any
891 function object compatible with the signature of the signal can be
892 passed to a <code>slot_type</code> parameter. For instance:</para>
894 <tgroup cols="2" align="left">
897 <entry>Preferred syntax</entry>
898 <entry>Portable syntax</entry>
907 typedef boost::signal<void (int x, int y)> OnClick;
910 void doOnClick(const OnClick::slot_type& slot);
916 void Button::doOnClick(
917 const OnClick::slot_type& slot
920 onClick.<methodname>connect</methodname>(slot);
923 void printCoordinates(long x, long y)
925 std::cout << "(" << x << ", " << y << ")\n";
928 void f(Button& button)
930 button.doOnClick(&printCoordinates);
938 typedef <classname alt="boost::signalN">boost::signal2</classname><void,int,int> OnClick;
941 void doOnClick(const OnClick::slot_type& slot);
947 void Button::doOnClick(
948 const OnClick::slot_type& slot
951 onClick.<methodname>connect</methodname>(slot);
954 void printCoordinates(long x, long y)
956 std::cout << "(" << x << ", " << y << ")\n";
959 void f(Button& button)
961 button.doOnClick(&printCoordinates);
970 <para>The <code>doOnClick</code> method is now functionally equivalent
971 to the <code>connect</code> method of the <code>onClick</code>
972 signal, but the details of the <code>doOnClick</code> method can be
973 hidden in an implementation detail file.</para>
978 <title>Example: Document-View</title>
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
987 <programlisting>class Document
990 typedef boost::signal<void (bool)> signal_t;
991 typedef boost::signals::connection connection_t;
997 connection_t connect(signal_t::slot_function_type subscriber)
999 return m_sig.connect(subscriber);
1002 void disconnect(connection_t subscriber)
1004 subscriber.disconnect();
1007 void append(const char* s)
1013 const std::string& getText() const
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>
1033 View(Document& m)
1036 m_connection = m_document.connect(boost::bind(&View::refresh, this, _1));
1041 m_document.disconnect(m_connection);
1044 virtual void refresh(bool bExtended) const = 0;
1047 Document& m_document;
1050 Document::connection_t m_connection;
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>
1058 <programlisting>class TextView : public View
1061 TextView(Document& doc)
1065 virtual void refresh(bool bExtended) const
1067 std::cout << "TextView: " << m_document.getText() << std::endl;
1071 <para>Alternatively, we can provide a view of the document
1072 translated into hex values using the <code>HexView</code>
1075 <programlisting>class HexView : public View
1078 HexView(Document& doc)
1082 virtual void refresh(bool bExtended) const
1084 const std::string& s = m_document.getText();
1086 std::cout << "HexView:";
1088 for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
1089 std::cout << ' ' << std::hex << static_cast<int>(*it);
1091 std::cout << std::endl;
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>
1099 <programlisting>int main(int argc, char* argv[])
1105 doc.append(argc == 2 ? argv[1] : "Hello world!");
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>
1115 <title>Linking against the Signals library</title>
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>