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