| 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<int, std::string></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 |
|---|
| 41 | template <typename T> void some_func(const T &); |
|---|
| 42 | template <typename T> class some_class; |
|---|
| 43 | |
|---|
| 44 | // function template overload |
|---|
| 45 | template <<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)> |
|---|
| 46 | void some_func(const <classname>boost::variant</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)> &); |
|---|
| 47 | |
|---|
| 48 | // explicit partial specialization |
|---|
| 49 | template <<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)> |
|---|
| 50 | class some_class< <classname>boost::variant</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)> >;</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< <emphasis>Sequence</emphasis> ></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>< std::string > types_initial; |
|---|
| 76 | typedef <classname>mpl::push_front</classname>< types_initial, int >::type types; |
|---|
| 77 | |
|---|
| 78 | <classname>boost::make_variant_over</classname>< types >::type v1;</programlisting> |
|---|
| 79 | |
|---|
| 80 | behaves equivalently to |
|---|
| 81 | |
|---|
| 82 | <programlisting><classname>boost::variant</classname>< int, std::string > 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 <typename T> |
|---|
| 104 | struct 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; |
|---|
| 118 | struct sub; |
|---|
| 119 | template <typename OpTag> struct binary_op; |
|---|
| 120 | |
|---|
| 121 | typedef <classname>boost::variant</classname>< |
|---|
| 122 | int |
|---|
| 123 | , binary_op<add> |
|---|
| 124 | , binary_op<sub> |
|---|
| 125 | > expression; |
|---|
| 126 | |
|---|
| 127 | template <typename OpTag> |
|---|
| 128 | struct binary_op |
|---|
| 129 | { |
|---|
| 130 | expression left; // <emphasis>variant instantiated here...</emphasis> |
|---|
| 131 | expression right; |
|---|
| 132 | |
|---|
| 133 | binary_op( const expression & lhs, const expression & 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 "work," |
|---|
| 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>< |
|---|
| 169 | int |
|---|
| 170 | , <classname>boost::recursive_wrapper</classname>< binary_op<add> > |
|---|
| 171 | , <classname>boost::recursive_wrapper</classname>< binary_op<sub> > |
|---|
| 172 | > 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<int></classname> |
|---|
| 184 | { |
|---|
| 185 | public: |
|---|
| 186 | |
|---|
| 187 | int operator()(int value) const |
|---|
| 188 | { |
|---|
| 189 | return value; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | int operator()(const binary_op<add> & 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<sub> & 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<add>( |
|---|
| 215 | binary_op<sub>(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>< |
|---|
| 236 | int |
|---|
| 237 | , std::vector< boost::recursive_variant_ > |
|---|
| 238 | >::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< int_tree_t > subresult; |
|---|
| 245 | subresult.push_back(3); |
|---|
| 246 | subresult.push_back(5); |
|---|
| 247 | |
|---|
| 248 | std::vector< int_tree_t > result; |
|---|
| 249 | result.push_back(1); |
|---|
| 250 | result.push_back(subresult); |
|---|
| 251 | result.push_back(7); |
|---|
| 252 | |
|---|
| 253 | int_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><bool> |
|---|
| 297 | { |
|---|
| 298 | public: |
|---|
| 299 | |
|---|
| 300 | template <typename T, typename U> |
|---|
| 301 | bool operator()( const T &, const U & ) const |
|---|
| 302 | { |
|---|
| 303 | return false; // cannot compare different types |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | template <typename T> |
|---|
| 307 | bool operator()( const T & lhs, const T & 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>< int, std::string > v1( "hello" ); |
|---|
| 320 | |
|---|
| 321 | <classname>boost::variant</classname>< double, std::string > v2( "hello" ); |
|---|
| 322 | assert( <functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v2) ); |
|---|
| 323 | |
|---|
| 324 | <classname>boost::variant</classname>< int, const char * > v3( "hello" ); |
|---|
| 325 | assert( !<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 | "delayed" 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><double, std::string> my_variant; |
|---|
| 335 | |
|---|
| 336 | std::vector< my_variant > seq1; |
|---|
| 337 | seq1.push_back("pi is close to "); |
|---|
| 338 | seq1.push_back(3.14); |
|---|
| 339 | |
|---|
| 340 | std::list< my_variant > seq2; |
|---|
| 341 | seq2.push_back("pi is close to "); |
|---|
| 342 | seq2.push_back(3.14); |
|---|
| 343 | |
|---|
| 344 | are_strict_equals visitor; |
|---|
| 345 | assert( 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> |
|---|