// TestSTLVariant101.cpp
//
// Show the essentials of the Variant Library in Boost.
// Supported in C++17, std::variant and std::visit()
//
// Show a variety of methods for type-safe visitation of type-safe unions.
//
// Q. can you get std::variant working with boost::static_visitor<>?
//
// BIG DEAL is: adding functionality NON-INTRUSIVELY to a class by Visior pattern.
//
// (C) Datasim Education BV 2009-2019
//
#include <iostream>
#include <boost/variant.hpp>
#include <variant>
// Basic shapes
struct Point
{
double x;
double y;
void displayPoint()
{
std::cout << "Point: " << x << ", " << y << '\n';
}
};
struct Circle
{
Point centre;
double radius;
void displayCircle()
{
std::cout << "Circle, radius " << radius << '\n';
std::cout << "Centre point "; centre.displayPoint();
}
};
struct LineSegment
{
Point p1;
Point p2;
void displayLineSegment()
{
std::cout << "Line, endpoints "<< '\n';
p1.displayPoint();
p2.displayPoint();
}
};
using VariantBoost = boost::variant<Point, LineSegment, Circle>;
using VariantSTL = std::variant<Point, LineSegment, Circle>;
// Different ways to add functionality/visit shapes
// A. In combination with Boost visitor pattern
class Print_Visitor : public boost::static_visitor<void>
{
public:
void operator () (Point& s) { s.displayPoint(); }
void operator () (LineSegment& s) { s.displayLineSegment(); }
void operator () (Circle& s) { s.displayCircle(); }
};
class Translate_Visitor : public boost::static_visitor<void>
{ // Move the shapes
private:
double off;
public:
Translate_Visitor(double offset) : off(offset) {}
void operator () (Point& s) { s.x += off; s.y += off;}
void operator () (LineSegment& s) {(*this)(s.p1); (*this)(s.p2);}
void operator () (Circle& s) { (*this)(s.centre); }
};
// B. Lambda function to test each type
auto lambdaVisit = [](auto&& arg)
{
using T = std::decay_t < decltype(arg)>;
// Hard-coded switch
if constexpr (std::is_same_v<T, Point>)
arg.displayPoint();
if constexpr (std::is_same_v<T, LineSegment>)
arg.displayLineSegment();
if constexpr (std::is_same_v<T, Circle>)
arg.displayCircle();
};
void MyVisit(VariantSTL& var)
{
std::visit(lambdaVisit, var);
}
void MyVisit2(VariantSTL& v)
{
switch (v.index()) {
case 0:
std::get<0>(v).displayPoint();
break;
case 1:
std::get<1>(v).displayLineSegment();
break;
case 2:
std::get<2>(v).displayCircle();
break;
default:
std::cout << "ugh\n";
}
}
void MyVisit3(VariantSTL& v)
{
switch (v.index()) {
case 0:
std::get<Point>(v).displayPoint();
break;
case 1:
std::get<LineSegment>(v).displayLineSegment();
break;
case 2:
std::get<Circle>(v).displayCircle();
break;
default:
std::cout << "ugh\n";
}
}
int main()
{
// Create some shapes
Point p1{ 1,1 };
Point p2{ 2,1 };
LineSegment myL{ p1, p2 };
double radius = 2.0;
Circle c{ p1, radius };
// Checking the data
std::cout << "\n*** Sanity checks \n";
p1.displayPoint(); p2.displayPoint();
myL.displayLineSegment(); c.displayCircle();
{
// Boost Visitor
std::cout << "\n*** C++11 visit using Boost visitor \n";
VariantBoost var(p1);
Print_Visitor vis;
// Give some values; visit each component
var = p1;
boost::apply_visitor(vis, var);
var = myL;
boost::apply_visitor(vis, var);
var = c;
boost::apply_visitor(vis, var);
}
// C++11 solution
{
std::cout << "\n*** C++11 visit\n";
VariantSTL var(p1);
MyVisit(var);
var = c;
MyVisit(var);
var = myL;
MyVisit(var);
}
// C. Based on the variant's index
{
std::cout << "\n*** C++11 visit using indices \n";
VariantSTL var(p1);
MyVisit2(var);
var = c;
MyVisit2(var);
var = myL;
MyVisit2(var);
}
// D. Based on the variant's type
{
std::cout << "\n*** C++11 visit using type as index \n";
VariantSTL var(p1);
MyVisit3(var);
var = c;
MyVisit3(var);
var = myL;
MyVisit3(var);
}
// E. Translate Visitor
{
std::cout << "\n*** Translate visitor \n";
Point p{ 0.0, 0.0 };
Point p2 {1.0, 1.0 };
LineSegment myL{ p, p2 };
Circle c{ p, 10.0 };
// Boost Visitor
std::cout << "\n*** C++11 visit using Boost visitor \n";
VariantBoost var(p);
double offset = 2.0;
Translate_Visitor vis(offset);
Print_Visitor visP;
// Give some values; visit each component
var = p;
boost::apply_visitor(vis, var);
boost::apply_visitor(visP, var);
var = myL;
boost::apply_visitor(vis, var);
boost::apply_visitor(visP, var);
var = c;
boost::apply_visitor(vis, var);
boost::apply_visitor(visP, var);
/* p.displayPoint();
myL.displayLineSegment();
c.displayCircle();*/
}
return 0;
}