661 lines
33 KiB
XML
661 lines
33 KiB
XML
<?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>
|