tutorial.xml 33 KB

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