2
0
mirror of https://github.com/boostorg/variant.git synced 2026-01-19 04:42:16 +00:00
Files
variant/doc/sample.html
Eric Friedman bb596cae89 Migrated from Sandbox CVS.
[SVN r18578]
2003-05-28 08:05:16 +00:00

336 lines
9.7 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">Sample programs</h2>
</td>
</tr>
</table>
<hr>
<p></p>
<ul>
<li><a href="#quick">A quick example</a></li>
<li><a href="#tree">A binary tree implementation</a></li>
<li><a href="#poly">Polymorphism: Inheritance vs. Variants</a></li>
</ul>
<hr>
<h2><a name="quick">A quick example</a></h2>
<p>This program computes the total sum of numerical values of various types.</p>
<p>The code defines a <code>variant</code> object, <code>v1</code>, which can
hold a value of this set of types: <code>short, int, float</code>, and <code>
double</code>.</p>
<p><code>double_sum</code> is a visitor class: Its function-call operator, <code>
double_sum::operator()</code>, accepts a single value, and adds it to the <code>
total_</code> data member. The program uses the construct: &quot;<code>boost::apply_visitor(visitor,
variant);</code>&quot; to invoke the visitor object, <code>(ds)</code> on the
specified variant <code>(v1)</code>.</p>
<p>Naturally, <code>ds.total_</code> holds the total of all previously &quot;visited&quot;
values, and - therefore - at the bottom of <code>main()</code>, its value is: 5
+ 16 + 3.11 + 15.3 = 39.41.</p>
<pre>#include &lt;iostream&gt;
#include &quot;boost/variant.hpp&quot;
#include &quot;boost/apply_visitor.hpp&quot;
#include &quot;boost/static_visitor.hpp&quot;
struct double_sum : boost::static_visitor&lt;double&gt;
{
double_sum() : total_(0.0) { }
template&lt;class X&gt;
double operator()(X x)
{
total_ += x;
return total_;
}
double total_;
};
int main(int, char* [] )
{
double_sum ds;
boost::variant&lt;short, int, float, double&gt; v1;
v1 = short(5);
boost::apply_visitor(ds, v1); // Apply ds to v1 (1st time)
v1 = 16;
boost::apply_visitor(ds, v1); // 2nd time
v1 = 3.11f;
boost::apply_visitor(ds, v1); // 3rd
v1 = 15.3;
double total = boost::apply_visitor(ds, v1); // 4th
// Expected output: &quot;Total = 39.41&quot;
std::cout &lt;&lt; &quot;Total = &quot; &lt;&lt; total &lt;&lt; std::endl;
return 0;
}
</pre>
<hr>
<h2><a name="tree">A binary tree implementation</a></h2>
<p>This sample program shows how <code>incomplete&lt;T&gt;</code> can be used to
define recursive <code>variants</code>.<br>
The code creates a small binary tree and then performs an in-order walk thru
its nodes, producing this output: 3 4 6 10 19 20 23<br>
</p>
<pre>#include &lt;iostream&gt;
#include &quot;boost/variant.hpp&quot;
#include &quot;boost/apply_visitor.hpp&quot;
#include &quot;boost/static_visitor.hpp&quot;
#include &quot;boost/incomplete.hpp&quot;
using boost::variant;
using boost::incomplete;
using std::cout;
using std::endl;
struct non_leaf_node; // Forward declaration
// Define a variant with these two types:
// 1) int
// 2) The (incomplete) non_leaf_node struct
typedef variant&lt;int, incomplete&lt;non_leaf_node&gt; &gt; tree_node;
struct non_leaf_node
{
non_leaf_node(const tree_node&amp; l, int num, const tree_node&amp; r)
: left_(l), right_(r), num_(num) { }
non_leaf_node(const non_leaf_node&amp; other)
: left_(other.left_), right_(other.right_), num_(other.num_) { }
int num_;
tree_node left_;
tree_node right_;
};
struct tree_printer : boost::static_visitor&lt;void&gt;
{
void operator()(int n) const
{
cout &lt;&lt; n &lt;&lt; ' ';
}
void operator()(const non_leaf_node&amp; node) const
{
boost::apply_visitor(*this, node.left_);
cout &lt;&lt; node.num_ &lt;&lt; ' ';
boost::apply_visitor(*this, node.right_);
}
};
int main(int, char* [] )
{
//Build a binary search tree:
non_leaf_node a(3,4, 6);
non_leaf_node b(19, 20, 23);
non_leaf_node c(a,10, b);
tree_node root(c);
//Perform an in-order walk
boost::apply_visitor(tree_printer(), root);
return 0;
}
</pre>
<hr>
<h2><a name="poly">Polymorphism: Inheritance vs. Variants</a></h2>
<p>Let's assume we need to write a program that manipulates instances of <code>
star</code> and <code>space_ship</code>, where each of these two classes
inherits from <code>space_object</code>. The program maintains a <code>vector</code>
of pointers to these objects, which is used to calculate the total weight of
all <code>star</code> objects: </p>
<pre> //
// 'classic' inheritance-based implementation
//
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;iostream&gt;
struct space_object
{
virtual int weight() const = 0;
virtual ~space_object() { }
};
struct space_ship : space_object
{
space_ship(int w = 0) : w_(w) { }
int weight() const { return w_; }
int get_speed() const { return 15; }
int w_;
};
struct star : space_object
{
star(int w = 0) : w_(w) { }
int weight() const { return w_; }
int w_;
};
struct total_weight
{
total_weight() : total_(0) { }
void operator()(space_object* so_p)
{
if(dynamic_cast&lt;star*&gt;(so_p))
total_ += so_p-&gt;weight();
}
int total_;
};
int main(int, char* [] )
{
typedef std::vector&lt;space_object*&gt; main_vec;
main_vec space_objects;
//fill space_objects
// ...
total_weight tw_job;
int total = std::for_each(space_objects.begin(), space_objects.end(),
tw_job).total_;
std::cout &lt;&lt; &quot;Total weight of all stars = &quot; &lt;&lt; total
&lt;&lt; std::endl;
//Apply delete to all pointers stored in space_objects
// ...
return 0;
}
</pre>
<p>The are several issues worth noticing about this sample:<br>
</p>
<ul>
<li>The code is bound to define a vector of pointers, rather than objects.
This incurs considerable time overhead and could cause memory leaks if
destruction is not handled properly. </li>
<li>the dynamic_cast&lt;&gt; used by <code>total_weight::operator()</code> is a
costly operation. Alternatively, one can define an <code>enum</code> type
which will be used to correctly identify the concrete object, but this is an
error prone technique: the author must manually set the correct value for each
new concrete class. </li>
<li><code>total_weight</code> is unsafe when new classes are introduced.
Suppose a new class, <code>black_hole</code> - inherits directly from <code>
space_object</code> - is added to the code. <code>total_weight</code> will
silently ignore this class, possibly creating a havoc of run-time problems.
This is a major flaw from software engineering standpoint. </li>
</ul>
<p>This real-life design problem can be elegantly solved using variants. Here is
the variant-based code: </p>
<pre> //
// Variant-based implementation
//
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;iostream&gt;
#include &quot;boost/variant.hpp&quot;
#include &quot;boost/apply_visitor.hpp&quot;
#include &quot;boost/static_visitor.hpp&quot;
struct space_ship
{
space_ship(int w = 0) : w_(w) { }
int weight() const { return w_; }
int get_speed() const { return 15; }
int w_;
};
struct star
{
star(int w = 0) : w_(w) { }
int weight() const { return w_; }
int w_;
};
struct total_weight : boost::static_visitor&lt;void&gt;
{
total_weight() : total_(0) { }
void operator()(const star&amp; a_star)
{
total_ += a_star.weight();
}
//space_ship objects are ignored:
void operator()(const space_ship&amp; ) { }
int total_;
};
int main(int, char* [] )
{
typedef boost::variant&lt;star, space_ship&gt; space_var;
typedef std::vector&lt;space_var&gt; main_vec;
main_vec space_objects;
//fill space_objects
// ...
total_weight tw_job;
std::for_each(space_objects.begin(), space_objects.end(),
boost::apply_visitor(tw_job));
std::cout &lt;&lt; &quot;Total weight of all stars = &quot; &lt;&lt; tw_job.total_
&lt;&lt; std::endl;
return 0;
}
</pre>
<p>This implementation directly addresses the three issues raised by the
non-variant implementation: (1) The <code>space_objects</code> vector now holds
<b>objects</b> (rather than pointers), (2) <code>dynamic_cast&lt;&gt;</code>s are <b>
not needed</b> at all, and - most importantly - (3) the compiler <b>will
produce an error</b> if <code>total_weight</code> is not changed, when a new
class is introduced. </p>
<hr>
<p>Revised 14 February 2003</p>
<p><i>© 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>