215 lines
8.3 KiB
XML
215 lines
8.3 KiB
XML
<?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">
|
|
<!--
|
|
Copyright 2003, Eric Friedman, Itay Maman.
|
|
|
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
-->
|
|
<section id="variant.tutorial.basic">
|
|
<title>Basic Usage</title>
|
|
|
|
<using-namespace name="boost"/>
|
|
<using-class name="boost::variant"/>
|
|
|
|
<para>A discriminated union container on some set of types is defined by
|
|
instantiating the <code><classname>boost::variant</classname></code> class
|
|
template with the desired types. These types are called
|
|
<emphasis role="bold">bounded types</emphasis> and are subject to the
|
|
requirements of the
|
|
<link linkend="variant.concepts.bounded-type"><emphasis>BoundedType</emphasis></link>
|
|
concept. Any number of bounded types may be specified, up to some
|
|
implementation-defined limit (see
|
|
<code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>).</para>
|
|
|
|
<para>For example, the following declares a discriminated union container on
|
|
<code>int</code> and <code>std::string</code>:
|
|
|
|
<programlisting><classname>boost::variant</classname>< int, std::string > v;</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>By default, a <code>variant</code> default-constructs its first
|
|
bounded type, so <code>v</code> initially contains <code>int(0)</code>. If
|
|
this is not desired, or if the first bounded type is not
|
|
default-constructible, a <code>variant</code> can be constructed
|
|
directly from any value convertible to one of its bounded types. Similarly,
|
|
a <code>variant</code> can be assigned any value convertible to one of its
|
|
bounded types, as demonstrated in the following:
|
|
|
|
<programlisting>v = "hello";</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>Now <code>v</code> contains a <code>std::string</code> equal to
|
|
<code>"hello"</code>. We can demonstrate this by
|
|
<emphasis role="bold">streaming</emphasis> <code>v</code> to standard
|
|
output:
|
|
|
|
<programlisting>std::cout << v << std::endl;</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>Usually though, we would like to do more with the content of a
|
|
<code>variant</code> than streaming. Thus, we need some way to access the
|
|
contained value. There are two ways to accomplish this:
|
|
<code><functionname>apply_visitor</functionname></code>, which is safest
|
|
and very powerful, and
|
|
<code><functionname>get</functionname><T></code>, which is
|
|
sometimes more convenient to use.</para>
|
|
|
|
<para>For instance, suppose we wanted to concatenate to the string contained
|
|
in <code>v</code>. With <emphasis role="bold">value retrieval</emphasis>
|
|
by <code><functionname>get</functionname></code>, this may be accomplished
|
|
quite simply, as seen in the following:
|
|
|
|
<programlisting>std::string& str = <functionname>boost::get</functionname><std::string>(v);
|
|
str += " world! ";</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>As desired, the <code>std::string</code> contained by <code>v</code> now
|
|
is equal to <code>"hello world! "</code>. Again, we can demonstrate this by
|
|
streaming <code>v</code> to standard output:
|
|
|
|
<programlisting>std::cout << v << std::endl;</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>While use of <code>get</code> is perfectly acceptable in this trivial
|
|
example, <code>get</code> generally suffers from several significant
|
|
shortcomings. For instance, if we were to write a function accepting a
|
|
<code>variant<int, std::string></code>, we would not know whether
|
|
the passed <code>variant</code> contained an <code>int</code> or a
|
|
<code>std::string</code>. If we insisted upon continued use of
|
|
<code>get</code>, we would need to query the <code>variant</code> for its
|
|
contained type. The following function, which "doubles" the
|
|
content of the given <code>variant</code>, demonstrates this approach:
|
|
|
|
<programlisting>void times_two( boost::variant< int, std::string > & operand )
|
|
{
|
|
if ( int* pi = <functionname>boost::get</functionname><int>( &operand ) )
|
|
*pi *= 2;
|
|
else if ( std::string* pstr = <functionname>boost::get</functionname><std::string>( &operand ) )
|
|
*pstr += *pstr;
|
|
}</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>However, such code is quite brittle, and without careful attention will
|
|
likely lead to the introduction of subtle logical errors detectable only at
|
|
runtime. For instance, consider if we wished to extend
|
|
<code>times_two</code> to operate on a <code>variant</code> with additional
|
|
bounded types. Specifically, let's add
|
|
<code>std::complex<double></code> to the set. Clearly, we would need
|
|
to at least change the function declaration:
|
|
|
|
<programlisting>void times_two( boost::variant< int, std::string, std::complex<double> > & operand )
|
|
{
|
|
// as above...?
|
|
}</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>Of course, additional changes are required, for currently if the passed
|
|
<code>variant</code> in fact contained a <code>std::complex</code> value,
|
|
<code>times_two</code> would silently return -- without any of the desired
|
|
side-effects and without any error. In this case, the fix is obvious. But in
|
|
more complicated programs, it could take considerable time to identify and
|
|
locate the error in the first place.</para>
|
|
|
|
<para>Thus, real-world use of <code>variant</code> typically demands an access
|
|
mechanism more robust than <code>get</code>. For this reason,
|
|
<code>variant</code> supports compile-time checked
|
|
<emphasis role="bold">visitation</emphasis> via
|
|
<code><functionname>apply_visitor</functionname></code>. Visitation requires
|
|
that the programmer explicitly handle (or ignore) each bounded type. Failure
|
|
to do so results in a compile-time error.</para>
|
|
|
|
<para>Visitation of a <code>variant</code> requires a visitor object. The
|
|
following demonstrates one such implementation of a visitor implementating
|
|
behavior identical to <code>times_two</code>:
|
|
|
|
<programlisting>class times_two_visitor
|
|
: public <classname>boost::static_visitor</classname><>
|
|
{
|
|
public:
|
|
|
|
void operator()(int & i) const
|
|
{
|
|
i *= 2;
|
|
}
|
|
|
|
void operator()(std::string & str) const
|
|
{
|
|
str += str;
|
|
}
|
|
|
|
};</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>With the implementation of the above visitor, we can then apply it to
|
|
<code>v</code>, as seen in the following:
|
|
|
|
<programlisting><functionname>boost::apply_visitor</functionname>( times_two_visitor(), v );</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>As expected, the content of <code>v</code> is now a
|
|
<code>std::string</code> equal to <code>"hello world! hello world! "</code>.
|
|
(We'll skip the verification this time.)</para>
|
|
|
|
<para>In addition to enhanced robustness, visitation provides another
|
|
important advantage over <code>get</code>: the ability to write generic
|
|
visitors. For instance, the following visitor will "double" the
|
|
content of <emphasis>any</emphasis> <code>variant</code> (provided its
|
|
bounded types each support operator+=):
|
|
|
|
<programlisting>class times_two_generic
|
|
: public <classname>boost::static_visitor</classname><>
|
|
{
|
|
public:
|
|
|
|
template <typename T>
|
|
void operator()( T & operand ) const
|
|
{
|
|
operand += operand;
|
|
}
|
|
|
|
};</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>Again, <code>apply_visitor</code> sets the wheels in motion:
|
|
|
|
<programlisting><functionname>boost::apply_visitor</functionname>( times_two_generic(), v );</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>While the initial setup costs of visitation may exceed that required for
|
|
<code>get</code>, the benefits quickly become significant. Before concluding
|
|
this section, we should explore one last benefit of visitation with
|
|
<code>apply_visitor</code>:
|
|
<emphasis role="bold">delayed visitation</emphasis>. Namely, a special form
|
|
of <code>apply_visitor</code> is available that does not immediately apply
|
|
the given visitor to any <code>variant</code> but rather returns a function
|
|
object that operates on any <code>variant</code> given to it. This behavior
|
|
is particularly useful when operating on sequences of <code>variant</code>
|
|
type, as the following demonstrates:
|
|
|
|
<programlisting>std::vector< <classname>boost::variant</classname><int, std::string> > vec;
|
|
vec.push_back( 21 );
|
|
vec.push_back( "hello " );
|
|
|
|
times_two_generic visitor;
|
|
std::for_each(
|
|
vec.begin(), vec.end()
|
|
, <functionname>boost::apply_visitor</functionname>(visitor)
|
|
);</programlisting>
|
|
|
|
</para>
|
|
|
|
</section>
|