Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/variant/doc/tutorial/advanced.xml @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 13.4 KB
Line 
1<?xml version="1.0" encoding="utf-8"?>
2<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
3  "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
4<section id="variant.tutorial.advanced">
5  <title>Advanced Topics</title>
6
7<using-namespace name="boost"/>
8<using-class name="boost::variant"/>
9
10<para>This section discusses several features of the library often required
11  for advanced uses of <code>variant</code>. Unlike in the above section, each
12  feature presented below is largely independent of the others. Accordingly,
13  this section is not necessarily intended to be read linearly or in its
14  entirety.</para>
15
16<section id="variant.tutorial.preprocessor">
17  <title>Preprocessor macros</title>
18
19  <para>While the <code>variant</code> class template's variadic parameter
20    list greatly simplifies use for specific instantiations of the template,
21    it significantly complicates use for generic instantiations. For instance,
22    while it is immediately clear how one might write a function accepting a
23    specific <code>variant</code> instantiation, say
24    <code>variant&lt;int, std::string&gt;</code>, it is less clear how one
25    might write a function accepting any given <code>variant</code>.</para>
26
27  <para>Due to the lack of support for true variadic template parameter lists
28    in the C++98 standard, the preprocessor is needed. While the
29    <libraryname>Preprocessor</libraryname> library provides a general and
30    powerful solution, the need to repeat
31    <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>
32    unnecessarily clutters otherwise simple code. Therefore, for common
33    use-cases, this library provides its own macro
34    <code><emphasis role="bold"><macroname>BOOST_VARIANT_ENUM_PARAMS</macroname></emphasis></code>.</para>
35
36  <para>This macro simplifies for the user the process of declaring
37    <code>variant</code> types in function templates or explicit partial
38    specializations of class templates, as shown in the following:
39
40<programlisting>// general cases
41template &lt;typename T&gt; void some_func(const T &amp;);
42template &lt;typename T&gt; class some_class;
43
44// function template overload
45template &lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)&gt;
46void some_func(const <classname>boost::variant</classname>&lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)&gt; &amp;);
47
48// explicit partial specialization
49template &lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)&gt;
50class some_class&lt; <classname>boost::variant</classname>&lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)&gt; &gt;;</programlisting>
51
52  </para>
53
54</section>
55
56<section id="variant.tutorial.over-sequence">
57  <title>Using a type sequence to specify bounded types</title>
58
59  <para>While convenient for typical uses, the <code>variant</code> class
60    template's variadic template parameter list is limiting in two significant
61    dimensions. First, due to the lack of support for true variadic template
62    parameter lists in C++, the number of parameters must be limited to some
63    implementation-defined maximum (namely,
64    <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>).
65    Second, the nature of parameter lists in general makes compile-time
66    manipulation of the lists excessively difficult.</para>
67
68  <para>To solve these problems,
69    <code>make_variant_over&lt; <emphasis>Sequence</emphasis> &gt;</code>
70    exposes a <code>variant</code> whose bounded types are the elements of
71    <code>Sequence</code> (where <code>Sequence</code> is any type fulfilling
72    the requirements of <libraryname>MPL</libraryname>'s
73    <emphasis>Sequence</emphasis> concept). For instance,
74
75<programlisting>typedef <classname>mpl::vector</classname>&lt; std::string &gt; types_initial;
76typedef <classname>mpl::push_front</classname>&lt; types_initial, int &gt;::type types;
77
78<classname>boost::make_variant_over</classname>&lt; types &gt;::type v1;</programlisting>
79
80    behaves equivalently to
81
82<programlisting><classname>boost::variant</classname>&lt; int, std::string &gt; v2;</programlisting>
83
84  </para>
85
86  <para><emphasis role="bold">Portability</emphasis>: Unfortunately, due to
87    standard conformance issues in several compilers,
88    <code>make_variant_over</code> is not universally available. On these
89    compilers the library indicates its lack of support for the syntax via the
90    definition of the preprocessor symbol
91    <code><macroname>BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT</macroname></code>.</para>
92
93</section>
94
95<section id="variant.tutorial.recursive">
96  <title>Recursive <code>variant</code> types</title>
97
98  <para>Recursive types facilitate the construction of complex semantics from
99    simple syntax. For instance, nearly every programmer is familiar with the
100    canonical definition of a linked list implementation, whose simple
101    definition allows sequences of unlimited length:
102
103<programlisting>template &lt;typename T&gt;
104struct list_node
105{
106    T data;
107    list_node * next;
108};</programlisting>
109
110  </para>
111
112  <para>The nature of <code>variant</code> as a generic class template
113    unfortunately precludes the straightforward construction of recursive
114    <code>variant</code> types. Consider the following attempt to construct
115    a structure for simple mathematical expressions:
116
117    <programlisting>struct add;
118struct sub;
119template &lt;typename OpTag&gt; struct binary_op;
120
121typedef <classname>boost::variant</classname>&lt;
122      int
123    , binary_op&lt;add&gt;
124    , binary_op&lt;sub&gt;
125    > expression;
126
127template &lt;typename OpTag&gt;
128struct binary_op
129{
130    expression left;  // <emphasis>variant instantiated here...</emphasis>
131    expression right;
132
133    binary_op( const expression &amp; lhs, const expression &amp; rhs )
134        : left(lhs), right(rhs)
135    {
136    }
137
138}; // <emphasis>...but binary_op not complete until here!</emphasis></programlisting>
139
140  </para>
141
142  <para>While well-intentioned, the above approach will not compile because
143    <code>binary_op</code> is still incomplete when the <code>variant</code>
144    type <code>expression</code> is instantiated. Further, the approach suffers
145    from a more significant logical flaw: even if C++ syntax were different
146    such that the above example could be made to &quot;work,&quot;
147    <code>expression</code> would need to be of infinite size, which is
148    clearly impossible.</para>
149
150  <para>To overcome these difficulties, <code>variant</code> includes special
151    support for the
152    <code><classname>boost::recursive_wrapper</classname></code> class
153    template, which breaks the circular dependency at the heart of these
154    problems. Further,
155    <code><classname>boost::make_recursive_variant</classname></code> provides
156    a more convenient syntax for declaring recursive <code>variant</code>
157    types. Tutorials for use of these facilities is described in
158    <xref linkend="variant.tutorial.recursive.recursive-wrapper"/> and
159    <xref linkend="variant.tutorial.recursive.recursive-variant"/>.</para>
160
161<section id="variant.tutorial.recursive.recursive-wrapper">
162  <title>Recursive types with <code>recursive_wrapper</code></title>
163
164  <para>The following example demonstrates how <code>recursive_wrapper</code>
165    could be used to solve the problem presented in
166    <xref linkend="variant.tutorial.recursive"/>:
167
168    <programlisting>typedef <classname>boost::variant</classname>&lt;
169      int
170    , <classname>boost::recursive_wrapper</classname>&lt; binary_op&lt;add&gt; &gt;
171    , <classname>boost::recursive_wrapper</classname>&lt; binary_op&lt;sub&gt; &gt;
172    &gt; expression;</programlisting>
173
174  </para>
175
176  <para>Because <code>variant</code> provides special support for
177    <code>recursive_wrapper</code>, clients may treat the resultant
178    <code>variant</code> as though the wrapper were not present. This is seen
179    in the implementation of the following visitor, which calculates the value
180    of an <code>expression</code> without any reference to
181    <code>recursive_wrapper</code>:
182
183    <programlisting>class calculator : public <classname>boost::static_visitor&lt;int&gt;</classname>
184{
185public:
186
187    int operator()(int value) const
188    {
189        return value;
190    }
191
192    int operator()(const binary_op&lt;add&gt; &amp; binary) const
193    {
194        return <functionname>boost::apply_visitor</functionname>( calculator(), binary.left )
195             + <functionname>boost::apply_visitor</functionname>( calculator(), binary.right );
196    }
197
198    int operator()(const binary_op&lt;sub&gt; &amp; binary) const
199    {
200        return <functionname>boost::apply_visitor</functionname>( calculator(), binary.left )
201             - <functionname>boost::apply_visitor</functionname>( calculator(), binary.right );
202    }
203
204};</programlisting>
205
206  </para>
207 
208  <para>Finally, we can demonstrate <code>expression</code> in action:
209 
210    <programlisting>void f()
211{
212    // result = ((7-3)+8) = 12
213    expression result(
214        binary_op&lt;add&gt;(
215            binary_op&lt;sub&gt;(7,3)
216          , 8
217          )
218      );
219
220    assert( <functionname>boost::apply_visitor</functionname>(calculator(),result) == 12 );
221}</programlisting>
222
223  </para>
224
225</section>
226
227<section id="variant.tutorial.recursive.recursive-variant">
228  <title>Recursive types with <code>make_recursive_variant</code></title>
229
230  <para>For some applications of recursive <code>variant</code> types, a user
231    may be able to sacrifice the full flexibility of using
232    <code>recursive_wrapper</code> with <code>variant</code> for the following
233    convenient syntax:
234
235<programlisting>typedef <classname>boost::make_recursive_variant</classname>&lt;
236      int
237    , std::vector&lt; boost::recursive_variant_ &gt;
238    &gt;::type int_tree_t;</programlisting>
239
240  </para>
241
242  <para>Use of the resultant <code>variant</code> type is as expected:
243
244<programlisting>std::vector&lt; int_tree_t &gt; subresult;
245subresult.push_back(3);
246subresult.push_back(5);
247
248std::vector&lt; int_tree_t &gt; result;
249result.push_back(1);
250result.push_back(subresult);
251result.push_back(7);
252
253int_tree_t var(result);</programlisting>
254
255  </para>
256
257  <para>To be clear, one might represent the resultant content of
258    <code>var</code> as <code>( 1 ( 3 5 ) 7 )</code>.</para>
259
260  <para>Finally, note that a type sequence can be used to specify the bounded
261    types of a recursive <code>variant</code> via the use of
262    <code><classname>boost::make_recursive_variant_over</classname></code>,
263    whose semantics are the same as <code>make_variant_over</code> (which is
264    described in <xref linkend="variant.tutorial.over-sequence"/>).</para>
265
266  <para><emphasis role="bold">Portability</emphasis>: Unfortunately, due to
267    standard conformance issues in several compilers,
268    <code>make_recursive_variant</code> is not universally supported. On these
269    compilers the library indicates its lack of support via the definition
270    of the preprocessor symbol
271    <code><macroname>BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT</macroname></code>.
272    Thus, unless working with highly-conformant compilers, maximum portability
273    will be achieved by instead using <code>recursive_wrapper</code>, as
274    described in
275    <xref linkend="variant.tutorial.recursive.recursive-wrapper"/>.</para>
276
277</section>
278
279</section> <!--/tutorial.recursive-->
280
281<section id="variant.tutorial.binary-visitation">
282  <title>Binary visitation</title>
283
284  <para>As the tutorial above demonstrates, visitation is a powerful mechanism
285    for manipulating <code>variant</code> content. Binary visitation further
286    extends the power and flexibility of visitation by allowing simultaneous
287    visitation of the content of two different <code>variant</code>
288    objects.</para>
289
290  <para>Notably this feature requires that binary visitors are incompatible
291    with the visitor objects discussed in the tutorial above, as they must
292    operate on two arguments. The following demonstrates the implementation of
293    a binary visitor:
294
295<programlisting>class are_strict_equals
296    : public <classname>boost::static_visitor</classname>&lt;bool&gt;
297{
298public:
299
300    template &lt;typename T, typename U&gt;
301    bool operator()( const T &amp;, const U &amp; ) const
302    {
303        return false; // cannot compare different types
304    }
305
306    template &lt;typename T&gt;
307    bool operator()( const T &amp; lhs, const T &amp; rhs ) const
308    {
309        return lhs == rhs;
310    }
311
312};</programlisting>
313
314  </para>
315
316  <para>As expected, the visitor is applied to two <code>variant</code>
317    arguments by means of <code>apply_visitor</code>:
318
319<programlisting><classname>boost::variant</classname>&lt; int, std::string &gt; v1( "hello" );
320
321<classname>boost::variant</classname>&lt; double, std::string &gt; v2( "hello" );
322assert( <functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v2) );
323
324<classname>boost::variant</classname>&lt; int, const char * &gt; v3( "hello" );
325assert( !<functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v3) );</programlisting>
326
327  </para>
328
329  <para>Finally, we must note that the function object returned from the
330    &quot;delayed&quot; form of
331    <code><functionname>apply_visitor</functionname></code> also supports
332    binary visitation, as the following demonstrates:
333
334<programlisting>typedef <classname>boost::variant</classname>&lt;double, std::string&gt; my_variant;
335
336std::vector&lt; my_variant &gt; seq1;
337seq1.push_back("pi is close to ");
338seq1.push_back(3.14);
339
340std::list&lt; my_variant &gt; seq2;
341seq2.push_back("pi is close to ");
342seq2.push_back(3.14);
343
344are_strict_equals visitor;
345assert( std::equal(
346      v1.begin(), v1.end(), v2.begin()
347    , <functionname>boost::apply_visitor</functionname>( visitor )
348    ) );</programlisting>
349
350  </para>
351
352</section>
353
354</section>
Note: See TracBrowser for help on using the repository browser.