123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
- "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
- <!--
- Copyright Douglas Gregor 2001-2004
- Copyright Frank Mori Hess 2007-2009
- Distributed under the Boost Software License, Version 1.0. (See accompanying
- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- -->
- <section last-revision="$Date: 2007-06-12 14:01:23 -0400 (Tue, 12 Jun 2007) $" id="signals2.tutorial">
- <title>Tutorial</title>
- <using-namespace name="boost::signals2"/>
- <using-namespace name="boost"/>
- <using-class name="boost::signals2::signal"/>
- <using-class name="boost::signals2::slot"/>
- <section>
- <title>How to Read this Tutorial</title>
- <para>This tutorial is not meant to be read linearly. Its top-level
- structure roughly separates different concepts in the library
- (e.g., handling calling multiple slots, passing values to and from
- slots) and in each of these concepts the basic ideas are presented
- first and then more complex uses of the library are described
- later. Each of the sections is marked <emphasis>Beginner</emphasis>,
- <emphasis>Intermediate</emphasis>, or <emphasis>Advanced</emphasis> to help guide the
- reader. The <emphasis>Beginner</emphasis> sections include information that all
- library users should know; one can make good use of the Signals2
- library after having read only the <emphasis>Beginner</emphasis> sections. The
- <emphasis>Intermediate</emphasis> sections build on the <emphasis>Beginner</emphasis>
- sections with slightly more complex uses of the library. Finally,
- the <emphasis>Advanced</emphasis> sections detail very advanced uses of the
- Signals2 library, that often require a solid working knowledge of
- the <emphasis>Beginner</emphasis> and <emphasis>Intermediate</emphasis> topics; most users
- will not need to read the <emphasis>Advanced</emphasis> sections.</para>
- </section>
- <section><title>Hello, World! (Beginner)</title>
- <para>The following example writes "Hello, World!" using signals and
- slots. First, we create a signal <code>sig</code>, a signal that
- takes no arguments and has a void return value. Next, we connect
- the <code>hello</code> function object to the signal using the
- <code>connect</code> method. Finally, use the signal
- <code>sig</code> like a function to call the slots, which in turns
- invokes <code>HelloWorld::operator()</code> to print "Hello,
- World!".</para>
- <programlisting><xi:include href="hello_world_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <programlisting><xi:include href="hello_world_single_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- </section>
- <section><title>Calling Multiple Slots</title>
- <section><title>Connecting Multiple Slots (Beginner)</title>
- <para>Calling a single slot from a signal isn't very interesting, so
- we can make the Hello, World program more interesting by splitting
- the work of printing "Hello, World!" into two completely separate
- slots. The first slot will print "Hello" and may look like
- this:</para>
- <programlisting><xi:include href="hello_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>The second slot will print ", World!" and a newline, to complete
- the program. The second slot may look like this:</para>
- <programlisting><xi:include href="world_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>Like in our previous example, we can create a signal
- <code>sig</code> that takes no arguments and has a
- <code>void</code> return value. This time, we connect both a
- <code>hello</code> and a <code>world</code> slot to the same
- signal, and when we call the signal both slots will be called.</para>
- <programlisting><xi:include href="hello_world_multi_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>By default, slots are pushed onto the back of the slot list,
- so the output of this program will be as expected:</para>
- <programlisting>
- Hello, World!
- </programlisting>
- </section>
- <section><title>Ordering Slot Call Groups (Intermediate)</title>
- <para>Slots are free to have side effects, and that can mean that some
- slots will have to be called before others even if they are not connected in that order. The Boost.Signals2
- library allows slots to be placed into groups that are ordered in
- some way. For our Hello, World program, we want "Hello" to be
- printed before ", World!", so we put "Hello" into a group that must
- be executed before the group that ", World!" is in. To do this, we
- can supply an extra parameter at the beginning of the
- <code>connect</code> call that specifies the group. Group values
- are, by default, <code>int</code>s, and are ordered by the integer
- < relation. Here's how we construct Hello, World:</para>
- <programlisting><xi:include href="hello_world_ordered_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>Invoking the signal will correctly print "Hello, World!", because the
- <code>Hello</code> object is in group 0, which precedes group 1 where
- the <code>World</code> object resides. The group
- parameter is, in fact, optional. We omitted it in the first Hello,
- World example because it was unnecessary when all of the slots are
- independent. So what happens if we mix calls to connect that use the
- group parameter and those that don't? The "unnamed" slots (i.e., those
- that have been connected without specifying a group name) can be
- placed at the front or back of the slot list (by passing
- <code>boost::signals2::at_front</code> or <code>boost::signals2::at_back</code>
- as the last parameter to <code><methodname
- alt="boost::signals2::signal::connect">connect</methodname></code>, respectively),
- and default to the end of the list. When
- a group is specified, the final <code>at_front</code> or <code>at_back</code>
- parameter describes where the slot
- will be placed within the group ordering. Ungrouped slots connected with
- <code>at_front</code> will always precede all grouped slots. Ungrouped
- slots connected with <code>at_back</code> will always succeed all
- grouped slots.
- </para>
- <para>
- If we add a new slot to our example like this:
- </para>
- <programlisting><xi:include href="good_morning_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <programlisting><xi:include href="hello_world_ordered_invoke_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>... we will get the result we wanted:</para>
- <programlisting>
- Hello, World!
- ... and good morning!
- </programlisting>
- </section>
- </section>
- <section><title>Passing Values to and from Slots</title>
- <section><title>Slot Arguments (Beginner)</title>
- <para>Signals can propagate arguments to each of the slots they call.
- For instance, a signal that propagates mouse motion events might
- want to pass along the new mouse coordinates and whether the mouse
- buttons are pressed.</para>
- <para>As an example, we'll create a signal that passes two
- <code>float</code> arguments to its slots. Then we'll create a few
- slots that print the results of various arithmetic operations on
- these values.</para>
- <programlisting><xi:include href="slot_arguments_slot_defs_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <programlisting><xi:include href="slot_arguments_main_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>This program will print out the following:</para>
- <programlisting>The arguments are 5 and 3
- The sum is 8
- The product is 15
- The difference is 2
- The quotient is 1.66667</programlisting>
- <para>So any values that are given to <code>sig</code> when it is
- called like a function are passed to each of the slots. We have to
- declare the types of these values up front when we create the
- signal. The type <code><classname>boost::signals2::signal</classname><void (float,
- float)></code> means that the signal has a <code>void</code>
- return value and takes two <code>float</code> values. Any slot
- connected to <code>sig</code> must therefore be able to take two
- <code>float</code> values.</para>
- </section>
- <section><title>Signal Return Values (Advanced)</title>
- <para>Just as slots can receive arguments, they can also return
- values. These values can then be returned back to the caller of the
- signal through a <firstterm>combiner</firstterm>. The combiner is a mechanism
- that can take the results of calling slots (there may be no
- results or a hundred; we don't know until the program runs) and
- coalesces them into a single result to be returned to the caller.
- The single result is often a simple function of the results of the
- slot calls: the result of the last slot call, the maximum value
- returned by any slot, or a container of all of the results are some
- possibilities.</para>
- <para>We can modify our previous arithmetic operations example
- slightly so that the slots all return the results of computing the
- product, quotient, sum, or difference. Then the signal itself can
- return a value based on these results to be printed:</para>
- <programlisting><xi:include href="signal_return_value_slot_defs_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <programlisting>boost::signals2::signal<float (float, float)> sig;</programlisting>
- <programlisting><xi:include href="signal_return_value_main_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>This example program will output <code>2</code>. This is because the
- default behavior of a signal that has a return type
- (<code>float</code>, the first template argument given to the
- <code><classname>boost::signals2::signal</classname></code> class template) is to call all slots and
- then return a <classname>boost::optional</classname> containing
- the result returned by the last slot called. This
- behavior is admittedly silly for this example, because slots have
- no side effects and the result is the last slot connected.</para>
- <para>A more interesting signal result would be the maximum of the
- values returned by any slot. To do this, we create a custom
- combiner that looks like this:</para>
- <programlisting><xi:include href="custom_combiners_maximum_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>The <code>maximum</code> class template acts as a function
- object. Its result type is given by its template parameter, and
- this is the type it expects to be computing the maximum based on
- (e.g., <code>maximum<float></code> would find the maximum
- <code>float</code> in a sequence of <code>float</code>s). When a
- <code>maximum</code> object is invoked, it is given an input
- iterator sequence <code>[first, last)</code> that includes the
- results of calling all of the slots. <code>maximum</code> uses this
- input iterator sequence to calculate the maximum element, and
- returns that maximum value.</para>
- <para>We actually use this new function object type by installing it
- as a combiner for our signal. The combiner template argument
- follows the signal's calling signature:</para>
- <programlisting>
- <classname>boost::signals2::signal</classname><float (float x, float y),
- maximum<float> > sig;
- </programlisting>
- <para>Now we can connect slots that perform arithmetic functions and
- use the signal:</para>
- <programlisting><xi:include href="custom_combiners_maximum_usage_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>The output of this program will be <code>15</code>, because
- regardless of the order in which the slots are connected, the product
- of 5 and 3 will be larger than the quotient, sum, or
- difference.</para>
- <para>In other cases we might want to return all of the values
- computed by the slots together, in one large data structure. This
- is easily done with a different combiner:</para>
- <programlisting><xi:include href="custom_combiners_aggregate_values_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>
- Again, we can create a signal with this new combiner:
- </para>
- <programlisting>
- <classname>boost::signals2::signal</classname><float (float, float),
- aggregate_values<std::vector<float> > > sig;</programlisting>
- <programlisting><xi:include href="custom_combiners_aggregate_values_usage_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>The output of this program will contain 15, 8, 1.6667, and 2. It
- is interesting here that
- the first template argument for the <code>signal</code> class,
- <code>float</code>, is not actually the return type of the signal.
- Instead, it is the return type used by the connected slots and will
- also be the <code>value_type</code> of the input iterators passed
- to the combiner. The combiner itself is a function object and its
- <code>result_type</code> member type becomes the return type of the
- signal.</para>
- <para>The input iterators passed to the combiner transform dereference
- operations into slot calls. Combiners therefore have the option to
- invoke only some slots until some particular criterion is met. For
- instance, in a distributed computing system, the combiner may ask
- each remote system whether it will handle the request. Only one
- remote system needs to handle a particular request, so after a
- remote system accepts the work we do not want to ask any other
- remote systems to perform the same task. Such a combiner need only
- check the value returned when dereferencing the iterator, and
- return when the value is acceptable. The following combiner returns
- the first non-NULL pointer to a <code>FulfilledRequest</code> data
- structure, without asking any later slots to fulfill the
- request:</para>
- <programlisting>
- struct DistributeRequest {
- typedef FulfilledRequest* result_type;
- template<typename InputIterator>
- result_type operator()(InputIterator first, InputIterator last) const
- {
- while (first != last) {
- if (result_type fulfilled = *first)
- return fulfilled;
- ++first;
- }
- return 0;
- }
- };
- </programlisting>
- </section>
- </section>
- <section><title>Connection Management</title>
- <section><title>Disconnecting Slots (Beginner)</title>
- <para>Slots aren't expected to exist indefinitely after they are
- connected. Often slots are only used to receive a few events and
- are then disconnected, and the programmer needs control to decide
- when a slot should no longer be connected.</para>
- <para>The entry point for managing connections explicitly is the
- <code><classname>boost::signals2::connection</classname></code> class. The
- <code>connection</code> class uniquely represents the connection
- between a particular signal and a particular slot. The
- <code><methodname alt="connection::connected">connected</methodname>()</code> method checks if the signal and slot are
- still connected, and the <code><methodname alt="connection::disconnect">disconnect()</methodname></code> method
- disconnects the signal and slot if they are connected before it is
- called. Each call to the signal's <code>connect()</code> method
- returns a connection object, which can be used to determine if the
- connection still exists or to disconnect the signal and slot.</para>
- <programlisting><xi:include href="disconnect_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- </section>
- <section><title>Blocking Slots (Beginner)</title>
- <para>Slots can be temporarily "blocked", meaning that they will be
- ignored when the signal is invoked but have not been permanently disconnected.
- This is typically used to prevent infinite recursion in cases where
- otherwise running a slot would cause the signal it is connected to to be
- invoked again. A
- <classname>boost::signals2::shared_connection_block</classname> object will
- temporarily block a slot. The connection is unblocked by either
- destroying or calling
- <methodname alt="shared_connection_block::unblock">unblock</methodname>
- on all the
- <code>shared_connection_block</code> objects that reference the connection.
- Here is an example of
- blocking/unblocking slots:</para>
- <programlisting><xi:include href="block_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- </section>
- <section><title>Scoped Connections (Intermediate)</title>
- <para>The <classname>boost::signals2::scoped_connection</classname> class
- references a signal/slot connection that will be disconnected when
- the <code>scoped_connection</code> class goes out of scope. This
- ability is useful when a connection need only be temporary,
- e.g.,</para>
- <programlisting><xi:include href="scoped_connection_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>
- Note, attempts to initialize a scoped_connection with the assignment syntax
- will fail due to it being noncopyable. Either the explicit initialization syntax
- or default construction followed by assignment from a <classname>signals2::connection</classname>
- will work:
- </para>
- <programlisting>
- // doesn't compile due to compiler attempting to copy a temporary scoped_connection object
- // boost::signals2::scoped_connection c0 = sig.<methodname>connect</methodname>(ShortLived());
- // okay
- boost::signals2::scoped_connection c1(sig.<methodname>connect</methodname>(ShortLived()));
- // also okay
- boost::signals2::scoped_connection c2;
- c2 = sig.<methodname>connect</methodname>(ShortLived());
- </programlisting>
- </section>
- <section><title>Disconnecting Equivalent Slots (Intermediate)</title>
- <para>One can disconnect slots that are equivalent to a given function
- object using a form of the
- <code><methodname>signal::disconnect</methodname></code> method, so long as
- the type of the function object has an accessible <code>==</code>
- operator. For instance:
- </para>
- <programlisting><xi:include href="disconnect_by_slot_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <programlisting><classname>boost::signals2::signal</classname><void ()> sig;</programlisting>
- </section>
- <programlisting><xi:include href="disconnect_by_slot_usage_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <section id="signals2.tutorial.connection-management"><title>Automatic Connection Management (Intermediate)</title>
- <para>Boost.Signals2 can automatically track the lifetime of objects
- involved in signal/slot connections, including automatic
- disconnection of slots when objects involved in the slot call are
- destroyed. For instance, consider a simple news delivery service,
- where clients connect to a news provider that then sends news to
- all connected clients as information arrives. The news delivery
- service may be constructed like this: </para>
- <programlisting>
- class NewsItem { /* ... */ };
- typedef boost::signals2::signal<void (const NewsItem&)> signal_type;
- signal_type deliverNews;
- </programlisting>
- <para>Clients that wish to receive news updates need only connect a
- function object that can receive news items to the
- <code>deliverNews</code> signal. For instance, we may have a
- special message area in our application specifically for news,
- e.g.,:</para>
- <programlisting>
- struct NewsMessageArea : public MessageArea
- {
- public:
- // ...
- void displayNews(const NewsItem& news) const
- {
- messageText = news.text();
- update();
- }
- };
- // ...
- NewsMessageArea *newsMessageArea = new NewsMessageArea(/* ... */);
- // ...
- deliverNews.<methodname>connect</methodname>(boost::bind(&NewsMessageArea::displayNews,
- newsMessageArea, _1));
- </programlisting>
- <para>However, what if the user closes the news message area,
- destroying the <code>newsMessageArea</code> object that
- <code>deliverNews</code> knows about? Most likely, a segmentation
- fault will occur. However, with Boost.Signals2 one may track any object
- which is managed by a shared_ptr, by using
- <methodname alt="boost::signals2::slot::track">slot::track</methodname>. A slot will automatically
- disconnect when any of its tracked objects expire. In
- addition, Boost.Signals2 will ensure that no tracked object expires
- while the slot it is associated with is in mid-execution. It does so by creating
- temporary shared_ptr copies of the slot's tracked objects before executing it.
- To track <code>NewsMessageArea</code>, we use a shared_ptr to manage
- its lifetime, and pass the shared_ptr to the slot via its
- <methodname alt="boost::signals2::slot::track">slot::track</methodname>
- method before connecting it,
- e.g.:</para>
- <programlisting>
- // ...
- boost::shared_ptr<NewsMessageArea> newsMessageArea(new NewsMessageArea(/* ... */));
- // ...
- deliverNews.<methodname>connect</methodname>(signal_type::slot_type(&NewsMessageArea::displayNews,
- newsMessageArea.get(), _1).track(newsMessageArea));
- </programlisting>
- <para>
- Note there is no explicit call to bind() needed in the above example. If the
- <classname>signals2::slot</classname> constructor is passed more than one
- argument, it will automatically pass all the arguments to <code>bind</code> and use the
- returned function object.
- </para>
- <para>Also note, we pass an ordinary pointer as the
- second argument to the slot constructor, using <code>newsMessageArea.get()</code>
- instead of passing the <code>shared_ptr</code> itself. If we had passed the
- <code>newsMessageArea</code> itself, a copy of the <code>shared_ptr</code> would
- have been bound into the slot function, preventing the <code>shared_ptr</code>
- from expiring. However, the use of
- <methodname alt="boost::signals2::slot::track">slot::track</methodname>
- implies we wish to allow the tracked object to expire, and automatically
- disconnect the connection when this occurs.
- </para>
- <para>
- <code>shared_ptr</code> classes other than <classname>boost::shared_ptr</classname>
- (such as <code>std::shared_ptr</code>) may also be tracked for connection management
- purposes. They are supported by the <methodname>slot::track_foreign</methodname> method.
- </para>
- </section>
- <section id="signals2.tutorial.deconstruct">
- <title>Postconstructors and Predestructors (Advanced)</title>
- <para>One limitation of using <code>shared_ptr</code> for tracking is that
- an object cannot setup tracking of itself in its constructor. However, it is
- possible to set up tracking in a post-constructor which is called after the
- object has been created and passed to a <classname>shared_ptr</classname>.
- The Boost.Signals2
- library provides support for post-constructors and pre-destructors
- via the <functionname>deconstruct()</functionname> factory function.
- </para>
- <para>
- For most cases, the simplest and most robust way to setup postconstructors
- for a class is to define an associated <code>adl_postconstruct</code> function
- which can be found by <functionname>deconstruct()</functionname>,
- make the class' constructors private, and give <functionname>deconstruct</functionname>
- access to the private constructors by declaring <classname>deconstruct_access</classname>
- a friend. This will ensure that objects of the class may only be created
- through the <functionname>deconstruct()</functionname> function, and their
- associated <code>adl_postconstruct()</code> function will always be called.
- </para>
- <para>The <link linkend="signals2.examples.deconstruct">examples</link> section
- contains several examples of defining classes with postconstructors and
- predestructors, and creating objects of these classes using
- <functionname>deconstruct()</functionname>
- </para>
- <para>
- Be aware that the postconstructor/predestructor support in Boost.Signals2
- is in no way essential to the use of the library. The use of
- <functionname>deconstruct</functionname>
- is purely optional. One alternative is to
- define static factory functions for your classes. The
- factory function can create an object, pass ownership of the object to
- a <classname>shared_ptr</classname>, setup tracking for the object,
- then return the <classname>shared_ptr</classname>.
- </para>
- </section>
- <section><title>When Can Disconnections Occur? (Intermediate)</title>
- <para>Signal/slot disconnections occur when any of these conditions
- occur:</para>
- <itemizedlist>
- <listitem><para>The connection is explicitly disconnected via the connection's
- <code>disconnect</code> method directly, or indirectly via the
- signal's <code>disconnect</code> method, or
- <code>scoped_connection</code>'s destructor.</para></listitem>
- <listitem><para>An object tracked by the slot is
- destroyed.</para></listitem>
- <listitem><para>The signal is destroyed.</para></listitem></itemizedlist>
- <para>These events can occur at any time without disrupting a signal's
- calling sequence. If a signal/slot connection is disconnected at
- any time during a signal's calling sequence, the calling sequence
- will still continue but will not invoke the disconnected slot.
- Additionally, a signal may be destroyed while it is in a calling
- sequence, in which case it will complete its slot call sequence
- but may not be accessed directly.</para>
- <para>Signals may be invoked recursively (e.g., a signal A calls a
- slot B that invokes signal A...). The disconnection behavior does
- not change in the recursive case, except that the slot calling
- sequence includes slot calls for all nested invocations of the
- signal.</para>
- <para>
- Note, even after a connection is disconnected, its's associated slot
- may still be in the process of executing. In other words, disconnection
- does not block waiting for the connection's associated slot to complete execution.
- This situation may occur in a multi-threaded environment if the
- disconnection occurs concurrently with signal invocation,
- or in a single-threaded environment if a slot disconnects itself.
- </para>
- </section>
- <section><title>Passing Slots (Intermediate)</title>
- <para>Slots in the Boost.Signals2 library are created from arbitrary
- function objects, and therefore have no fixed type. However, it is
- commonplace to require that slots be passed through interfaces that
- cannot be templates. Slots can be passed via the
- <code>slot_type</code> for each particular signal type and any
- function object compatible with the signature of the signal can be
- passed to a <code>slot_type</code> parameter. For instance:</para>
- <programlisting><xi:include href="passing_slots_defs_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <programlisting>
- <xi:include href="passing_slots_usage_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>The <code>doOnClick</code> method is now functionally equivalent
- to the <code>connect</code> method of the <code>onClick</code>
- signal, but the details of the <code>doOnClick</code> method can be
- hidden in an implementation detail file.</para>
- </section>
- </section>
- <section id="signals2.tutorial.document-view">
- <title>Example: Document-View</title>
- <para>Signals can be used to implement flexible Document-View
- architectures. The document will contain a signal to which each of
- the views can connect. The following <code>Document</code> class
- defines a simple text document that supports mulitple views. Note
- that it stores a single signal to which all of the views will be
- connected.</para>
- <programlisting><xi:include href="document_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>
- Next, we can begin to define views. The
- following <code>TextView</code> class provides a simple view of the
- document text.
- </para>
- <programlisting><xi:include href="text_view_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>Alternatively, we can provide a view of the document
- translated into hex values using the <code>HexView</code>
- view:</para>
- <programlisting><xi:include href="hex_view_def_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>
- To tie the example together, here is a
- simple <code>main</code> function that sets up two views and then
- modifies the document:
- </para>
- <programlisting><xi:include href="document_view_main_code_snippet.xml"
- xmlns:xi="http://www.w3.org/2001/XInclude" parse="xml"/></programlisting>
- <para>The complete example source, contributed by Keith MacDonald,
- is available in the <link linkend="signals2.examples.document-view">examples</link> section.
- We also provide variations on the program which employ automatic connection management
- to disconnect views on their destruction.
- </para>
- </section>
- <section id="signals2.tutorial.extended-slot-type">
- <title>Giving a Slot Access to its Connection (Advanced)</title>
- <para>
- You may encounter situations where you wish to disconnect or block a slot's
- connection from within the slot itself. For example, suppose you have a group
- of asynchronous tasks, each of which emits a signal when it completes.
- You wish to connect a slot to all the tasks to retrieve their results as
- each completes. Once a
- given task completes and the slot is run, the slot no longer needs to be
- connected to the completed task.
- Therefore, you may wish to clean up old connections by having the slot
- disconnect its invoking connection when it runs.
- </para>
- <para>
- For a slot to disconnect (or block) its invoking connection, it must have
- access to a <classname>signals2::connection</classname> object which references
- the invoking signal-slot connection. The difficulty is,
- the <code>connection</code> object is returned by the
- <methodname>signal::connect</methodname>
- method, and therefore is not available until after the slot is
- already connected to the signal. This can be particularly troublesome
- in a multi-threaded environment where the signal may be invoked
- concurrently by a different thread while the slot is being connected.
- </para>
- <para>
- Therefore, the signal classes provide
- <methodname>signal::connect_extended</methodname>
- methods, which allow slots which take an extra argument to be connected to a signal.
- The extra argument is a <classname>signals2::connection</classname> object which refers
- to the signal-slot connection currently invoking the slot.
- <methodname>signal::connect_extended</methodname>
- uses slots of the type given by the
- <classname>signal::extended_slot_type</classname>
- typedef.
- </para>
- <para>
- The examples section includes an
- <link linkend="signals2.examples.tutorial.extended_slot">extended_slot</link>
- program which demonstrates the syntax for using
- <methodname>signal::connect_extended</methodname>.
- </para>
- </section>
- <section id="signals2.tutorial.signal-mutex-template-parameter">
- <title>Changing the <code>Mutex</code> Type of a Signal (Advanced).</title>
- <para>
- For most cases the default type of <classname>boost::signals2::mutex</classname> for
- a <classname>signals2::signal</classname>'s <code>Mutex</code> template type parameter should
- be fine. If you wish to use an alternate mutex type, it must be default-constructible
- and fulfill the <code>Lockable</code> concept defined by the Boost.Thread library.
- That is, it must have <code>lock()</code> and <code>unlock()</code> methods
- (the <code>Lockable</code> concept also includes a <code>try_lock()</code> method
- but this library does not require try locking).
- </para>
- <para>
- The Boost.Signals2 library provides one alternate mutex class for use with <code>signal</code>:
- <classname>boost::signals2::dummy_mutex</classname>. This is a fake mutex for
- use in single-threaded programs, where locking a real mutex would be useless
- overhead. Other mutex types you could use with <code>signal</code> include
- <classname>boost::mutex</classname>, or the <code>std::mutex</code> from
- C++11.
- </para>
- <para>
- Changing a signal's <code>Mutex</code> template type parameter can be tedious, due to
- the large number of template parameters which precede it. The
- <classname>signal_type</classname> metafunction is particularly useful in this case,
- since it enables named template type parameters for the <classname>signals2::signal</classname>
- class. For example, to declare a signal which takes an <code>int</code> as
- an argument and uses a <classname>boost::signals2::dummy_mutex</classname>
- for its <code>Mutex</code> types, you could write:
- </para>
- <programlisting>namespace bs2 = boost::signals2;
- using namespace bs2::keywords;
- bs2::signal_type<void (int), mutex_type<bs2::dummy_mutex> >::type sig;
- </programlisting>
- </section>
- <section>
- <title>Linking against the Signals2 library</title>
- <para>Unlike the original Boost.Signals library, Boost.Signals2 is currently header-only.
- </para>
- </section>
- </section>
|