blob: fcc1b961c4f939851db31e8242f2a4b5c59bc6f1 [file] [log] [blame]
<?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">
<section last-revision="$Date: 2007-11-25 13:38:02 -0500 (Sun, 25 Nov 2007) $">
<title>Design Overview</title>
<using-namespace name="boost"/>
<using-namespace name="boost::signals"/>
<section>
<title>Type Erasure</title>
<para>"Type erasure", where static type information is eliminated
by the use of dynamically dispatched interfaces, is used
extensively within the Boost.Signals library to reduce the amount
of code generated by template instantiation. Each signal must
manage a list of slots and their associated connections, along
with a <code>std::map</code> to map from group identifiers to
their associated connections. However, instantiating this map for
every token type, and perhaps within each translation unit (for
some popular template instantiation strategies) increase compile
time overhead and space overhead.</para>
<para> To combat this so-called "template bloat", we use
Boost.Function and Boost.Any to store unknown types and
operations. Then, all of the code for handling the list of slots
and the mapping from slot identifiers to connections is factored
into the class <code><classname>signal_base</classname></code>
that deals exclusively with the <code>any</code> and
<code><classname>function</classname></code> objects, hiding the
actual implementations using the well-known pimpl idiom. The
actual <code><classname>signalN</classname></code> class templates
deal only with code that will change depending on the number of
arguments or which is inherently template-dependent (such as
connection).</para>
</section>
<section>
<title><code>connection</code> class</title>
<para> The <code><classname>connection</classname></code> class is
central to the behavior of the Boost.Signals library. It is the
only entity within the Boost.Signals system that has knowledge of
all objects that are associated by a given connection. To be
specific, the <code><classname>connection</classname></code> class
itself is merely a thin wrapper over a
<code><classname>shared_ptr</classname></code> to a
<code>basic_connection</code> object.</para>
<para> <code><classname>connection</classname></code> objects are
stored by all participants in the Signals system: each
<code><classname>trackable</classname></code> object contains a
list of <code><classname>connection</classname></code> objects
describing all connections it is a part of; similarly, all signals
contain a set of pairs that define a slot. The pairs consist of a
slot function object (generally a Boost.Function object) and a
<code><classname>connection</classname></code> object (that will
disconnect on destruction). Finally, the mapping from slot groups
to slots is based on the key value in a
<code><classname>std::multimap</classname></code> (the stored data
in the <code><classname>std::multimap</classname></code> is the
slot pair).</para>
</section>
<section>
<title>Slot Call Iterator</title>
<para> The slot call iterator is conceptually a stack of iterator
adaptors that modify the behavior of the underlying iterator
through the list of slots. The following table describes the type
and behavior of each iterator adaptor required. Note that this is
only a conceptual model: the implementation collapses all these
layers into a single iterator adaptor because several popular
compilers failed to compile the implementation of the conceptual
model.</para>
<informaltable>
<tgroup cols="2" align="left">
<thead>
<row>
<entry>Iterator Adaptor</entry>
<entry>Purpose</entry>
</row>
</thead>
<tbody>
<row>
<entry><para>Slot List Iterator</para></entry>
<entry><para>An iterator through the list of slots
connected to a signal. The <code>value_type</code> of this
iterator will be
<code><classname>std::pair</classname>&lt;any,
connection&gt;</code>, where the
<code><classname>any</classname></code> contains an
instance of the slot function type.</para></entry>
</row>
<row>
<entry><para>Filter Iterator Adaptor</para></entry>
<entry><para>This filtering iterator adaptor filters out
slots that have been disconnected, so we never see a
disconnected slot in later stages.</para></entry>
</row>
<row>
<entry><para>Projection Iterator Adaptor</para></entry>
<entry><para>The projection iterator adaptor returns a
reference to the first member of the pair that constitutes
a connected slot (e.g., just the
<code><classname>boost::any</classname></code> object that
holds the slot function).</para></entry>
</row>
<row>
<entry><para>Transform Iterator Adaptor</para></entry>
<entry><para>This transform iterator adaptor performs an
<code><functionname>any_cast</functionname></code> to
extract a reference to the slot function with the
appropriate slot function type.</para></entry>
</row>
<row>
<entry><para>Transform Iterator Adaptor</para></entry>
<entry><para>This transform iterator adaptor calls the
function object returned by dereferencing the underlying
iterator with the set of arguments given to the signal
itself, and returns the result of that slot
call.</para></entry>
</row>
<row>
<entry><para>Input Caching Iterator Adaptor</para></entry>
<entry><para>This iterator adaptor caches the result of
dereferencing the underlying iterator. Therefore,
dereferencing this iterator multiple times will only
result in the underlying iterator being dereferenced once;
thus, a slot can only be called once but its result can be
used multiple times.</para></entry>
</row>
<row>
<entry><para>Slot Call Iterator</para></entry>
<entry><para>Iterates over calls to each slot.</para></entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><code>visit_each</code> function template</title>
<para> The <code><functionname>visit_each</functionname></code>
function template is a mechanism for discovering objects that are
stored within another object. Function template
<code><functionname>visit_each</functionname></code> takes three
arguments: an object to explore, a visitor function object that is
invoked with each subobject, and the <code>int</code> 0. </para>
<para> The third parameter is merely a temporary solution to the
widespread lack of proper function template partial ordering. The
primary <code><functionname>visit_each</functionname></code>
function template specifies this third parameter type to be
<code>long</code>, whereas any user specializations must specify
their third parameter to be of type <code>int</code>. Thus, even
though a broken compiler cannot tell the ordering between, e.g., a
match against a parameter <code>T</code> and a parameter
<code>A&lt;T&gt;</code>, it can determine that the conversion from
the integer 0 to <code>int</code> is better than the conversion to
<code>long</code>. The ordering determined by this conversion thus
achieves partial ordering of the function templates in a limited,
but successful, way. The following example illustrates the use of
this technique:</para>
<programlisting>
template&lt;typename&gt; class A {};
template&lt;typename T&gt; void foo(T, long);
template&lt;typename T&gt; void foo(A&lt;T&gt;, int);
A&lt;T&gt; at;
foo(at, 0);
</programlisting>
<para> In this example, we assume that our compiler can not tell
that <code>A&lt;T&gt;</code> is a better match than
<code>T</code>, and therefore assume that the function templates
cannot be ordered based on that parameter. Then the conversion
from 0 to <code>int</code> is better than the conversion from 0 to
<code>long</code>, and the second function template is
chosen. </para>
</section>
</section>