]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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-2014, 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<int, std::string></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 | |
48 | template <typename T> void some_func(const T &); | |
49 | template <typename T> class some_class; | |
50 | ||
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)> &); | |
54 | ||
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> | |
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< <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, | |
81 | ||
82 | <programlisting>typedef <classname>mpl::vector</classname>< std::string > types_initial; | |
83 | typedef <classname>mpl::push_front</classname>< types_initial, int >::type types; | |
84 | ||
85 | <classname>boost::make_variant_over</classname>< types >::type v1;</programlisting> | |
86 | ||
87 | behaves equivalently to | |
88 | ||
89 | <programlisting><classname>boost::variant</classname>< int, std::string > 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 <typename T> | |
111 | struct 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; | |
125 | struct sub; | |
126 | template <typename OpTag> struct binary_op; | |
127 | ||
128 | typedef <classname>boost::variant</classname>< | |
129 | int | |
130 | , binary_op<add> | |
131 | , binary_op<sub> | |
132 | > expression; | |
133 | ||
134 | template <typename OpTag> | |
135 | struct binary_op | |
136 | { | |
137 | expression left; // <emphasis>variant instantiated here...</emphasis> | |
138 | expression right; | |
139 | ||
140 | binary_op( const expression & lhs, const expression & 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 "work," | |
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>< | |
176 | int | |
177 | , <classname>boost::recursive_wrapper</classname>< binary_op<add> > | |
178 | , <classname>boost::recursive_wrapper</classname>< binary_op<sub> > | |
179 | > 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<int></classname> | |
191 | { | |
192 | public: | |
193 | ||
194 | int operator()(int value) const | |
195 | { | |
196 | return value; | |
197 | } | |
198 | ||
199 | int operator()(const binary_op<add> & 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<sub> & 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<add>( | |
222 | binary_op<sub>(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>< | |
247 | int | |
248 | , std::vector< boost::recursive_variant_ > | |
249 | >::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< int_tree_t > subresult; | |
256 | subresult.push_back(3); | |
257 | subresult.push_back(5); | |
258 | ||
259 | std::vector< int_tree_t > result; | |
260 | result.push_back(1); | |
261 | result.push_back(subresult); | |
262 | result.push_back(7); | |
263 | ||
264 | int_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><bool> | |
308 | { | |
309 | public: | |
310 | ||
311 | template <typename T, typename U> | |
312 | bool operator()( const T &, const U & ) const | |
313 | { | |
314 | return false; // cannot compare different types | |
315 | } | |
316 | ||
317 | template <typename T> | |
318 | bool operator()( const T & lhs, const T & 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>< int, std::string > v1( "hello" ); | |
331 | ||
332 | <classname>boost::variant</classname>< double, std::string > v2( "hello" ); | |
333 | assert( <functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v2) ); | |
334 | ||
335 | <classname>boost::variant</classname>< int, const char * > v3( "hello" ); | |
336 | assert( !<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 | "delayed" 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><double, std::string> my_variant; | |
346 | ||
347 | std::vector< my_variant > seq1; | |
348 | seq1.push_back("pi is close to "); | |
349 | seq1.push_back(3.14); | |
350 | ||
351 | std::list< my_variant > seq2; | |
352 | seq2.push_back("pi is close to "); | |
353 | seq2.push_back(3.14); | |
354 | ||
355 | are_strict_equals visitor; | |
356 | assert( 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 <boost/variant/multivisitors.hpp> | |
379 | ||
380 | typedef <classname>boost::variant</classname><int, double, bool> bool_like_t; | |
381 | typedef <classname>boost::variant</classname><int, double> arithmetics_t; | |
382 | ||
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 { | |
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> | |
400 | bool_like_t v0(true), v1(1), v2(2.0); | |
401 | ||
402 | assert( | |
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 | "delayed" 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> |