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<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 48template <typename T> void some_func(const T &); 49template <typename T> class some_class; 50 51// function template overload 52template <<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)> 53void some_func(const <classname>boost::variant</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)> &); 54 55// explicit partial specialization 56template <<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)> 57class 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; 83typedef <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> 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 <typename OpTag> struct binary_op; 127 128typedef <classname>boost::variant</classname>< 129 int 130 , binary_op<add> 131 , binary_op<sub> 132 > expression; 133 134template <typename OpTag> 135struct 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{ 192public: 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; 256subresult.push_back(3); 257subresult.push_back(5); 258 259std::vector< int_tree_t > 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><bool> 308{ 309public: 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" ); 333assert( <functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v2) ); 334 335<classname>boost::variant</classname>< int, const char * > 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 "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 347std::vector< my_variant > seq1; 348seq1.push_back("pi is close to "); 349seq1.push_back(3.14); 350 351std::list< my_variant > 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 <boost/variant/multivisitors.hpp> 379 380typedef <classname>boost::variant</classname><int, double, bool> bool_like_t; 381typedef <classname>boost::variant</classname><int, double> arithmetics_t; 382 383struct 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> 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 "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> 420