mirror of
https://github.com/boostorg/variant.git
synced 2026-01-19 04:42:16 +00:00
500 lines
19 KiB
HTML
500 lines
19 KiB
HTML
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
|
||
<head>
|
||
<meta http-equiv="Content-Language" content="en-us">
|
||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||
<meta name="keywords" content="Variant, design pattern, generic programming, C++">
|
||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||
<title>Boost::variant</title>
|
||
</head>
|
||
|
||
<body bgcolor="#FFFFFF" link="#0000FF" vlink="#800080">
|
||
|
||
<table summary="header" border="0" cellpadding="7" cellspacing="0" width="100%">
|
||
<tr>
|
||
<td valign="top" width="300">
|
||
<h3><a HREF="../../../index.htm">
|
||
<img src="../../../c++boost.gif" alt="C++ Boost" width="277" height="86" BORDER="0">
|
||
</a></h3>
|
||
</td>
|
||
<td valign="top">
|
||
<h1 align="center"><a href="index.html">boost::variant</a></h1>
|
||
<h2 align="center">Tutorial</h2>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<hr>
|
||
<p></p>
|
||
<ul>
|
||
<li><a href="#FirstVariantProgram">First variant program</a></li>
|
||
<li><a href="#Instantiation">Instantiation</a></li>
|
||
<li><a href="#ValueSemantics">Value semantics</a></li>
|
||
<li><a href="#FunctorBasedVisitation">Functor-based visitation</a></li>
|
||
</ul>
|
||
<hr>
|
||
<p>The following sample program illustrates a typical usage of <code>variant</code>.
|
||
Additional sample programs may be found <a href="sample.html">here</a>.<br>
|
||
</p>
|
||
<h2><a name="FirstVariantProgram">First <code>variant</code> program</a></h2>
|
||
<p>Let's suppose you need to implement an object factory function, <code>create</code>.
|
||
Based on an integer argument to the function, <code>create</code> must
|
||
construct and return an object of one of the following types: <code>std::string</code>,
|
||
<code>char*</code>, and <code>int</code>. The relationship between the integer
|
||
argument, <code>i</code>, and the returned type is specified as follows:<br>
|
||
</p>
|
||
<pre> 2000 < i -> char*
|
||
1000 < i <= 2000 -> std::string
|
||
<i>else </i> -> int
|
||
</pre>
|
||
<p>Typical implementations would probably designate of a virtual base class,
|
||
returning objects of the derived types through a pointer to the base class.
|
||
However, since none of the desired types may derive from the base class, the
|
||
programmer would need to write "adapter" classes derived from the common base
|
||
to supply the semantics of the various concrete types (i.e, <code>std::string</code>,
|
||
<code>int</code>, and <code>char*</code>). Clients of the factory function
|
||
would then check the result using <code>dynamic_cast</code> to determine which
|
||
concrete type had in fact been created.</p>
|
||
<p>Other implementations might leverage the Boost <code>
|
||
<a href="../../any/index.html">any</a></code> class, thus avoiding the hassle
|
||
of defining and implementing the virtual base class and the derived adapter
|
||
classes. However, this solution still does not avoid the problem of needing to
|
||
manually check the result using <code>any_cast</code>.</p>
|
||
<p>On the other hand, by returning an object of type <code>variant< std::string,
|
||
char*, int ></code> from the factory function, the resultant code is clear and
|
||
straight-forward: </p>
|
||
<pre>#include <iostream>
|
||
#include <string>
|
||
#include "boost/variant.hpp"
|
||
#include "boost/apply_visitor.hpp"
|
||
|
||
using std::string;
|
||
using boost::variant;
|
||
|
||
typedef variant<string, char*, int> create_result;
|
||
|
||
//
|
||
// <i>create</i>, our factory function
|
||
//
|
||
create_result create(int i)
|
||
{
|
||
// Return char* if (i > 2000):
|
||
if (which > 2000) return "JKLM";
|
||
|
||
// Return std::string if (1000 < i <= 2000):
|
||
if (which > 1000) return string("QRST");
|
||
|
||
// Otherwise, return int:
|
||
return 15;
|
||
}
|
||
|
||
//
|
||
// <i>printer</i>, a visitor that prints to std::cout
|
||
//
|
||
struct printer : boost::static_visitor<>
|
||
{
|
||
template <typename T>
|
||
void operator()(const T& t)
|
||
{
|
||
std::cout << "operand: " << t << std::endl;
|
||
}
|
||
};
|
||
|
||
|
||
int main(int , char* [] )
|
||
{
|
||
printer print;
|
||
create_result result;
|
||
|
||
// First, create a char*...
|
||
result = create(2500);
|
||
|
||
// ...and print result by applying print visitor to it:
|
||
boost::apply_visitor(a_printer, inst); // Output: "operand: JKLM"
|
||
|
||
// Now, create a std::string and print it:
|
||
result = create(1500);
|
||
boost::apply_visitor(a_printer, inst); // Output: "operand: QRST"
|
||
|
||
// Finally, create an int and print it:
|
||
result = create(5);
|
||
boost::apply_visitor(a_printer, inst); // Output: "15"
|
||
|
||
return 0;
|
||
}
|
||
</pre>
|
||
<hr>
|
||
<h2><a name="Instantiation">Instantiation</a></h2>
|
||
<p>A concrete <code>variant</code> instantiation has this general form: </p>
|
||
<pre>typedef boost::variant<T1, T2, ... TN> variant_type;
|
||
</pre>
|
||
<p>Where the types <code>T1, T2, ... TN</code> must model the
|
||
<a href="reference.html#BoundedTypes">BoundedType</a> concept. An instance of
|
||
<code>variant_type</code> is capable of holding a value of any of these types.</p>
|
||
<h3>Examples: </h3>
|
||
<pre> boost::variant<int, char, double> v1; // OK
|
||
|
||
variant<int, char, double, char> v2; // Error: char appears twice
|
||
variant<float> v3; // Error: At least two types must be specified
|
||
variant<char, char* const, void*> v4; // Error: top-level const types are illegal
|
||
|
||
variant<char, const char*, void*> v5; // OK - const char* is not a top-level const type
|
||
</pre>
|
||
<p>If one of these types is incomplete at the instantiation point, it must be
|
||
enclosed inside an <code>incomplete<></code> wrapper, as shown below:<br>
|
||
</p>
|
||
<pre> struct jas1; // Forward declaration of jas1 (Just Another Struct)
|
||
|
||
variant<jas1, double, std::string> v6; // Error: jas1 is incomplete
|
||
struct jas1 { ... };
|
||
|
||
struct jas2; // Forward declaration of jas2
|
||
variant<incomplete<jas2>, double, std::string> v7; // OK -
|
||
// incomplete<> is used for incomplete types
|
||
struct jas2 { ... };
|
||
</pre>
|
||
<hr>
|
||
<h2><a name="ValueSemantics">Value semantics</a></h2>
|
||
<p>Once a <code>variant</code> object has been created its value may be changed
|
||
by <code>variant</code>'s assingment operator. The right-hand side value of the
|
||
assignment is converted to the closet possible type of the assigned variant's
|
||
<a href="reference.html#SetOfTypes">set of types</a>, by using overload
|
||
resolution rules. Naturally, If there is an exact match, no convesion is
|
||
applied, and the right-hand side value is copied as-is. On the other hand, if
|
||
no conversion exists, or, if the conversion is ambiguous, a compiler error is
|
||
triggered.</p>
|
||
<p>The assignment rules (mentioned above) also apply when a variant object is
|
||
initalized. Hence, the rest of this section will refer to assignment and
|
||
initialization interchangeably. </p>
|
||
<pre> variant<int, char, double> v1;
|
||
v1 = 5.9; // double -> double (no conversion is needed)
|
||
v1 = 3; // int -> int - " -
|
||
v1 = 'x'; // char -> char - " -
|
||
|
||
v1 = short(5); // short -> int
|
||
v1 = 5.9f; // float -> double
|
||
v1 = static_cast<unsigned char>('x'); // unsigned char -> int
|
||
|
||
|
||
v1 = string("abc"); // Error! no implicit conversion from
|
||
// string to int/char/double
|
||
|
||
v1 = static_cast<long double>(4.0); // Error! (ambiguity):
|
||
// long double -> double conversion
|
||
// clashes with long double -> int conversion
|
||
|
||
|
||
variant<std::string, double, int> v2; // Default construction.
|
||
// Use default value of first type,
|
||
// i.e: v2 = std::string()
|
||
|
||
|
||
struct class1
|
||
{
|
||
class1(const char* s = 0) { .. }
|
||
..
|
||
};
|
||
|
||
struct class2
|
||
{
|
||
class2(const char* s) { .. }
|
||
..
|
||
};
|
||
|
||
variant<class1, double> v3;
|
||
v3 = 5; // int -> double
|
||
v3 = class1("abc"); // class1 -> class1
|
||
v3 = "abc"; // const char* -> class1
|
||
|
||
variant<class1, class2> v4;
|
||
v4 = class1("text1"); // class1 -> class1
|
||
v4 = class2("text2"); // class2 -> class2
|
||
v4 = "text3"; // Error! (ambiguity):
|
||
// class1 clashes with class2
|
||
</pre>
|
||
<p>Copy assignment: When a variant object is assigned to another variant object
|
||
- which is of the same concrete type - the assignee becomes an exact duplicate
|
||
of the assigned variant: </p>
|
||
<pre> variant<int, char, double> v1, v2;
|
||
v1 = 5.9; // v1 = double(5.9)
|
||
v2 = v1; // v2 = double(5.9)
|
||
|
||
v1 = 3; // v1 = int(3)
|
||
v2 = v1; // v2 = int(3)
|
||
|
||
v1 = short(5); // short -> int, hence: v1 = int(5)
|
||
v2 = v1; // v2 = int(5)
|
||
|
||
v1 = 5.7f; // float -> double, hence: v1 = double(5.7)
|
||
v2 = v1; // v2 = double(5.7)
|
||
</pre>
|
||
<p>Variant-to-variant assignment: Consider this case: </p>
|
||
<pre> typedef variant<RR, SS, TT, .. > rhs_type;
|
||
rhs_type src;
|
||
|
||
typedef variant<XX, YY, ZZ, .. > lhs_type;
|
||
lhs_type trg;
|
||
|
||
trg = src; // Variant to variant assignment:
|
||
// trg and src are two variants of different types
|
||
|
||
</pre>
|
||
<p>What will <code>trg</code>'s value be, following such an assignment?<br>
|
||
</p>
|
||
<ul>
|
||
<li>Case 1: If <code>rhs_type</code> is not part of <code>lhs_type</code>'s
|
||
<a href="reference.html#SetOfTypes">set of types</a>, then <code>src</code>'s
|
||
value is assigned to <code>trg</code>, using the rules discussed throughout
|
||
this section. </li>
|
||
<li>Case 2: If <code>rhs_type</code> <b>does</b> appear on <code>lhs_type</code>'s
|
||
set of types, then <code>src</code> itself will be assigned into <code>trg</code>,
|
||
turning <code>trg</code> into a <b>variant holding a variant</b> </li>
|
||
</ul>
|
||
<pre> typedef variant<int, std::string> variant_type;
|
||
variant_type a;
|
||
|
||
variant<int, double, std::string> b;
|
||
variant<variant_type, int, double, std::string> c;
|
||
|
||
a = "hello"; // char* converted to std::string
|
||
|
||
b = a; // b <- value of a, so b holds an std::string (Case 1)
|
||
|
||
c = a; // c <- a, so c holds a variant_type object holding
|
||
// an std::string (Case 2)
|
||
</pre>
|
||
<p>Note that a variant-to-variant assignment will fail - at compile time - if
|
||
one of <code>src</code>'s bounded types cannot be assigned to <code>trg</code>
|
||
(due to ambiguity/lack of conversion).<br>
|
||
For instance: </p>
|
||
<pre> variant<int, std::string> a;
|
||
variant<int, const char*> b;
|
||
|
||
a = b; // OK: int->int or const char* -> std::string
|
||
|
||
b = a; // Error!
|
||
// std::string is not implicitly convertible to either int
|
||
// or const char*, so the compiler will break on this line
|
||
</pre>
|
||
<hr>
|
||
<h2><a name="FunctorBasedVisitation">Functor-based visitation</a></h2>
|
||
<p>The visitation facility, implemented via <a href="reference.html#Visitation">
|
||
<code>apply_visitor()</code></a>, is the primary mechanism thru which client
|
||
code can gain access to a variant-held value.<br>
|
||
<br>
|
||
Let us consider the following piece of code. </p>
|
||
<pre> boost::variant<XX, YY, ZZ, ... > a_variant;
|
||
|
||
struct visitor_type { ... };
|
||
visitor_type visitor;
|
||
|
||
boost::apply_visitor(visitor, a_variant)
|
||
</pre>
|
||
<p>In this sample, <code>visitor_type</code> must be a
|
||
<a href="reference.html#Visitor">Visitor</a> of <code>a_variant</code>. These
|
||
requirements may be informally defined as: <code>visitor_type</code> must be a
|
||
unary function object that is capable of accepting a value of any of <code>
|
||
a_variant</code>'s <a href="reference.html#BoundedTypes">BoundedTypes</a>. If
|
||
<code>visitor_type</code> fails to meet these requirements, (i.e: it cannot
|
||
accept one, or more, of the types in <code>a_variant</code>'s set of types) a
|
||
compiler error is triggered.</p>
|
||
<p>The 'visit' begins when <code>apply_visitor()</code> is called: <code>
|
||
apply_visitor(visitor, a_variant)</code> passes <code>a_variant</code>'s
|
||
currently held value to <code>visitor</code>. From <code>visitor</code>'s
|
||
standpoint, its function-call operator is called with whatever value <code>
|
||
a_variant</code> is holding. This gives <code>visitor</code> a chacne to
|
||
inspect/modify <code>a_variant</code>'s value.</p>
|
||
<p>The following snip demonstrates the visitation facility using a concrete
|
||
visitor class, <code>print_int_char_visitor</code>. The code will print "Char
|
||
value: 'x'" and "Int value: 77" to <code>std::cout</code>:</p>
|
||
<pre> struct print_int_float_visitor
|
||
{
|
||
//result_type specifies the return type of operator()
|
||
typedef void result_type;
|
||
|
||
// Handle int values
|
||
void operator()(int x) const
|
||
{
|
||
std::cout << "Int value: " << x << std::endl;
|
||
}
|
||
|
||
// Handle char values
|
||
void operator()(float x) const
|
||
{
|
||
std::cout << "Float value: " << x << std::endl;
|
||
}
|
||
};
|
||
.
|
||
.
|
||
variant<int, float> var = 53.22f;
|
||
|
||
//Apply print_int_char_visitor to var.
|
||
apply_visitor(print_int_float_visitor(), var); // Output: "Float value: 53.22"
|
||
|
||
var = 77;
|
||
apply_visitor(print_int_float_visitor(), var); // Output: "Int value: 77"
|
||
</pre>
|
||
<p>Note how <code>print_int_char_visitor</code> specifies the type of its return
|
||
value thru a nested type - <code>result_type</code>. This return value is also
|
||
the value that is returned by <code>boost::apply_visitor()</code> to its
|
||
caller.</p>
|
||
<p>The next code snip, demonstrate how <code>boost::static_visitor<T></code> can
|
||
be used as a base class to supply <code>result_type</code>'s definition. The
|
||
visitor in this snip, <code>multiply_by_two</code> multiplies its operand by
|
||
two and returns the result as an <code>int</code> value. </p>
|
||
<pre> struct multiply_by_two
|
||
: boost::static_visitor<int> // Return type is int,
|
||
// so derive from static_visitor<int>
|
||
{
|
||
template<typename T>
|
||
int operator()(T t) const
|
||
{
|
||
return int(2 * t);
|
||
}
|
||
};
|
||
.
|
||
.
|
||
variant<int, short, char> a = 9;
|
||
int result = apply_visitor(multiply_by_two(), a);
|
||
std::cout << "Result = " << result << std::endl; //Output: "Result = 18"
|
||
</pre>
|
||
<p>If a visitor offers several overloads of <code>operator()</code>, overload
|
||
resolution rules are applied to choose the correct form. If a visitor cannot
|
||
accept one of the types a variant may hold (due to ambiguity/lack of
|
||
conversion), a comiler error is generated. <br>
|
||
</p>
|
||
<pre> struct print_int_char : boost::static_visitor<void>
|
||
{
|
||
// Handler *A*
|
||
void operator()(int f) const
|
||
{
|
||
std::cout << "Int value: " << f << std::endl;
|
||
}
|
||
|
||
// Handler *B*
|
||
void operator()(char c) const
|
||
{
|
||
std::cout << "Char value: '" << c << '\''
|
||
<< std::endl;
|
||
}
|
||
};
|
||
.
|
||
.
|
||
variant<short, char> a = static_cast<short>(88);
|
||
apply_visitor(print_int_char(), a); // Output: "Int value: 88.0"
|
||
// (int value intercepted by handler *A*)
|
||
|
||
a = 'x'; // a = 'x'
|
||
apply_visitor(print_int_char(), a); // Output: "Char value: 'x'"
|
||
|
||
variant<int, char, void*> b = 88; // b = int(88)
|
||
apply_visitor(print_int_char(), b); // Error! -
|
||
// void* cannot be handled by neither
|
||
// handler (*A* or *B*)
|
||
</pre>
|
||
<p>"Catch-all" behavior can be achieved by supplying a templated form of <code>
|
||
operator(): </code></p>
|
||
<pre> struct ignore_non_ints_visitor : boost::static_visitor<void>
|
||
{
|
||
void operator()(int t) const
|
||
{
|
||
std::cout << "Current value: " << t << std::endl;
|
||
}
|
||
|
||
template<typename T>
|
||
void operator()(const T& t) const
|
||
{
|
||
// Catch all other types:
|
||
std::cout << "Ignore me" << std::endl;
|
||
}
|
||
};
|
||
.
|
||
.
|
||
variant<int, double, std::string> a = "abcde";
|
||
apply_visitor(ignore_non_ints_visitor(), a); //Output: "Ignore me"
|
||
|
||
a = 22.24;
|
||
apply_visitor(ignore_non_ints_visitor(), a); //Output: "Ignore me"
|
||
|
||
a = 8;
|
||
apply_visitor(ignore_non_ints_visitor(), a); //Output: "Current value: 8"
|
||
</pre>
|
||
<p>A visitor may accept its operand using a "pass-by-reference" parameter
|
||
passing scheme. This allows a visitor to mutate the variant-held value: </p>
|
||
<pre> struct first_capital : boost::static_visitor<void>
|
||
{
|
||
void operator()(std::string& str) const
|
||
{
|
||
str[0] = toupper(str[0]);
|
||
}
|
||
|
||
void operator()(char* str) const
|
||
{
|
||
*str = toupper(*str);
|
||
}
|
||
};
|
||
|
||
struct printer : boost::static_visitor<void>
|
||
{
|
||
template<typename T>
|
||
void operator()(const T& t) const
|
||
{
|
||
std::cout << t << std::endl;
|
||
}
|
||
};
|
||
.
|
||
.
|
||
variant<std::string, char*> a = std::string("abcde");
|
||
apply_visitor(printer(), a); //Output: "abcde"
|
||
|
||
apply_visitor(first_capital(), a); //Invoke the mutating visitor
|
||
//(capitalizes the first letter)
|
||
|
||
apply_visitor(printer(), a); //Output: "Abcde"
|
||
</pre>
|
||
<p>The last sample shows persistency of visitors. Every visitor is actually a
|
||
function object, so its data members can be used to keep persistent data. The
|
||
<code>int_accumulator</code> visitor uses <code>total_</code> to keep track of
|
||
the total sum of its operands: </p>
|
||
<pre> struct int_accumulator : boost::static_visitor<void>
|
||
{
|
||
int_accumulator() : total_(0) { }
|
||
|
||
void operator()(int x)
|
||
{
|
||
total_ += x;
|
||
}
|
||
|
||
int total_;
|
||
} ;
|
||
.
|
||
.
|
||
int_accumulator adder;
|
||
variant<int, short, char> a = 15;
|
||
apply_visitor(adder, a); //adder.total_ += 15
|
||
|
||
a = short(9);
|
||
apply_visitor(adder, a); //adder.total_ += 9
|
||
|
||
std::cout << "Total = " << adder.total_ << std::endl; //Output: "Total = 24"
|
||
</pre>
|
||
<p><b>Note:</b> When <code>boost::apply_visitor()</code> is used, the compiler
|
||
verifies that the specified visitor type is a valid visitor with respect to the
|
||
relevant variant type. If the visitor fails to meet the
|
||
<a href="reference.html#Visitor">Visitor</a> requirements, a compiler error is
|
||
fired. This allows programming errors to be detected at compile-time rather
|
||
than run-time, thus enhancing code safety and stability.</p>
|
||
<hr>
|
||
<p>Revised 14 February 2003</p>
|
||
<p><i><EFBFBD> Copyright Eric Friedman and Itay Maman 2002-2003. All rights reserved.</i></p>
|
||
<p>Permission to use, copy, modify, distribute and sell this software and its
|
||
documentation for any purpose is hereby granted without fee, provided that the
|
||
above copyright notice appear in all copies and that both that copyright notice
|
||
and this permission notice appear in supporting documentation. Eric Friedman
|
||
and Itay Maman make no representations about the suitability of this software
|
||
for any purpose. It is provided "as is" without express or implied warranty.</p>
|
||
|
||
</body>
|
||
|
||
</html>
|