mirror of
https://github.com/boostorg/variant.git
synced 2026-01-19 04:42:16 +00:00
336 lines
9.7 KiB
HTML
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: "<code>boost::apply_visitor(visitor,
|
|
variant);</code>" 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 "visited"
|
|
values, and - therefore - at the bottom of <code>main()</code>, its value is: 5
|
|
+ 16 + 3.11 + 15.3 = 39.41.</p>
|
|
<pre>#include <iostream>
|
|
|
|
#include "boost/variant.hpp"
|
|
#include "boost/apply_visitor.hpp"
|
|
#include "boost/static_visitor.hpp"
|
|
|
|
struct double_sum : boost::static_visitor<double>
|
|
{
|
|
double_sum() : total_(0.0) { }
|
|
|
|
template<class X>
|
|
double operator()(X x)
|
|
{
|
|
total_ += x;
|
|
return total_;
|
|
}
|
|
|
|
double total_;
|
|
};
|
|
|
|
int main(int, char* [] )
|
|
{
|
|
double_sum ds;
|
|
boost::variant<short, int, float, double> 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: "Total = 39.41"
|
|
std::cout << "Total = " << total << std::endl;
|
|
|
|
return 0;
|
|
}
|
|
</pre>
|
|
<hr>
|
|
<h2><a name="tree">A binary tree implementation</a></h2>
|
|
<p>This sample program shows how <code>incomplete<T></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 <iostream>
|
|
|
|
#include "boost/variant.hpp"
|
|
#include "boost/apply_visitor.hpp"
|
|
#include "boost/static_visitor.hpp"
|
|
#include "boost/incomplete.hpp"
|
|
|
|
|
|
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<int, incomplete<non_leaf_node> > tree_node;
|
|
|
|
struct non_leaf_node
|
|
{
|
|
non_leaf_node(const tree_node& l, int num, const tree_node& r)
|
|
: left_(l), right_(r), num_(num) { }
|
|
non_leaf_node(const non_leaf_node& other)
|
|
: left_(other.left_), right_(other.right_), num_(other.num_) { }
|
|
|
|
int num_;
|
|
tree_node left_;
|
|
tree_node right_;
|
|
};
|
|
|
|
|
|
struct tree_printer : boost::static_visitor<void>
|
|
{
|
|
void operator()(int n) const
|
|
{
|
|
cout << n << ' ';
|
|
}
|
|
|
|
void operator()(const non_leaf_node& node) const
|
|
{
|
|
boost::apply_visitor(*this, node.left_);
|
|
cout << node.num_ << ' ';
|
|
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 <vector>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
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<star*>(so_p))
|
|
total_ += so_p->weight();
|
|
}
|
|
|
|
int total_;
|
|
};
|
|
|
|
int main(int, char* [] )
|
|
{
|
|
typedef std::vector<space_object*> 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 << "Total weight of all stars = " << total
|
|
<< 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<> 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 <vector>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
#include "boost/variant.hpp"
|
|
#include "boost/apply_visitor.hpp"
|
|
#include "boost/static_visitor.hpp"
|
|
|
|
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<void>
|
|
{
|
|
total_weight() : total_(0) { }
|
|
|
|
void operator()(const star& a_star)
|
|
{
|
|
total_ += a_star.weight();
|
|
}
|
|
|
|
//space_ship objects are ignored:
|
|
void operator()(const space_ship& ) { }
|
|
|
|
int total_;
|
|
};
|
|
|
|
int main(int, char* [] )
|
|
{
|
|
typedef boost::variant<star, space_ship> space_var;
|
|
typedef std::vector<space_var> 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 << "Total weight of all stars = " << tw_job.total_
|
|
<< 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<></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 "as is" without express or implied warranty.</p>
|
|
|
|
</body>
|
|
|
|
</html>
|