| 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>< int, std::string > 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> <code>v</code> to standard |
|---|
| 42 | output: |
|---|
| 43 | |
|---|
| 44 | <programlisting>std::cout << v << 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><T></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& str = <functionname>boost::get</functionname><std::string>(v); |
|---|
| 62 | str += " 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 << v << 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<int, std::string></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 "doubles" the |
|---|
| 82 | content of the given <code>variant</code>, demonstrates this approach: |
|---|
| 83 | |
|---|
| 84 | <programlisting>void times_two( boost::variant< int, std::string > & operand ) |
|---|
| 85 | { |
|---|
| 86 | if ( int* pi = <functionname>boost::get</functionname><int>( &v ) ) |
|---|
| 87 | *pi *= 2; |
|---|
| 88 | else if ( std::string* pstr = <functionname>boost::get</functionname><std::string>( &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<double></code> to the set. Clearly, we would need |
|---|
| 100 | to at least change the function declaration: |
|---|
| 101 | |
|---|
| 102 | <programlisting>void times_two( boost::variant< int, std::string, std::complex<double> > & 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><> |
|---|
| 130 | { |
|---|
| 131 | public: |
|---|
| 132 | |
|---|
| 133 | void operator()(int & i) const |
|---|
| 134 | { |
|---|
| 135 | i *= 2; |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | void operator()(std::string & 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 "double" the |
|---|
| 161 | content of <emphasis>any</emphasis> <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><> |
|---|
| 166 | { |
|---|
| 167 | public: |
|---|
| 168 | |
|---|
| 169 | template <typename T> |
|---|
| 170 | void operator()( T & 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< <classname>boost::variant</classname><int, std::string> > vec; |
|---|
| 197 | vec.push_back( 21 ); |
|---|
| 198 | vec.push_back( "hello " ); |
|---|
| 199 | |
|---|
| 200 | times_two_generic visitor; |
|---|
| 201 | std::for_each( |
|---|
| 202 | vec.begin(), vec.end() |
|---|
| 203 | , <functionname>boost::apply_visitor</functionname>(visitor) |
|---|
| 204 | );</programlisting> |
|---|
| 205 | |
|---|
| 206 | </para> |
|---|
| 207 | |
|---|
| 208 | </section> |
|---|