| <?xml version="1.0" encoding="utf-8"?> |
| <!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" |
| "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"> |
| <section id="variant.tutorial.advanced"> |
| <title>Advanced Topics</title> |
| |
| <using-namespace name="boost"/> |
| <using-class name="boost::variant"/> |
| |
| <para>This section discusses several features of the library often required |
| for advanced uses of <code>variant</code>. Unlike in the above section, each |
| feature presented below is largely independent of the others. Accordingly, |
| this section is not necessarily intended to be read linearly or in its |
| entirety.</para> |
| |
| <section id="variant.tutorial.preprocessor"> |
| <title>Preprocessor macros</title> |
| |
| <para>While the <code>variant</code> class template's variadic parameter |
| list greatly simplifies use for specific instantiations of the template, |
| it significantly complicates use for generic instantiations. For instance, |
| while it is immediately clear how one might write a function accepting a |
| specific <code>variant</code> instantiation, say |
| <code>variant<int, std::string></code>, it is less clear how one |
| might write a function accepting any given <code>variant</code>.</para> |
| |
| <para>Due to the lack of support for true variadic template parameter lists |
| in the C++98 standard, the preprocessor is needed. While the |
| <libraryname>Preprocessor</libraryname> library provides a general and |
| powerful solution, the need to repeat |
| <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code> |
| unnecessarily clutters otherwise simple code. Therefore, for common |
| use-cases, this library provides its own macro |
| <code><emphasis role="bold"><macroname>BOOST_VARIANT_ENUM_PARAMS</macroname></emphasis></code>.</para> |
| |
| <para>This macro simplifies for the user the process of declaring |
| <code>variant</code> types in function templates or explicit partial |
| specializations of class templates, as shown in the following: |
| |
| <programlisting>// general cases |
| template <typename T> void some_func(const T &); |
| template <typename T> class some_class; |
| |
| // function template overload |
| template <<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)> |
| void some_func(const <classname>boost::variant</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)> &); |
| |
| // explicit partial specialization |
| template <<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)> |
| class some_class< <classname>boost::variant</classname><<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)> >;</programlisting> |
| |
| </para> |
| |
| </section> |
| |
| <section id="variant.tutorial.over-sequence"> |
| <title>Using a type sequence to specify bounded types</title> |
| |
| <para>While convenient for typical uses, the <code>variant</code> class |
| template's variadic template parameter list is limiting in two significant |
| dimensions. First, due to the lack of support for true variadic template |
| parameter lists in C++, the number of parameters must be limited to some |
| implementation-defined maximum (namely, |
| <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>). |
| Second, the nature of parameter lists in general makes compile-time |
| manipulation of the lists excessively difficult.</para> |
| |
| <para>To solve these problems, |
| <code>make_variant_over< <emphasis>Sequence</emphasis> ></code> |
| exposes a <code>variant</code> whose bounded types are the elements of |
| <code>Sequence</code> (where <code>Sequence</code> is any type fulfilling |
| the requirements of <libraryname>MPL</libraryname>'s |
| <emphasis>Sequence</emphasis> concept). For instance, |
| |
| <programlisting>typedef <classname>mpl::vector</classname>< std::string > types_initial; |
| typedef <classname>mpl::push_front</classname>< types_initial, int >::type types; |
| |
| <classname>boost::make_variant_over</classname>< types >::type v1;</programlisting> |
| |
| behaves equivalently to |
| |
| <programlisting><classname>boost::variant</classname>< int, std::string > v2;</programlisting> |
| |
| </para> |
| |
| <para><emphasis role="bold">Portability</emphasis>: Unfortunately, due to |
| standard conformance issues in several compilers, |
| <code>make_variant_over</code> is not universally available. On these |
| compilers the library indicates its lack of support for the syntax via the |
| definition of the preprocessor symbol |
| <code><macroname>BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT</macroname></code>.</para> |
| |
| </section> |
| |
| <section id="variant.tutorial.recursive"> |
| <title>Recursive <code>variant</code> types</title> |
| |
| <para>Recursive types facilitate the construction of complex semantics from |
| simple syntax. For instance, nearly every programmer is familiar with the |
| canonical definition of a linked list implementation, whose simple |
| definition allows sequences of unlimited length: |
| |
| <programlisting>template <typename T> |
| struct list_node |
| { |
| T data; |
| list_node * next; |
| };</programlisting> |
| |
| </para> |
| |
| <para>The nature of <code>variant</code> as a generic class template |
| unfortunately precludes the straightforward construction of recursive |
| <code>variant</code> types. Consider the following attempt to construct |
| a structure for simple mathematical expressions: |
| |
| <programlisting>struct add; |
| struct sub; |
| template <typename OpTag> struct binary_op; |
| |
| typedef <classname>boost::variant</classname>< |
| int |
| , binary_op<add> |
| , binary_op<sub> |
| > expression; |
| |
| template <typename OpTag> |
| struct binary_op |
| { |
| expression left; // <emphasis>variant instantiated here...</emphasis> |
| expression right; |
| |
| binary_op( const expression & lhs, const expression & rhs ) |
| : left(lhs), right(rhs) |
| { |
| } |
| |
| }; // <emphasis>...but binary_op not complete until here!</emphasis></programlisting> |
| |
| </para> |
| |
| <para>While well-intentioned, the above approach will not compile because |
| <code>binary_op</code> is still incomplete when the <code>variant</code> |
| type <code>expression</code> is instantiated. Further, the approach suffers |
| from a more significant logical flaw: even if C++ syntax were different |
| such that the above example could be made to "work," |
| <code>expression</code> would need to be of infinite size, which is |
| clearly impossible.</para> |
| |
| <para>To overcome these difficulties, <code>variant</code> includes special |
| support for the |
| <code><classname>boost::recursive_wrapper</classname></code> class |
| template, which breaks the circular dependency at the heart of these |
| problems. Further, |
| <code><classname>boost::make_recursive_variant</classname></code> provides |
| a more convenient syntax for declaring recursive <code>variant</code> |
| types. Tutorials for use of these facilities is described in |
| <xref linkend="variant.tutorial.recursive.recursive-wrapper"/> and |
| <xref linkend="variant.tutorial.recursive.recursive-variant"/>.</para> |
| |
| <section id="variant.tutorial.recursive.recursive-wrapper"> |
| <title>Recursive types with <code>recursive_wrapper</code></title> |
| |
| <para>The following example demonstrates how <code>recursive_wrapper</code> |
| could be used to solve the problem presented in |
| <xref linkend="variant.tutorial.recursive"/>: |
| |
| <programlisting>typedef <classname>boost::variant</classname>< |
| int |
| , <classname>boost::recursive_wrapper</classname>< binary_op<add> > |
| , <classname>boost::recursive_wrapper</classname>< binary_op<sub> > |
| > expression;</programlisting> |
| |
| </para> |
| |
| <para>Because <code>variant</code> provides special support for |
| <code>recursive_wrapper</code>, clients may treat the resultant |
| <code>variant</code> as though the wrapper were not present. This is seen |
| in the implementation of the following visitor, which calculates the value |
| of an <code>expression</code> without any reference to |
| <code>recursive_wrapper</code>: |
| |
| <programlisting>class calculator : public <classname>boost::static_visitor<int></classname> |
| { |
| public: |
| |
| int operator()(int value) const |
| { |
| return value; |
| } |
| |
| int operator()(const binary_op<add> & binary) const |
| { |
| return <functionname>boost::apply_visitor</functionname>( calculator(), binary.left ) |
| + <functionname>boost::apply_visitor</functionname>( calculator(), binary.right ); |
| } |
| |
| int operator()(const binary_op<sub> & binary) const |
| { |
| return <functionname>boost::apply_visitor</functionname>( calculator(), binary.left ) |
| - <functionname>boost::apply_visitor</functionname>( calculator(), binary.right ); |
| } |
| |
| };</programlisting> |
| |
| </para> |
| |
| <para>Finally, we can demonstrate <code>expression</code> in action: |
| |
| <programlisting>void f() |
| { |
| // result = ((7-3)+8) = 12 |
| expression result( |
| binary_op<add>( |
| binary_op<sub>(7,3) |
| , 8 |
| ) |
| ); |
| |
| assert( <functionname>boost::apply_visitor</functionname>(calculator(),result) == 12 ); |
| }</programlisting> |
| |
| </para> |
| |
| </section> |
| |
| <section id="variant.tutorial.recursive.recursive-variant"> |
| <title>Recursive types with <code>make_recursive_variant</code></title> |
| |
| <para>For some applications of recursive <code>variant</code> types, a user |
| may be able to sacrifice the full flexibility of using |
| <code>recursive_wrapper</code> with <code>variant</code> for the following |
| convenient syntax: |
| |
| <programlisting>typedef <classname>boost::make_recursive_variant</classname>< |
| int |
| , std::vector< boost::recursive_variant_ > |
| >::type int_tree_t;</programlisting> |
| |
| </para> |
| |
| <para>Use of the resultant <code>variant</code> type is as expected: |
| |
| <programlisting>std::vector< int_tree_t > subresult; |
| subresult.push_back(3); |
| subresult.push_back(5); |
| |
| std::vector< int_tree_t > result; |
| result.push_back(1); |
| result.push_back(subresult); |
| result.push_back(7); |
| |
| int_tree_t var(result);</programlisting> |
| |
| </para> |
| |
| <para>To be clear, one might represent the resultant content of |
| <code>var</code> as <code>( 1 ( 3 5 ) 7 )</code>.</para> |
| |
| <para>Finally, note that a type sequence can be used to specify the bounded |
| types of a recursive <code>variant</code> via the use of |
| <code><classname>boost::make_recursive_variant_over</classname></code>, |
| whose semantics are the same as <code>make_variant_over</code> (which is |
| described in <xref linkend="variant.tutorial.over-sequence"/>).</para> |
| |
| <para><emphasis role="bold">Portability</emphasis>: Unfortunately, due to |
| standard conformance issues in several compilers, |
| <code>make_recursive_variant</code> is not universally supported. On these |
| compilers the library indicates its lack of support via the definition |
| of the preprocessor symbol |
| <code><macroname>BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT</macroname></code>. |
| Thus, unless working with highly-conformant compilers, maximum portability |
| will be achieved by instead using <code>recursive_wrapper</code>, as |
| described in |
| <xref linkend="variant.tutorial.recursive.recursive-wrapper"/>.</para> |
| |
| </section> |
| |
| </section> <!--/tutorial.recursive--> |
| |
| <section id="variant.tutorial.binary-visitation"> |
| <title>Binary visitation</title> |
| |
| <para>As the tutorial above demonstrates, visitation is a powerful mechanism |
| for manipulating <code>variant</code> content. Binary visitation further |
| extends the power and flexibility of visitation by allowing simultaneous |
| visitation of the content of two different <code>variant</code> |
| objects.</para> |
| |
| <para>Notably this feature requires that binary visitors are incompatible |
| with the visitor objects discussed in the tutorial above, as they must |
| operate on two arguments. The following demonstrates the implementation of |
| a binary visitor: |
| |
| <programlisting>class are_strict_equals |
| : public <classname>boost::static_visitor</classname><bool> |
| { |
| public: |
| |
| template <typename T, typename U> |
| bool operator()( const T &, const U & ) const |
| { |
| return false; // cannot compare different types |
| } |
| |
| template <typename T> |
| bool operator()( const T & lhs, const T & rhs ) const |
| { |
| return lhs == rhs; |
| } |
| |
| };</programlisting> |
| |
| </para> |
| |
| <para>As expected, the visitor is applied to two <code>variant</code> |
| arguments by means of <code>apply_visitor</code>: |
| |
| <programlisting><classname>boost::variant</classname>< int, std::string > v1( "hello" ); |
| |
| <classname>boost::variant</classname>< double, std::string > v2( "hello" ); |
| assert( <functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v2) ); |
| |
| <classname>boost::variant</classname>< int, const char * > v3( "hello" ); |
| assert( !<functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v3) );</programlisting> |
| |
| </para> |
| |
| <para>Finally, we must note that the function object returned from the |
| "delayed" form of |
| <code><functionname>apply_visitor</functionname></code> also supports |
| binary visitation, as the following demonstrates: |
| |
| <programlisting>typedef <classname>boost::variant</classname><double, std::string> my_variant; |
| |
| std::vector< my_variant > seq1; |
| seq1.push_back("pi is close to "); |
| seq1.push_back(3.14); |
| |
| std::list< my_variant > seq2; |
| seq2.push_back("pi is close to "); |
| seq2.push_back(3.14); |
| |
| are_strict_equals visitor; |
| assert( std::equal( |
| seq1.begin(), seq1.end(), seq2.begin() |
| , <functionname>boost::apply_visitor</functionname>( visitor ) |
| ) );</programlisting> |
| |
| </para> |
| |
| </section> |
| |
| </section> |