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">
5 Copyright 2003, Eric Friedman, Itay Maman.
6 Copyright 2013-2014, Antony Polukhin.
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)
11 <section id=
"variant.tutorial.advanced">
12 <title>Advanced Topics
</title>
14 <using-namespace name=
"boost"/>
15 <using-class name=
"boost::variant"/>
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
23 <section id=
"variant.tutorial.preprocessor">
24 <title>Preprocessor macros
</title>
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
<int, std::string
></code>, it is less clear how one
32 might write a function accepting any given
<code>variant
</code>.
</para>
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>
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:
47 <programlisting>// general cases
48 template
<typename T
> void some_func(const T
&);
49 template
<typename T
> class some_class;
51 // function template overload
52 template
<<macroname>BOOST_VARIANT_ENUM_PARAMS
</macroname>(typename T)
>
53 void some_func(const
<classname>boost::variant
</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS
</macroname>(T)
> &);
55 // explicit partial specialization
56 template
<<macroname>BOOST_VARIANT_ENUM_PARAMS
</macroname>(typename T)
>
57 class some_class
< <classname>boost::variant
</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS
</macroname>(T)
> >;
</programlisting>
63 <section id=
"variant.tutorial.over-sequence">
64 <title>Using a type sequence to specify bounded types
</title>
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>
75 <para>To solve these problems,
76 <code>make_variant_over
< <emphasis>Sequence
</emphasis> ></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,
82 <programlisting>typedef
<classname>mpl::vector
</classname>< std::string
> types_initial;
83 typedef
<classname>mpl::push_front
</classname>< types_initial, int
>::type types;
85 <classname>boost::make_variant_over
</classname>< types
>::type v1;
</programlisting>
87 behaves equivalently to
89 <programlisting><classname>boost::variant
</classname>< int, std::string
> v2;
</programlisting>
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>
102 <section id=
"variant.tutorial.recursive">
103 <title>Recursive
<code>variant
</code> types
</title>
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:
110 <programlisting>template
<typename T
>
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:
124 <programlisting>struct add;
126 template
<typename OpTag
> struct binary_op;
128 typedef
<classname>boost::variant
</classname><
130 , binary_op
<add
>
131 , binary_op
<sub
>
134 template
<typename OpTag
>
137 expression left; //
<emphasis>variant instantiated here...
</emphasis>
140 binary_op( const expression
& lhs, const expression
& rhs )
141 : left(lhs), right(rhs)
145 }; //
<emphasis>...but binary_op not complete until here!
</emphasis></programlisting>
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
"work,
"
154 <code>expression
</code> would need to be of infinite size, which is
155 clearly impossible.
</para>
157 <para>To overcome these difficulties,
<code>variant
</code> includes special
159 <code><classname>boost::recursive_wrapper
</classname></code> class
160 template, which breaks the circular dependency at the heart of these
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>
168 <section id=
"variant.tutorial.recursive.recursive-wrapper">
169 <title>Recursive types with
<code>recursive_wrapper
</code></title>
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"/>:
175 <programlisting>typedef
<classname>boost::variant
</classname><
177 ,
<classname>boost::recursive_wrapper
</classname>< binary_op
<add
> >
178 ,
<classname>boost::recursive_wrapper
</classname>< binary_op
<sub
> >
179 > expression;
</programlisting>
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>:
190 <programlisting>class calculator : public
<classname>boost::static_visitor
<int
></classname>
194 int operator()(int value) const
199 int operator()(const binary_op
<add
> & binary) const
201 return
<functionname>boost::apply_visitor
</functionname>( calculator(), binary.left )
202 +
<functionname>boost::apply_visitor
</functionname>( calculator(), binary.right );
205 int operator()(const binary_op
<sub
> & binary) const
207 return
<functionname>boost::apply_visitor
</functionname>( calculator(), binary.left )
208 -
<functionname>boost::apply_visitor
</functionname>( calculator(), binary.right );
215 <para>Finally, we can demonstrate
<code>expression
</code> in action:
217 <programlisting>void f()
219 // result = ((
7-
3)+
8) =
12
221 binary_op
<add
>(
222 binary_op
<sub
>(
7,
3)
227 assert(
<functionname>boost::apply_visitor
</functionname>(calculator(),result) ==
12 );
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>
238 <section id=
"variant.tutorial.recursive.recursive-variant">
239 <title>Recursive types with
<code>make_recursive_variant
</code></title>
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
246 <programlisting>typedef
<classname>boost::make_recursive_variant
</classname><
248 , std::vector
< boost::recursive_variant_
>
249 >::type int_tree_t;
</programlisting>
253 <para>Use of the resultant
<code>variant
</code> type is as expected:
255 <programlisting>std::vector
< int_tree_t
> subresult;
256 subresult.push_back(
3);
257 subresult.push_back(
5);
259 std::vector
< int_tree_t
> result;
261 result.push_back(subresult);
264 int_tree_t var(result);
</programlisting>
268 <para>To be clear, one might represent the resultant content of
269 <code>var
</code> as
<code>(
1 (
3 5 )
7 )
</code>.
</para>
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>
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
286 <xref linkend=
"variant.tutorial.recursive.recursive-wrapper"/>.
</para>
290 </section> <!--/tutorial.recursive-->
292 <section id=
"variant.tutorial.binary-visitation">
293 <title>Binary visitation
</title>
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>
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
306 <programlisting>class are_strict_equals
307 : public
<classname>boost::static_visitor
</classname><bool
>
311 template
<typename T, typename U
>
312 bool operator()( const T
&, const U
& ) const
314 return false; // cannot compare different types
317 template
<typename T
>
318 bool operator()( const T
& lhs, const T
& rhs ) const
327 <para>As expected, the visitor is applied to two
<code>variant
</code>
328 arguments by means of
<code>apply_visitor
</code>:
330 <programlisting><classname>boost::variant
</classname>< int, std::string
> v1(
"hello" );
332 <classname>boost::variant
</classname>< double, std::string
> v2(
"hello" );
333 assert(
<functionname>boost::apply_visitor
</functionname>(are_strict_equals(), v1, v2) );
335 <classname>boost::variant
</classname>< int, const char *
> v3(
"hello" );
336 assert( !
<functionname>boost::apply_visitor
</functionname>(are_strict_equals(), v1, v3) );
</programlisting>
340 <para>Finally, we must note that the function object returned from the
341 "delayed
" form of
342 <code><functionname>apply_visitor
</functionname></code> also supports
343 binary visitation, as the following demonstrates:
345 <programlisting>typedef
<classname>boost::variant
</classname><double, std::string
> my_variant;
347 std::vector
< my_variant
> seq1;
348 seq1.push_back(
"pi is close to ");
349 seq1.push_back(
3.14);
351 std::list
< my_variant
> seq2;
352 seq2.push_back(
"pi is close to ");
353 seq2.push_back(
3.14);
355 are_strict_equals visitor;
357 seq1.begin(), seq1.end(), seq2.begin()
358 ,
<functionname>boost::apply_visitor
</functionname>( visitor )
359 ) );
</programlisting>
365 <section id=
"variant.tutorial.multi-visitation">
366 <title>Multi visitation
</title>
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>
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:
378 #include
<boost/variant/multivisitors.hpp
>
380 typedef
<classname>boost::variant
</classname><int, double, bool
> bool_like_t;
381 typedef
<classname>boost::variant
</classname><int, double
> arithmetics_t;
383 struct if_visitor: public
<classname>boost::static_visitor
</classname><arithmetics_t
> {
384 template
<class T1, class T2
>
385 arithmetics_t operator()(bool b, T1 v1, T2 v2) const {
396 <para>As expected, the visitor is applied to three
<code>variant
</code>
397 arguments by means of
<code>apply_visitor
</code>:
400 bool_like_t v0(true), v1(
1), v2(
2.0);
403 <functionname>boost::apply_visitor
</functionname>(if_visitor(), v0, v1, v2)
410 <para>Finally, we must note that multi visitation does not support
411 "delayed
" form of
412 <code><functionname>apply_visitor
</functionname> if
413 <macroname>BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES
</macroname> is defined
</code>.