Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/variant/doc/tutorial/basic.xml @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 8.1 KB
Line 
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.basic">
5  <title>Basic Usage</title>
6
7<using-namespace name="boost"/>
8<using-class name="boost::variant"/>
9
10<para>A discriminated union container on some set of types is defined by
11  instantiating the <code><classname>boost::variant</classname></code> class
12  template with the desired types. These types are called
13  <emphasis role="bold">bounded types</emphasis> and are subject to the
14  requirements of the
15  <link linkend="variant.concepts.bounded-type"><emphasis>BoundedType</emphasis></link>
16  concept. Any number of bounded types may be specified, up to some
17  implementation-defined limit (see
18  <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>).</para>
19
20<para>For example, the following declares a discriminated union container on
21  <code>int</code> and <code>std::string</code>:
22
23<programlisting><classname>boost::variant</classname>&lt; int, std::string &gt; v;</programlisting>
24
25</para>
26
27<para>By default, a <code>variant</code> default-constructs its first
28  bounded type, so <code>v</code> initially contains <code>int(0)</code>. If
29  this is not desired, or if the first bounded type is not
30  default-constructible, a <code>variant</code> can be constructed
31  directly from any value convertible to one of its bounded types. Similarly,
32  a <code>variant</code> can be assigned any value convertible to one of its
33  bounded types, as demonstrated in the following:
34
35<programlisting>v = "hello";</programlisting>
36
37</para>
38
39<para>Now <code>v</code> contains a <code>std::string</code> equal to
40  <code>"hello"</code>. We can demonstrate this by
41  <emphasis role="bold">streaming</emphasis>&nbsp;<code>v</code> to standard
42  output:
43
44<programlisting>std::cout &lt;&lt; v &lt;&lt; std::endl;</programlisting>
45
46</para>
47
48<para>Usually though, we would like to do more with the content of a
49  <code>variant</code> than streaming. Thus, we need some way to access the
50  contained value. There are two ways to accomplish this:
51  <code><functionname>apply_visitor</functionname></code>, which is safest
52  and very powerful, and
53  <code><functionname>get</functionname>&lt;T&gt;</code>, which is
54  sometimes more convenient to use.</para>
55
56<para>For instance, suppose we wanted to concatenate to the string contained
57  in <code>v</code>. With <emphasis role="bold">value retrieval</emphasis>
58  by <code><functionname>get</functionname></code>, this may be accomplished
59  quite simply, as seen in the following:
60
61<programlisting>std::string&amp; str = <functionname>boost::get</functionname>&lt;std::string&gt;(v);
62str += " world! ";</programlisting>
63
64</para>
65
66<para>As desired, the <code>std::string</code> contained by <code>v</code> now
67  is equal to <code>"hello world! "</code>. Again, we can demonstrate this by
68  streaming <code>v</code> to standard output:
69
70<programlisting>std::cout &lt;&lt; v &lt;&lt; std::endl;</programlisting>
71
72</para>
73
74<para>While use of <code>get</code> is perfectly acceptable in this trivial
75  example, <code>get</code> generally suffers from several significant
76  shortcomings. For instance, if we were to write a function accepting a
77  <code>variant&lt;int, std::string&gt;</code>, we would not know whether
78  the passed <code>variant</code> contained an <code>int</code> or a
79  <code>std::string</code>. If we insisted upon continued use of
80  <code>get</code>, we would need to query the <code>variant</code> for its
81  contained type. The following function, which &quot;doubles&quot; the
82  content of the given <code>variant</code>, demonstrates this approach:
83
84<programlisting>void times_two( boost::variant&lt; int, std::string &gt; &amp; operand )
85{
86    if ( int* pi = <functionname>boost::get</functionname>&lt;int&gt;( &amp;v ) )
87        *pi *= 2;
88    else if ( std::string* pstr = <functionname>boost::get</functionname>&lt;std::string&gt;( &amp;v ) )
89        *pstr += *pstr;
90}</programlisting>
91
92</para>
93
94<para>However, such code is quite brittle, and without careful attention will
95  likely lead to the introduction of subtle logical errors detectable only at
96  runtime. For instance, consider if we wished to extend
97  <code>times_two</code> to operate on a <code>variant</code> with additional
98  bounded types. Specifically, let's add
99  <code>std::complex&lt;double&gt;</code> to the set. Clearly, we would need
100  to at least change the function declaration:
101
102<programlisting>void times_two( boost::variant&lt; int, std::string, std::complex&lt;double&gt; &gt; &amp; operand )
103{
104    // as above...?
105}</programlisting>
106
107</para>
108
109<para>Of course, additional changes are required, for currently if the passed
110  <code>variant</code> in fact contained a <code>std::complex</code> value,
111  <code>times_two</code> would silently return -- without any of the desired
112  side-effects and without any error. In this case, the fix is obvious. But in
113  more complicated programs, it could take considerable time to identify and
114  locate the error in the first place.</para>
115
116<para>Thus, real-world use of <code>variant</code> typically demands an access
117  mechanism more robust than <code>get</code>. For this reason,
118  <code>variant</code> supports compile-time checked
119  <emphasis role="bold">visitation</emphasis> via
120  <code><functionname>apply_visitor</functionname></code>. Visitation requires
121  that the programmer explicitly handle (or ignore) each bounded type. Failure
122  to do so results in a compile-time error.</para>
123
124<para>Visitation of a <code>variant</code> requires a visitor object. The
125  following demonstrates one such implementation of a visitor implementating
126  behavior identical to <code>times_two</code>:
127
128<programlisting>class times_two_visitor
129    : public <classname>boost::static_visitor</classname>&lt;&gt;
130{
131public:
132
133    void operator()(int &amp; i) const
134    {
135        i *= 2;
136    }
137
138    void operator()(std::string &amp; str) const
139    {
140        str += str;
141    }
142
143};</programlisting>
144
145</para>
146
147<para>With the implementation of the above visitor, we can then apply it to
148  <code>v</code>, as seen in the following:
149
150<programlisting><functionname>boost::apply_visitor</functionname>( times_two_visitor(), v );</programlisting>
151
152</para>
153
154<para>As expected, the content of <code>v</code> is now a
155  <code>std::string</code> equal to <code>"hello world! hello world! "</code>.
156  (We'll skip the verification this time.)</para>
157
158<para>In addition to enhanced robustness, visitation provides another
159  important advantage over <code>get</code>: the ability to write generic
160  visitors. For instance, the following visitor will &quot;double&quot; the
161  content of <emphasis>any</emphasis>&nbsp;<code>variant</code> (provided its
162  bounded types each support operator+=):
163
164<programlisting>class times_two_generic
165    : public <classname>boost::static_visitor</classname>&lt;&gt;
166{
167public:
168
169    template &lt;typename T&gt;
170    void operator()( T &amp; operand ) const
171    {
172        operand += operand;
173    }
174
175};</programlisting>
176
177</para>
178
179<para>Again, <code>apply_visitor</code> sets the wheels in motion:
180
181<programlisting><functionname>boost::apply_visitor</functionname>( times_two_generic(), v );</programlisting>
182
183</para>
184
185<para>While the initial setup costs of visitation may exceed that required for
186  <code>get</code>, the benefits quickly become significant. Before concluding
187  this section, we should explore one last benefit of visitation with
188  <code>apply_visitor</code>:
189  <emphasis role="bold">delayed visitation</emphasis>. Namely, a special form
190  of <code>apply_visitor</code> is available that does not immediately apply
191  the given visitor to any <code>variant</code> but rather returns a function
192  object that operates on any <code>variant</code> given to it. This behavior
193  is particularly useful when operating on sequences of <code>variant</code>
194  type, as the following demonstrates:
195
196<programlisting>std::vector&lt; <classname>boost::variant</classname>&lt;int, std::string&gt; &gt; vec;
197vec.push_back( 21 );
198vec.push_back( "hello " );
199
200times_two_generic visitor;
201std::for_each(
202      vec.begin(), vec.end()
203   , <functionname>boost::apply_visitor</functionname>(visitor)
204   );</programlisting>
205
206</para>
207
208</section>
Note: See TracBrowser for help on using the repository browser.