]>
Commit | Line | Data |
---|---|---|
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 | <!-- | |
5 | Copyright Douglas Gregor 2001-2004 | |
6 | Copyright Frank Mori Hess 2007-2009 | |
7 | ||
8 | Distributed under the Boost Software License, Version 1.0. (See accompanying | |
9 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
10 | --> | |
11 | <section last-revision="$Date: 2007-06-12 14:01:23 -0400 (Tue, 12 Jun 2007) $" id="signals2.tutorial"> | |
12 | <title>Tutorial</title> | |
13 | ||
14 | <using-namespace name="boost::signals2"/> | |
15 | <using-namespace name="boost"/> | |
16 | <using-class name="boost::signals2::signal"/> | |
17 | <using-class name="boost::signals2::slot"/> | |
18 | ||
19 | <section> | |
20 | <title>How to Read this Tutorial</title> | |
21 | <para>This tutorial is not meant to be read linearly. Its top-level | |
22 | structure roughly separates different concepts in the library | |
23 | (e.g., handling calling multiple slots, passing values to and from | |
24 | slots) and in each of these concepts the basic ideas are presented | |
25 | first and then more complex uses of the library are described | |
26 | later. Each of the sections is marked <emphasis>Beginner</emphasis>, | |
27 | <emphasis>Intermediate</emphasis>, or <emphasis>Advanced</emphasis> to help guide the | |
28 | reader. The <emphasis>Beginner</emphasis> sections include information that all | |
29 | library users should know; one can make good use of the Signals2 | |
30 | library after having read only the <emphasis>Beginner</emphasis> sections. The | |
31 | <emphasis>Intermediate</emphasis> sections build on the <emphasis>Beginner</emphasis> | |
32 | sections with slightly more complex uses of the library. Finally, | |
33 | the <emphasis>Advanced</emphasis> sections detail very advanced uses of the | |
34 | Signals2 library, that often require a solid working knowledge of | |
35 | the <emphasis>Beginner</emphasis> and <emphasis>Intermediate</emphasis> topics; most users | |
36 | will not need to read the <emphasis>Advanced</emphasis> sections.</para> | |
37 | </section> | |
38 | ||
39 | <section><title>Hello, World! (Beginner)</title> | |
40 | <para>The following example writes "Hello, World!" using signals and | |
41 | slots. First, we create a signal <code>sig</code>, a signal that | |
42 | takes no arguments and has a void return value. Next, we connect | |
43 | the <code>hello</code> function object to the signal using the | |
44 | <code>connect</code> method. Finally, use the signal | |
45 | <code>sig</code> like a function to call the slots, which in turns | |
46 | invokes <code>HelloWorld::operator()</code> to print "Hello, | |
47 | World!".</para> | |
48 | <programlisting><xi:include href="hello_world_def_code_snippet.xml" | |
49 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
50 | <programlisting><xi:include href="hello_world_single_code_snippet.xml" | |
51 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
52 | </section> | |
53 | ||
54 | <section><title>Calling Multiple Slots</title> | |
55 | <section><title>Connecting Multiple Slots (Beginner)</title> | |
56 | <para>Calling a single slot from a signal isn't very interesting, so | |
57 | we can make the Hello, World program more interesting by splitting | |
58 | the work of printing "Hello, World!" into two completely separate | |
59 | slots. The first slot will print "Hello" and may look like | |
60 | this:</para> | |
61 | <programlisting><xi:include href="hello_def_code_snippet.xml" | |
62 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
63 | <para>The second slot will print ", World!" and a newline, to complete | |
64 | the program. The second slot may look like this:</para> | |
65 | <programlisting><xi:include href="world_def_code_snippet.xml" | |
66 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
67 | <para>Like in our previous example, we can create a signal | |
68 | <code>sig</code> that takes no arguments and has a | |
69 | <code>void</code> return value. This time, we connect both a | |
70 | <code>hello</code> and a <code>world</code> slot to the same | |
71 | signal, and when we call the signal both slots will be called.</para> | |
72 | <programlisting><xi:include href="hello_world_multi_code_snippet.xml" | |
73 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
74 | <para>By default, slots are pushed onto the back of the slot list, | |
75 | so the output of this program will be as expected:</para> | |
76 | <programlisting> | |
77 | Hello, World! | |
78 | </programlisting> | |
79 | </section> | |
80 | ||
81 | <section><title>Ordering Slot Call Groups (Intermediate)</title> | |
82 | <para>Slots are free to have side effects, and that can mean that some | |
83 | slots will have to be called before others even if they are not connected in that order. The Boost.Signals2 | |
84 | library allows slots to be placed into groups that are ordered in | |
85 | some way. For our Hello, World program, we want "Hello" to be | |
86 | printed before ", World!", so we put "Hello" into a group that must | |
87 | be executed before the group that ", World!" is in. To do this, we | |
88 | can supply an extra parameter at the beginning of the | |
89 | <code>connect</code> call that specifies the group. Group values | |
90 | are, by default, <code>int</code>s, and are ordered by the integer | |
91 | < relation. Here's how we construct Hello, World:</para> | |
92 | <programlisting><xi:include href="hello_world_ordered_code_snippet.xml" | |
93 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
94 | <para>Invoking the signal will correctly print "Hello, World!", because the | |
95 | <code>Hello</code> object is in group 0, which precedes group 1 where | |
96 | the <code>World</code> object resides. The group | |
97 | parameter is, in fact, optional. We omitted it in the first Hello, | |
98 | World example because it was unnecessary when all of the slots are | |
99 | independent. So what happens if we mix calls to connect that use the | |
100 | group parameter and those that don't? The "unnamed" slots (i.e., those | |
101 | that have been connected without specifying a group name) can be | |
102 | placed at the front or back of the slot list (by passing | |
103 | <code>boost::signals2::at_front</code> or <code>boost::signals2::at_back</code> | |
104 | as the last parameter to <code><methodname | |
105 | alt="boost::signals2::signal::connect">connect</methodname></code>, respectively), | |
106 | and default to the end of the list. When | |
107 | a group is specified, the final <code>at_front</code> or <code>at_back</code> | |
108 | parameter describes where the slot | |
109 | will be placed within the group ordering. Ungrouped slots connected with | |
110 | <code>at_front</code> will always precede all grouped slots. Ungrouped | |
111 | slots connected with <code>at_back</code> will always succeed all | |
112 | grouped slots. | |
113 | </para> | |
114 | <para> | |
115 | If we add a new slot to our example like this: | |
116 | </para> | |
117 | <programlisting><xi:include href="good_morning_def_code_snippet.xml" | |
118 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
119 | <programlisting><xi:include href="hello_world_ordered_invoke_code_snippet.xml" | |
120 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
121 | <para>... we will get the result we wanted:</para> | |
122 | <programlisting> | |
123 | Hello, World! | |
124 | ... and good morning! | |
125 | </programlisting> | |
126 | </section> | |
127 | </section> | |
128 | ||
129 | <section><title>Passing Values to and from Slots</title> | |
130 | <section><title>Slot Arguments (Beginner)</title> | |
131 | <para>Signals can propagate arguments to each of the slots they call. | |
132 | For instance, a signal that propagates mouse motion events might | |
133 | want to pass along the new mouse coordinates and whether the mouse | |
134 | buttons are pressed.</para> | |
135 | <para>As an example, we'll create a signal that passes two | |
136 | <code>float</code> arguments to its slots. Then we'll create a few | |
137 | slots that print the results of various arithmetic operations on | |
138 | these values.</para> | |
139 | <programlisting><xi:include href="slot_arguments_slot_defs_code_snippet.xml" | |
140 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
141 | <programlisting><xi:include href="slot_arguments_main_code_snippet.xml" | |
142 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
143 | <para>This program will print out the following:</para> | |
144 | <programlisting>The arguments are 5 and 3 | |
145 | The sum is 8 | |
146 | The product is 15 | |
147 | The difference is 2 | |
148 | The quotient is 1.66667</programlisting> | |
149 | <para>So any values that are given to <code>sig</code> when it is | |
150 | called like a function are passed to each of the slots. We have to | |
151 | declare the types of these values up front when we create the | |
152 | signal. The type <code><classname>boost::signals2::signal</classname><void (float, | |
153 | float)></code> means that the signal has a <code>void</code> | |
154 | return value and takes two <code>float</code> values. Any slot | |
155 | connected to <code>sig</code> must therefore be able to take two | |
156 | <code>float</code> values.</para> | |
157 | </section> | |
158 | ||
159 | <section><title>Signal Return Values (Advanced)</title> | |
160 | <para>Just as slots can receive arguments, they can also return | |
161 | values. These values can then be returned back to the caller of the | |
162 | signal through a <firstterm>combiner</firstterm>. The combiner is a mechanism | |
163 | that can take the results of calling slots (there may be no | |
164 | results or a hundred; we don't know until the program runs) and | |
165 | coalesces them into a single result to be returned to the caller. | |
166 | The single result is often a simple function of the results of the | |
167 | slot calls: the result of the last slot call, the maximum value | |
168 | returned by any slot, or a container of all of the results are some | |
169 | possibilities.</para> | |
170 | <para>We can modify our previous arithmetic operations example | |
171 | slightly so that the slots all return the results of computing the | |
172 | product, quotient, sum, or difference. Then the signal itself can | |
173 | return a value based on these results to be printed:</para> | |
174 | <programlisting><xi:include href="signal_return_value_slot_defs_code_snippet.xml" | |
175 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
176 | <programlisting>boost::signals2::signal<float (float, float)> sig;</programlisting> | |
177 | <programlisting><xi:include href="signal_return_value_main_code_snippet.xml" | |
178 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
179 | ||
180 | <para>This example program will output <code>2</code>. This is because the | |
181 | default behavior of a signal that has a return type | |
182 | (<code>float</code>, the first template argument given to the | |
183 | <code><classname>boost::signals2::signal</classname></code> class template) is to call all slots and | |
184 | then return a <classname>boost::optional</classname> containing | |
185 | the result returned by the last slot called. This | |
186 | behavior is admittedly silly for this example, because slots have | |
187 | no side effects and the result is the last slot connected.</para> | |
188 | <para>A more interesting signal result would be the maximum of the | |
189 | values returned by any slot. To do this, we create a custom | |
190 | combiner that looks like this:</para> | |
191 | <programlisting><xi:include href="custom_combiners_maximum_def_code_snippet.xml" | |
192 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
193 | <para>The <code>maximum</code> class template acts as a function | |
194 | object. Its result type is given by its template parameter, and | |
195 | this is the type it expects to be computing the maximum based on | |
196 | (e.g., <code>maximum<float></code> would find the maximum | |
197 | <code>float</code> in a sequence of <code>float</code>s). When a | |
198 | <code>maximum</code> object is invoked, it is given an input | |
199 | iterator sequence <code>[first, last)</code> that includes the | |
200 | results of calling all of the slots. <code>maximum</code> uses this | |
201 | input iterator sequence to calculate the maximum element, and | |
202 | returns that maximum value.</para> | |
203 | <para>We actually use this new function object type by installing it | |
204 | as a combiner for our signal. The combiner template argument | |
205 | follows the signal's calling signature:</para> | |
206 | <programlisting> | |
207 | <classname>boost::signals2::signal</classname><float (float x, float y), | |
208 | maximum<float> > sig; | |
209 | </programlisting> | |
210 | <para>Now we can connect slots that perform arithmetic functions and | |
211 | use the signal:</para> | |
212 | <programlisting><xi:include href="custom_combiners_maximum_usage_code_snippet.xml" | |
213 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
214 | <para>The output of this program will be <code>15</code>, because | |
215 | regardless of the order in which the slots are connected, the product | |
216 | of 5 and 3 will be larger than the quotient, sum, or | |
217 | difference.</para> | |
218 | <para>In other cases we might want to return all of the values | |
219 | computed by the slots together, in one large data structure. This | |
220 | is easily done with a different combiner:</para> | |
221 | <programlisting><xi:include href="custom_combiners_aggregate_values_def_code_snippet.xml" | |
222 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
223 | <para> | |
224 | Again, we can create a signal with this new combiner: | |
225 | </para> | |
226 | <programlisting> | |
227 | <classname>boost::signals2::signal</classname><float (float, float), | |
228 | aggregate_values<std::vector<float> > > sig;</programlisting> | |
229 | <programlisting><xi:include href="custom_combiners_aggregate_values_usage_code_snippet.xml" | |
230 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
231 | <para>The output of this program will contain 15, 8, 1.6667, and 2. It | |
232 | is interesting here that | |
233 | the first template argument for the <code>signal</code> class, | |
234 | <code>float</code>, is not actually the return type of the signal. | |
235 | Instead, it is the return type used by the connected slots and will | |
236 | also be the <code>value_type</code> of the input iterators passed | |
237 | to the combiner. The combiner itself is a function object and its | |
238 | <code>result_type</code> member type becomes the return type of the | |
239 | signal.</para> | |
240 | <para>The input iterators passed to the combiner transform dereference | |
241 | operations into slot calls. Combiners therefore have the option to | |
242 | invoke only some slots until some particular criterion is met. For | |
243 | instance, in a distributed computing system, the combiner may ask | |
244 | each remote system whether it will handle the request. Only one | |
245 | remote system needs to handle a particular request, so after a | |
246 | remote system accepts the work we do not want to ask any other | |
247 | remote systems to perform the same task. Such a combiner need only | |
248 | check the value returned when dereferencing the iterator, and | |
249 | return when the value is acceptable. The following combiner returns | |
250 | the first non-NULL pointer to a <code>FulfilledRequest</code> data | |
251 | structure, without asking any later slots to fulfill the | |
252 | request:</para> | |
253 | <programlisting> | |
254 | struct DistributeRequest { | |
255 | typedef FulfilledRequest* result_type; | |
256 | ||
257 | template<typename InputIterator> | |
258 | result_type operator()(InputIterator first, InputIterator last) const | |
259 | { | |
260 | while (first != last) { | |
261 | if (result_type fulfilled = *first) | |
262 | return fulfilled; | |
263 | ++first; | |
264 | } | |
265 | return 0; | |
266 | } | |
267 | }; | |
268 | </programlisting> | |
269 | </section> | |
270 | </section> | |
271 | ||
272 | <section><title>Connection Management</title> | |
273 | <section><title>Disconnecting Slots (Beginner)</title> | |
274 | <para>Slots aren't expected to exist indefinitely after they are | |
275 | connected. Often slots are only used to receive a few events and | |
276 | are then disconnected, and the programmer needs control to decide | |
277 | when a slot should no longer be connected.</para> | |
278 | <para>The entry point for managing connections explicitly is the | |
279 | <code><classname>boost::signals2::connection</classname></code> class. The | |
280 | <code>connection</code> class uniquely represents the connection | |
281 | between a particular signal and a particular slot. The | |
282 | <code><methodname alt="connection::connected">connected</methodname>()</code> method checks if the signal and slot are | |
283 | still connected, and the <code><methodname alt="connection::disconnect">disconnect()</methodname></code> method | |
284 | disconnects the signal and slot if they are connected before it is | |
285 | called. Each call to the signal's <code>connect()</code> method | |
286 | returns a connection object, which can be used to determine if the | |
287 | connection still exists or to disconnect the signal and slot.</para> | |
288 | <programlisting><xi:include href="disconnect_code_snippet.xml" | |
289 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
290 | </section> | |
291 | ||
292 | <section><title>Blocking Slots (Beginner)</title> | |
293 | ||
294 | <para>Slots can be temporarily "blocked", meaning that they will be | |
295 | ignored when the signal is invoked but have not been permanently disconnected. | |
296 | This is typically used to prevent infinite recursion in cases where | |
297 | otherwise running a slot would cause the signal it is connected to to be | |
298 | invoked again. A | |
299 | <classname>boost::signals2::shared_connection_block</classname> object will | |
300 | temporarily block a slot. The connection is unblocked by either | |
301 | destroying or calling | |
302 | <methodname alt="shared_connection_block::unblock">unblock</methodname> | |
303 | on all the | |
304 | <code>shared_connection_block</code> objects that reference the connection. | |
305 | Here is an example of | |
306 | blocking/unblocking slots:</para> | |
307 | ||
308 | <programlisting><xi:include href="block_code_snippet.xml" | |
309 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
310 | ||
311 | </section> | |
312 | ||
313 | <section><title>Scoped Connections (Intermediate)</title> | |
314 | <para>The <classname>boost::signals2::scoped_connection</classname> class | |
315 | references a signal/slot connection that will be disconnected when | |
316 | the <code>scoped_connection</code> class goes out of scope. This | |
317 | ability is useful when a connection need only be temporary, | |
318 | e.g.,</para> | |
319 | <programlisting><xi:include href="scoped_connection_code_snippet.xml" | |
320 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
321 | ||
322 | <para> | |
323 | Note, attempts to initialize a scoped_connection with the assignment syntax | |
324 | will fail due to it being noncopyable. Either the explicit initialization syntax | |
325 | or default construction followed by assignment from a <classname>signals2::connection</classname> | |
326 | will work: | |
327 | </para> | |
328 | <programlisting> | |
329 | // doesn't compile due to compiler attempting to copy a temporary scoped_connection object | |
330 | // boost::signals2::scoped_connection c0 = sig.<methodname>connect</methodname>(ShortLived()); | |
331 | ||
332 | // okay | |
333 | boost::signals2::scoped_connection c1(sig.<methodname>connect</methodname>(ShortLived())); | |
334 | ||
335 | // also okay | |
336 | boost::signals2::scoped_connection c2; | |
337 | c2 = sig.<methodname>connect</methodname>(ShortLived()); | |
338 | </programlisting> | |
339 | </section> | |
340 | ||
341 | <section><title>Disconnecting Equivalent Slots (Intermediate)</title> | |
342 | <para>One can disconnect slots that are equivalent to a given function | |
343 | object using a form of the | |
344 | <code><methodname>signal::disconnect</methodname></code> method, so long as | |
345 | the type of the function object has an accessible <code>==</code> | |
346 | operator. For instance: | |
347 | ||
348 | </para> | |
349 | <programlisting><xi:include href="disconnect_by_slot_def_code_snippet.xml" | |
350 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
351 | <programlisting><classname>boost::signals2::signal</classname><void ()> sig;</programlisting> | |
352 | </section> | |
353 | <programlisting><xi:include href="disconnect_by_slot_usage_code_snippet.xml" | |
354 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
355 | ||
356 | <section id="signals2.tutorial.connection-management"><title>Automatic Connection Management (Intermediate)</title> | |
357 | <para>Boost.Signals2 can automatically track the lifetime of objects | |
358 | involved in signal/slot connections, including automatic | |
359 | disconnection of slots when objects involved in the slot call are | |
360 | destroyed. For instance, consider a simple news delivery service, | |
361 | where clients connect to a news provider that then sends news to | |
362 | all connected clients as information arrives. The news delivery | |
363 | service may be constructed like this: </para> | |
364 | <programlisting> | |
365 | class NewsItem { /* ... */ }; | |
366 | ||
367 | typedef boost::signals2::signal<void (const NewsItem&)> signal_type; | |
368 | signal_type deliverNews; | |
369 | </programlisting> | |
370 | ||
371 | <para>Clients that wish to receive news updates need only connect a | |
372 | function object that can receive news items to the | |
373 | <code>deliverNews</code> signal. For instance, we may have a | |
374 | special message area in our application specifically for news, | |
375 | e.g.,:</para> | |
376 | <programlisting> | |
377 | struct NewsMessageArea : public MessageArea | |
378 | { | |
379 | public: | |
380 | // ... | |
381 | ||
382 | void displayNews(const NewsItem& news) const | |
383 | { | |
384 | messageText = news.text(); | |
385 | update(); | |
386 | } | |
387 | }; | |
388 | ||
389 | // ... | |
390 | NewsMessageArea *newsMessageArea = new NewsMessageArea(/* ... */); | |
391 | // ... | |
392 | deliverNews.<methodname>connect</methodname>(boost::bind(&NewsMessageArea::displayNews, | |
393 | newsMessageArea, _1)); | |
394 | </programlisting> | |
395 | <para>However, what if the user closes the news message area, | |
396 | destroying the <code>newsMessageArea</code> object that | |
397 | <code>deliverNews</code> knows about? Most likely, a segmentation | |
398 | fault will occur. However, with Boost.Signals2 one may track any object | |
399 | which is managed by a shared_ptr, by using | |
400 | <methodname alt="boost::signals2::slot::track">slot::track</methodname>. A slot will automatically | |
401 | disconnect when any of its tracked objects expire. In | |
402 | addition, Boost.Signals2 will ensure that no tracked object expires | |
403 | while the slot it is associated with is in mid-execution. It does so by creating | |
404 | temporary shared_ptr copies of the slot's tracked objects before executing it. | |
405 | To track <code>NewsMessageArea</code>, we use a shared_ptr to manage | |
406 | its lifetime, and pass the shared_ptr to the slot via its | |
407 | <methodname alt="boost::signals2::slot::track">slot::track</methodname> | |
408 | method before connecting it, | |
409 | e.g.:</para> | |
410 | <programlisting> | |
411 | // ... | |
412 | boost::shared_ptr<NewsMessageArea> newsMessageArea(new NewsMessageArea(/* ... */)); | |
413 | // ... | |
414 | deliverNews.<methodname>connect</methodname>(signal_type::slot_type(&NewsMessageArea::displayNews, | |
415 | newsMessageArea.get(), _1).track(newsMessageArea)); | |
416 | </programlisting> | |
417 | <para> | |
418 | Note there is no explicit call to bind() needed in the above example. If the | |
419 | <classname>signals2::slot</classname> constructor is passed more than one | |
420 | argument, it will automatically pass all the arguments to <code>bind</code> and use the | |
421 | returned function object. | |
422 | </para> | |
423 | <para>Also note, we pass an ordinary pointer as the | |
424 | second argument to the slot constructor, using <code>newsMessageArea.get()</code> | |
425 | instead of passing the <code>shared_ptr</code> itself. If we had passed the | |
426 | <code>newsMessageArea</code> itself, a copy of the <code>shared_ptr</code> would | |
427 | have been bound into the slot function, preventing the <code>shared_ptr</code> | |
428 | from expiring. However, the use of | |
429 | <methodname alt="boost::signals2::slot::track">slot::track</methodname> | |
430 | implies we wish to allow the tracked object to expire, and automatically | |
431 | disconnect the connection when this occurs. | |
432 | </para> | |
433 | <para> | |
434 | <code>shared_ptr</code> classes other than <classname>boost::shared_ptr</classname> | |
435 | (such as <code>std::shared_ptr</code>) may also be tracked for connection management | |
436 | purposes. They are supported by the <methodname>slot::track_foreign</methodname> method. | |
437 | </para> | |
438 | </section> | |
439 | ||
440 | <section id="signals2.tutorial.deconstruct"> | |
441 | <title>Postconstructors and Predestructors (Advanced)</title> | |
442 | <para>One limitation of using <code>shared_ptr</code> for tracking is that | |
443 | an object cannot setup tracking of itself in its constructor. However, it is | |
444 | possible to set up tracking in a post-constructor which is called after the | |
445 | object has been created and passed to a <classname>shared_ptr</classname>. | |
446 | The Boost.Signals2 | |
447 | library provides support for post-constructors and pre-destructors | |
448 | via the <functionname>deconstruct()</functionname> factory function. | |
449 | </para> | |
450 | <para> | |
451 | For most cases, the simplest and most robust way to setup postconstructors | |
452 | for a class is to define an associated <code>adl_postconstruct</code> function | |
453 | which can be found by <functionname>deconstruct()</functionname>, | |
454 | make the class' constructors private, and give <functionname>deconstruct</functionname> | |
455 | access to the private constructors by declaring <classname>deconstruct_access</classname> | |
456 | a friend. This will ensure that objects of the class may only be created | |
457 | through the <functionname>deconstruct()</functionname> function, and their | |
458 | associated <code>adl_postconstruct()</code> function will always be called. | |
459 | </para> | |
460 | <para>The <link linkend="signals2.examples.deconstruct">examples</link> section | |
461 | contains several examples of defining classes with postconstructors and | |
462 | predestructors, and creating objects of these classes using | |
463 | <functionname>deconstruct()</functionname> | |
464 | </para> | |
465 | <para> | |
466 | Be aware that the postconstructor/predestructor support in Boost.Signals2 | |
467 | is in no way essential to the use of the library. The use of | |
468 | <functionname>deconstruct</functionname> | |
469 | is purely optional. One alternative is to | |
470 | define static factory functions for your classes. The | |
471 | factory function can create an object, pass ownership of the object to | |
472 | a <classname>shared_ptr</classname>, setup tracking for the object, | |
473 | then return the <classname>shared_ptr</classname>. | |
474 | </para> | |
475 | </section> | |
476 | ||
477 | <section><title>When Can Disconnections Occur? (Intermediate)</title> | |
478 | <para>Signal/slot disconnections occur when any of these conditions | |
479 | occur:</para> | |
480 | <itemizedlist> | |
481 | <listitem><para>The connection is explicitly disconnected via the connection's | |
482 | <code>disconnect</code> method directly, or indirectly via the | |
483 | signal's <code>disconnect</code> method, or | |
484 | <code>scoped_connection</code>'s destructor.</para></listitem> | |
485 | <listitem><para>An object tracked by the slot is | |
486 | destroyed.</para></listitem> | |
487 | <listitem><para>The signal is destroyed.</para></listitem></itemizedlist> | |
488 | <para>These events can occur at any time without disrupting a signal's | |
489 | calling sequence. If a signal/slot connection is disconnected at | |
490 | any time during a signal's calling sequence, the calling sequence | |
491 | will still continue but will not invoke the disconnected slot. | |
492 | Additionally, a signal may be destroyed while it is in a calling | |
493 | sequence, and which case it will complete its slot call sequence | |
494 | but may not be accessed directly.</para> | |
495 | <para>Signals may be invoked recursively (e.g., a signal A calls a | |
496 | slot B that invokes signal A...). The disconnection behavior does | |
497 | not change in the recursive case, except that the slot calling | |
498 | sequence includes slot calls for all nested invocations of the | |
499 | signal.</para> | |
500 | <para> | |
501 | Note, even after a connection is disconnected, its's associated slot | |
502 | may still be in the process of executing. In other words, disconnection | |
503 | does not block waiting for the connection's associated slot to complete execution. | |
504 | This situation may occur in a multi-threaded environment if the | |
505 | disconnection occurs concurrently with signal invocation, | |
506 | or in a single-threaded environment if a slot disconnects itself. | |
507 | </para> | |
508 | </section> | |
509 | ||
510 | <section><title>Passing Slots (Intermediate)</title> | |
511 | <para>Slots in the Boost.Signals2 library are created from arbitrary | |
512 | function objects, and therefore have no fixed type. However, it is | |
513 | commonplace to require that slots be passed through interfaces that | |
514 | cannot be templates. Slots can be passed via the | |
515 | <code>slot_type</code> for each particular signal type and any | |
516 | function object compatible with the signature of the signal can be | |
517 | passed to a <code>slot_type</code> parameter. For instance:</para> | |
518 | <programlisting><xi:include href="passing_slots_defs_code_snippet.xml" | |
519 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
520 | <programlisting> | |
521 | <xi:include href="passing_slots_usage_code_snippet.xml" | |
522 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
523 | ||
524 | <para>The <code>doOnClick</code> method is now functionally equivalent | |
525 | to the <code>connect</code> method of the <code>onClick</code> | |
526 | signal, but the details of the <code>doOnClick</code> method can be | |
527 | hidden in an implementation detail file.</para> | |
528 | </section> | |
529 | </section> | |
530 | ||
531 | <section id="signals2.tutorial.document-view"> | |
532 | <title>Example: Document-View</title> | |
533 | ||
534 | <para>Signals can be used to implement flexible Document-View | |
535 | architectures. The document will contain a signal to which each of | |
536 | the views can connect. The following <code>Document</code> class | |
537 | defines a simple text document that supports mulitple views. Note | |
538 | that it stores a single signal to which all of the views will be | |
539 | connected.</para> | |
540 | ||
541 | <programlisting><xi:include href="document_def_code_snippet.xml" | |
542 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
543 | ||
544 | <para> | |
545 | Next, we can begin to define views. The | |
546 | following <code>TextView</code> class provides a simple view of the | |
547 | document text. | |
548 | </para> | |
549 | ||
550 | <programlisting><xi:include href="text_view_def_code_snippet.xml" | |
551 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
552 | ||
553 | <para>Alternatively, we can provide a view of the document | |
554 | translated into hex values using the <code>HexView</code> | |
555 | view:</para> | |
556 | ||
557 | <programlisting><xi:include href="hex_view_def_code_snippet.xml" | |
558 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
559 | ||
560 | <para> | |
561 | To tie the example together, here is a | |
562 | simple <code>main</code> function that sets up two views and then | |
563 | modifies the document: | |
564 | </para> | |
565 | ||
566 | <programlisting><xi:include href="document_view_main_code_snippet.xml" | |
567 | xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting> | |
568 | ||
569 | <para>The complete example source, contributed by Keith MacDonald, | |
570 | is available in the <link linkend="signals2.examples.document-view">examples</link> section. | |
571 | We also provide variations on the program which employ automatic connection management | |
572 | to disconnect views on their destruction. | |
573 | </para> | |
574 | </section> | |
575 | ||
576 | <section id="signals2.tutorial.extended-slot-type"> | |
577 | <title>Giving a Slot Access to its Connection (Advanced)</title> | |
578 | <para> | |
579 | You may encounter situations where you wish to disconnect or block a slot's | |
580 | connection from within the slot itself. For example, suppose you have a group | |
581 | of asynchronous tasks, each of which emits a signal when it completes. | |
582 | You wish to connect a slot to all the tasks to retrieve their results as | |
583 | each completes. Once a | |
584 | given task completes and the slot is run, the slot no longer needs to be | |
585 | connected to the completed task. | |
586 | Therefore, you may wish to clean up old connections by having the slot | |
587 | disconnect its invoking connection when it runs. | |
588 | </para> | |
589 | <para> | |
590 | For a slot to disconnect (or block) its invoking connection, it must have | |
591 | access to a <classname>signals2::connection</classname> object which references | |
592 | the invoking signal-slot connection. The difficulty is, | |
593 | the <code>connection</code> object is returned by the | |
594 | <methodname>signal::connect</methodname> | |
595 | method, and therefore is not available until after the slot is | |
596 | already connected to the signal. This can be particularly troublesome | |
597 | in a multi-threaded environment where the signal may be invoked | |
598 | concurrently by a different thread while the slot is being connected. | |
599 | </para> | |
600 | <para> | |
601 | Therefore, the signal classes provide | |
602 | <methodname>signal::connect_extended</methodname> | |
603 | methods, which allow slots which take an extra argument to be connected to a signal. | |
604 | The extra argument is a <classname>signals2::connection</classname> object which refers | |
605 | to the signal-slot connection currently invoking the slot. | |
606 | <methodname>signal::connect_extended</methodname> | |
607 | uses slots of the type given by the | |
608 | <classname>signal::extended_slot_type</classname> | |
609 | typedef. | |
610 | </para> | |
611 | <para> | |
612 | The examples section includes an | |
613 | <link linkend="signals2.examples.tutorial.extended_slot">extended_slot</link> | |
614 | program which demonstrates the syntax for using | |
615 | <methodname>signal::connect_extended</methodname>. | |
616 | </para> | |
617 | </section> | |
618 | ||
619 | <section id="signals2.tutorial.signal-mutex-template-parameter"> | |
620 | <title>Changing the <code>Mutex</code> Type of a Signal (Advanced).</title> | |
621 | <para> | |
622 | For most cases the default type of <classname>boost::signals2::mutex</classname> for | |
623 | a <classname>signals2::signal</classname>'s <code>Mutex</code> template type parameter should | |
624 | be fine. If you wish to use an alternate mutex type, it must be default-constructible | |
625 | and fulfill the <code>Lockable</code> concept defined by the Boost.Thread library. | |
626 | That is, it must have <code>lock()</code> and <code>unlock()</code> methods | |
627 | (the <code>Lockable</code> concept also includes a <code>try_lock()</code> method | |
628 | but this library does not require try locking). | |
629 | </para> | |
630 | <para> | |
631 | The Boost.Signals2 library provides one alternate mutex class for use with <code>signal</code>: | |
632 | <classname>boost::signals2::dummy_mutex</classname>. This is a fake mutex for | |
633 | use in single-threaded programs, where locking a real mutex would be useless | |
634 | overhead. Other mutex types you could use with <code>signal</code> include | |
635 | <classname>boost::mutex</classname>, or the <code>std::mutex</code> from | |
636 | C++11. | |
637 | </para> | |
638 | <para> | |
639 | Changing a signal's <code>Mutex</code> template type parameter can be tedious, due to | |
640 | the large number of template parameters which precede it. The | |
641 | <classname>signal_type</classname> metafunction is particularly useful in this case, | |
642 | since it enables named template type parameters for the <classname>signals2::signal</classname> | |
643 | class. For example, to declare a signal which takes an <code>int</code> as | |
644 | an argument and uses a <classname>boost::signals2::dummy_mutex</classname> | |
645 | for its <code>Mutex</code> types, you could write: | |
646 | </para> | |
647 | <programlisting>namespace bs2 = boost::signals2; | |
648 | using namespace bs2::keywords; | |
649 | bs2::signal_type<void (int), mutex_type<bs2::dummy_mutex> >::type sig; | |
650 | </programlisting> | |
651 | ||
652 | </section> | |
653 | ||
654 | <section> | |
655 | <title>Linking against the Signals2 library</title> | |
656 | <para>Unlike the original Boost.Signals library, Boost.Signals2 is currently header-only. | |
657 | </para> | |
658 | </section> | |
659 | ||
660 | </section> |