advanced.xml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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-2019 Antony Polukhin.
  7. Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. -->
  10. <section id="variant.tutorial.advanced">
  11. <title>Advanced Topics</title>
  12. <using-namespace name="boost"/>
  13. <using-class name="boost::variant"/>
  14. <para>This section discusses several features of the library often required
  15. for advanced uses of <code>variant</code>. Unlike in the above section, each
  16. feature presented below is largely independent of the others. Accordingly,
  17. this section is not necessarily intended to be read linearly or in its
  18. entirety.</para>
  19. <section id="variant.tutorial.preprocessor">
  20. <title>Preprocessor macros</title>
  21. <para>While the <code>variant</code> class template's variadic parameter
  22. list greatly simplifies use for specific instantiations of the template,
  23. it significantly complicates use for generic instantiations. For instance,
  24. while it is immediately clear how one might write a function accepting a
  25. specific <code>variant</code> instantiation, say
  26. <code>variant&lt;int, std::string&gt;</code>, it is less clear how one
  27. might write a function accepting any given <code>variant</code>.</para>
  28. <para>Due to the lack of support for true variadic template parameter lists
  29. in the C++98 standard, the preprocessor is needed. While the
  30. <libraryname>Preprocessor</libraryname> library provides a general and
  31. powerful solution, the need to repeat
  32. <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>
  33. unnecessarily clutters otherwise simple code. Therefore, for common
  34. use-cases, this library provides its own macro
  35. <code><emphasis role="bold"><macroname>BOOST_VARIANT_ENUM_PARAMS</macroname></emphasis></code>.</para>
  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. <programlisting>// general cases
  40. template &lt;typename T&gt; void some_func(const T &amp;);
  41. template &lt;typename T&gt; class some_class;
  42. // function template overload
  43. template &lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)&gt;
  44. void some_func(const <classname>boost::variant</classname>&lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)&gt; &amp;);
  45. // explicit partial specialization
  46. template &lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(typename T)&gt;
  47. class some_class&lt; <classname>boost::variant</classname>&lt;<macroname>BOOST_VARIANT_ENUM_PARAMS</macroname>(T)&gt; &gt;;</programlisting>
  48. </para>
  49. </section>
  50. <section id="variant.tutorial.over-sequence">
  51. <title>Using a type sequence to specify bounded types</title>
  52. <para>While convenient for typical uses, the <code>variant</code> class
  53. template's variadic template parameter list is limiting in two significant
  54. dimensions. First, due to the lack of support for true variadic template
  55. parameter lists in C++, the number of parameters must be limited to some
  56. implementation-defined maximum (namely,
  57. <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>).
  58. Second, the nature of parameter lists in general makes compile-time
  59. manipulation of the lists excessively difficult.</para>
  60. <para>To solve these problems,
  61. <code>make_variant_over&lt; <emphasis>Sequence</emphasis> &gt;</code>
  62. exposes a <code>variant</code> whose bounded types are the elements of
  63. <code>Sequence</code> (where <code>Sequence</code> is any type fulfilling
  64. the requirements of <libraryname>MPL</libraryname>'s
  65. <emphasis>Sequence</emphasis> concept). For instance,
  66. <programlisting>typedef <classname>mpl::vector</classname>&lt; std::string &gt; types_initial;
  67. typedef <classname>mpl::push_front</classname>&lt; types_initial, int &gt;::type types;
  68. <classname>boost::make_variant_over</classname>&lt; types &gt;::type v1;</programlisting>
  69. behaves equivalently to
  70. <programlisting><classname>boost::variant</classname>&lt; int, std::string &gt; v2;</programlisting>
  71. </para>
  72. <para><emphasis role="bold">Portability</emphasis>: Unfortunately, due to
  73. standard conformance issues in several compilers,
  74. <code>make_variant_over</code> is not universally available. On these
  75. compilers the library indicates its lack of support for the syntax via the
  76. definition of the preprocessor symbol
  77. <code><macroname>BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT</macroname></code>.</para>
  78. </section>
  79. <section id="variant.tutorial.recursive">
  80. <title>Recursive <code>variant</code> types</title>
  81. <para>Recursive types facilitate the construction of complex semantics from
  82. simple syntax. For instance, nearly every programmer is familiar with the
  83. canonical definition of a linked list implementation, whose simple
  84. definition allows sequences of unlimited length:
  85. <programlisting>template &lt;typename T&gt;
  86. struct list_node
  87. {
  88. T data;
  89. list_node * next;
  90. };</programlisting>
  91. </para>
  92. <para>The nature of <code>variant</code> as a generic class template
  93. unfortunately precludes the straightforward construction of recursive
  94. <code>variant</code> types. Consider the following attempt to construct
  95. a structure for simple mathematical expressions:
  96. <programlisting>struct add;
  97. struct sub;
  98. template &lt;typename OpTag&gt; struct binary_op;
  99. typedef <classname>boost::variant</classname>&lt;
  100. int
  101. , binary_op&lt;add&gt;
  102. , binary_op&lt;sub&gt;
  103. > expression;
  104. template &lt;typename OpTag&gt;
  105. struct binary_op
  106. {
  107. expression left; // <emphasis>variant instantiated here...</emphasis>
  108. expression right;
  109. binary_op( const expression &amp; lhs, const expression &amp; rhs )
  110. : left(lhs), right(rhs)
  111. {
  112. }
  113. }; // <emphasis>...but binary_op not complete until here!</emphasis></programlisting>
  114. </para>
  115. <para>While well-intentioned, the above approach will not compile because
  116. <code>binary_op</code> is still incomplete when the <code>variant</code>
  117. type <code>expression</code> is instantiated. Further, the approach suffers
  118. from a more significant logical flaw: even if C++ syntax were different
  119. such that the above example could be made to &quot;work,&quot;
  120. <code>expression</code> would need to be of infinite size, which is
  121. clearly impossible.</para>
  122. <para>To overcome these difficulties, <code>variant</code> includes special
  123. support for the
  124. <code><classname>boost::recursive_wrapper</classname></code> class
  125. template, which breaks the circular dependency at the heart of these
  126. problems. Further,
  127. <code><classname>boost::make_recursive_variant</classname></code> provides
  128. a more convenient syntax for declaring recursive <code>variant</code>
  129. types. Tutorials for use of these facilities is described in
  130. <xref linkend="variant.tutorial.recursive.recursive-wrapper"/> and
  131. <xref linkend="variant.tutorial.recursive.recursive-variant"/>.</para>
  132. <section id="variant.tutorial.recursive.recursive-wrapper">
  133. <title>Recursive types with <code>recursive_wrapper</code></title>
  134. <para>The following example demonstrates how <code>recursive_wrapper</code>
  135. could be used to solve the problem presented in
  136. <xref linkend="variant.tutorial.recursive"/>:
  137. <programlisting>typedef <classname>boost::variant</classname>&lt;
  138. int
  139. , <classname>boost::recursive_wrapper</classname>&lt; binary_op&lt;add&gt; &gt;
  140. , <classname>boost::recursive_wrapper</classname>&lt; binary_op&lt;sub&gt; &gt;
  141. &gt; expression;</programlisting>
  142. </para>
  143. <para>Because <code>variant</code> provides special support for
  144. <code>recursive_wrapper</code>, clients may treat the resultant
  145. <code>variant</code> as though the wrapper were not present. This is seen
  146. in the implementation of the following visitor, which calculates the value
  147. of an <code>expression</code> without any reference to
  148. <code>recursive_wrapper</code>:
  149. <programlisting>class calculator : public <classname>boost::static_visitor&lt;int&gt;</classname>
  150. {
  151. public:
  152. int operator()(int value) const
  153. {
  154. return value;
  155. }
  156. int operator()(const binary_op&lt;add&gt; &amp; binary) const
  157. {
  158. return <functionname>boost::apply_visitor</functionname>( calculator(), binary.left )
  159. + <functionname>boost::apply_visitor</functionname>( calculator(), binary.right );
  160. }
  161. int operator()(const binary_op&lt;sub&gt; &amp; binary) const
  162. {
  163. return <functionname>boost::apply_visitor</functionname>( calculator(), binary.left )
  164. - <functionname>boost::apply_visitor</functionname>( calculator(), binary.right );
  165. }
  166. };</programlisting>
  167. </para>
  168. <para>Finally, we can demonstrate <code>expression</code> in action:
  169. <programlisting>void f()
  170. {
  171. // result = ((7-3)+8) = 12
  172. expression result(
  173. binary_op&lt;add&gt;(
  174. binary_op&lt;sub&gt;(7,3)
  175. , 8
  176. )
  177. );
  178. assert( <functionname>boost::apply_visitor</functionname>(calculator(),result) == 12 );
  179. }</programlisting>
  180. </para>
  181. <para><emphasis role="bold">Performance</emphasis>: <classname>boost::recursive_wrapper</classname>
  182. has no empty state, which makes its move constructor not very optimal. Consider using <code>std::unique_ptr</code>
  183. or some other safe pointer for better performance on C++11 compatible compilers.</para>
  184. </section>
  185. <section id="variant.tutorial.recursive.recursive-variant">
  186. <title>Recursive types with <code>make_recursive_variant</code></title>
  187. <para>For some applications of recursive <code>variant</code> types, a user
  188. may be able to sacrifice the full flexibility of using
  189. <code>recursive_wrapper</code> with <code>variant</code> for the following
  190. convenient syntax:
  191. <programlisting>typedef <classname>boost::make_recursive_variant</classname>&lt;
  192. int
  193. , std::vector&lt; boost::recursive_variant_ &gt;
  194. &gt;::type int_tree_t;</programlisting>
  195. </para>
  196. <para>Use of the resultant <code>variant</code> type is as expected:
  197. <programlisting>std::vector&lt; int_tree_t &gt; subresult;
  198. subresult.push_back(3);
  199. subresult.push_back(5);
  200. std::vector&lt; int_tree_t &gt; result;
  201. result.push_back(1);
  202. result.push_back(subresult);
  203. result.push_back(7);
  204. int_tree_t var(result);</programlisting>
  205. </para>
  206. <para>To be clear, one might represent the resultant content of
  207. <code>var</code> as <code>( 1 ( 3 5 ) 7 )</code>.</para>
  208. <para>Finally, note that a type sequence can be used to specify the bounded
  209. types of a recursive <code>variant</code> via the use of
  210. <code><classname>boost::make_recursive_variant_over</classname></code>,
  211. whose semantics are the same as <code>make_variant_over</code> (which is
  212. described in <xref linkend="variant.tutorial.over-sequence"/>).</para>
  213. <para><emphasis role="bold">Portability</emphasis>: Unfortunately, due to
  214. standard conformance issues in several compilers,
  215. <code>make_recursive_variant</code> is not universally supported. On these
  216. compilers the library indicates its lack of support via the definition
  217. of the preprocessor symbol
  218. <code><macroname>BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT</macroname></code>.
  219. Thus, unless working with highly-conformant compilers, maximum portability
  220. will be achieved by instead using <code>recursive_wrapper</code>, as
  221. described in
  222. <xref linkend="variant.tutorial.recursive.recursive-wrapper"/>.</para>
  223. </section>
  224. </section> <!--/tutorial.recursive-->
  225. <section id="variant.tutorial.binary-visitation">
  226. <title>Binary visitation</title>
  227. <para>As the tutorial above demonstrates, visitation is a powerful mechanism
  228. for manipulating <code>variant</code> content. Binary visitation further
  229. extends the power and flexibility of visitation by allowing simultaneous
  230. visitation of the content of two different <code>variant</code>
  231. objects.</para>
  232. <para>Notably this feature requires that binary visitors are incompatible
  233. with the visitor objects discussed in the tutorial above, as they must
  234. operate on two arguments. The following demonstrates the implementation of
  235. a binary visitor:
  236. <programlisting>class are_strict_equals
  237. : public <classname>boost::static_visitor</classname>&lt;bool&gt;
  238. {
  239. public:
  240. template &lt;typename T, typename U&gt;
  241. bool operator()( const T &amp;, const U &amp; ) const
  242. {
  243. return false; // cannot compare different types
  244. }
  245. template &lt;typename T&gt;
  246. bool operator()( const T &amp; lhs, const T &amp; rhs ) const
  247. {
  248. return lhs == rhs;
  249. }
  250. };</programlisting>
  251. </para>
  252. <para>As expected, the visitor is applied to two <code>variant</code>
  253. arguments by means of <code>apply_visitor</code>:
  254. <programlisting><classname>boost::variant</classname>&lt; int, std::string &gt; v1( "hello" );
  255. <classname>boost::variant</classname>&lt; double, std::string &gt; v2( "hello" );
  256. assert( <functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v2) );
  257. <classname>boost::variant</classname>&lt; int, const char * &gt; v3( "hello" );
  258. assert( !<functionname>boost::apply_visitor</functionname>(are_strict_equals(), v1, v3) );</programlisting>
  259. </para>
  260. <para>Finally, we must note that the function object returned from the
  261. &quot;delayed&quot; form of
  262. <code><functionname>apply_visitor</functionname></code> also supports
  263. binary visitation, as the following demonstrates:
  264. <programlisting>typedef <classname>boost::variant</classname>&lt;double, std::string&gt; my_variant;
  265. std::vector&lt; my_variant &gt; seq1;
  266. seq1.push_back("pi is close to ");
  267. seq1.push_back(3.14);
  268. std::list&lt; my_variant &gt; seq2;
  269. seq2.push_back("pi is close to ");
  270. seq2.push_back(3.14);
  271. are_strict_equals visitor;
  272. assert( std::equal(
  273. seq1.begin(), seq1.end(), seq2.begin()
  274. , <functionname>boost::apply_visitor</functionname>( visitor )
  275. ) );</programlisting>
  276. </para>
  277. </section>
  278. <section id="variant.tutorial.multi-visitation">
  279. <title>Multi visitation</title>
  280. <para>Multi visitation extends the power and flexibility of visitation by allowing simultaneous
  281. visitation of the content of three and more different <code>variant</code>
  282. objects. Note that header for multi visitors shall be included separately.</para>
  283. <para>Notably this feature requires that multi visitors are incompatible
  284. with the visitor objects discussed in the tutorial above, as they must
  285. operate on same amout of arguments that was passed to <code>apply_visitor</code>.
  286. The following demonstrates the implementation of a multi visitor for three parameters:
  287. <programlisting>
  288. #include &lt;boost/variant/multivisitors.hpp&gt;
  289. typedef <classname>boost::variant</classname>&lt;int, double, bool&gt; bool_like_t;
  290. typedef <classname>boost::variant</classname>&lt;int, double&gt; arithmetics_t;
  291. struct if_visitor: public <classname>boost::static_visitor</classname>&lt;arithmetics_t&gt; {
  292. template &lt;class T1, class T2&gt;
  293. arithmetics_t operator()(bool b, T1 v1, T2 v2) const {
  294. if (b) {
  295. return v1;
  296. } else {
  297. return v2;
  298. }
  299. }
  300. };
  301. </programlisting>
  302. </para>
  303. <para>As expected, the visitor is applied to three <code>variant</code>
  304. arguments by means of <code>apply_visitor</code>:
  305. <programlisting>
  306. bool_like_t v0(true), v1(1), v2(2.0);
  307. assert(
  308. <functionname>boost::apply_visitor</functionname>(if_visitor(), v0, v1, v2)
  309. ==
  310. arithmetics_t(1)
  311. );
  312. </programlisting>
  313. </para>
  314. <para>Finally, we must note that multi visitation does not support
  315. &quot;delayed&quot; form of
  316. <code><functionname>apply_visitor</functionname> if
  317. <macroname>BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES</macroname> is defined</code>.
  318. </para>
  319. </section>
  320. </section>