Blog: Articles on C++11 and Computational Finance (by Daniel J. Duffy)

Daniel Duffy

C++ author, trainer
Code:
// 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;
}
C++17 (and Boost) type-safe variant and its visitors. Use /std:c++latest
 

Attachments

Daniel Duffy

C++ author, trainer
Some weekend thoughts ...
I didn't always work on high-level languages like C++. Once upon a time I was a summer intern with GE in Ireland testing diodes and triodes (big ones!) using Basic on a noisy teletype machine connected to the computer in Scotland by a very long cable.

Advantage of paper tape: no chance of hacking, easy to make backup
Disadvantage code difficult to maintain (need a pair of scissors .. cut and paste). And earplugs. These days people never had it so good :unsure: 😂

29781
29782
 
Last edited:

Daniel Duffy

C++ author, trainer
C++ 2020 Concepts, finally

Concepts are an extension to the templates feature provided by the C++ programming language.[...] concepts are named Boolean predicates on template parameters, evaluated at compile time. A concept may be associated with a template (class template, function template, or member function of a class template), in which case it serves as a constraint: it limits the set of arguments that are accepted as template parameters.

C++20 Concepts Are Here in Visual Studio 2019 version 16.3 – C++ Team Blog

 
Top