From 8224ffb980ffe020928efdf4ddac0aeffbac7dec Mon Sep 17 00:00:00 2001
From: Sandipan Mohanty <s.mohanty@fz-juelich.de>
Date: Sat, 11 May 2024 20:07:54 +0200
Subject: [PATCH] Chapter 2 examples

---
 chapter_02/examples/collect.cc              |  26 +++++
 chapter_02/examples/complex_number_class.cc | 114 ++++++++++++++++++++
 chapter_02/examples/desig2.cc               |  19 ++++
 chapter_02/examples/geometry/CMakeLists.txt |   9 ++
 chapter_02/examples/geometry/Circle.cc      |  31 ++++++
 chapter_02/examples/geometry/Circle.hh      |  27 +++++
 chapter_02/examples/geometry/Point.cc       |  53 +++++++++
 chapter_02/examples/geometry/Point.hh       |  23 ++++
 chapter_02/examples/geometry/Polygon.hh     |  50 +++++++++
 chapter_02/examples/geometry/Shape.hh       |  17 +++
 chapter_02/examples/geometry/Triangle.cc    |  23 ++++
 chapter_02/examples/geometry/Triangle.hh    |  24 +++++
 chapter_02/examples/geometry/main.cc        |  19 ++++
 chapter_02/examples/literals.cc             |  34 ++++++
 chapter_02/examples/onexcept.cc             |  25 +++++
 chapter_02/examples/op_overload.cc          |  31 ++++++
 chapter_02/examples/trivialclassoverload.cc |  19 ++++
 chapter_02/examples/verbose_ctordtor.cc     |  57 ++++++++++
 18 files changed, 601 insertions(+)
 create mode 100644 chapter_02/examples/collect.cc
 create mode 100644 chapter_02/examples/complex_number_class.cc
 create mode 100644 chapter_02/examples/desig2.cc
 create mode 100644 chapter_02/examples/geometry/CMakeLists.txt
 create mode 100644 chapter_02/examples/geometry/Circle.cc
 create mode 100644 chapter_02/examples/geometry/Circle.hh
 create mode 100644 chapter_02/examples/geometry/Point.cc
 create mode 100644 chapter_02/examples/geometry/Point.hh
 create mode 100644 chapter_02/examples/geometry/Polygon.hh
 create mode 100644 chapter_02/examples/geometry/Shape.hh
 create mode 100644 chapter_02/examples/geometry/Triangle.cc
 create mode 100644 chapter_02/examples/geometry/Triangle.hh
 create mode 100644 chapter_02/examples/geometry/main.cc
 create mode 100644 chapter_02/examples/literals.cc
 create mode 100644 chapter_02/examples/onexcept.cc
 create mode 100644 chapter_02/examples/op_overload.cc
 create mode 100644 chapter_02/examples/trivialclassoverload.cc
 create mode 100644 chapter_02/examples/verbose_ctordtor.cc

diff --git a/chapter_02/examples/collect.cc b/chapter_02/examples/collect.cc
new file mode 100644
index 0000000..bc7f1f1
--- /dev/null
+++ b/chapter_02/examples/collect.cc
@@ -0,0 +1,26 @@
+#include <iostream>
+#include <vector>
+
+class collect {
+    std::vector<int> dat;
+public:
+    auto operator|(int i) -> collect&
+    {
+        dat.push_back(i);
+        return *this;
+    }
+    auto operator~() const noexcept -> decltype(dat)
+    {
+        return dat;
+    }
+};
+auto main() -> int
+{
+    auto C = collect{};
+    C | 1 | 2 | 3 | 4 ;
+    for (auto el : (~C)) {
+        std::cout << el << "\n";
+    }
+}
+
+
diff --git a/chapter_02/examples/complex_number_class.cc b/chapter_02/examples/complex_number_class.cc
new file mode 100644
index 0000000..5b73f63
--- /dev/null
+++ b/chapter_02/examples/complex_number_class.cc
@@ -0,0 +1,114 @@
+#include <iostream>
+
+class complex_number
+{
+public:
+    // If the constructor is meant to do nothing, let the compiler generate it
+    constexpr complex_number() noexcept = default;
+    // Same for the destructor
+    ~complex_number() noexcept = default;
+    // Constructor with real and imaginary parts
+    constexpr complex_number(double re, double im) noexcept;
+    // Constructor with only a real part. It "delegates" its work to the 
+    // constructor above. See its implementation below
+    constexpr complex_number(double re) noexcept;
+    // Copy constructor with trivial member wise copy
+    constexpr complex_number(const complex_number &c) noexcept = default;
+    // Trivial assignment operator
+    constexpr auto operator=(const complex_number& c) noexcept -> complex_number& = default;
+    // Move constructor with trivial member wise move
+    constexpr complex_number(complex_number &&c) noexcept = default;
+    // Move assignment operator
+    constexpr auto operator=(complex_number&& c) noexcept -> complex_number& = default;
+    // Arithmetic operators changing the calling object
+    constexpr auto operator+=(const complex_number&) noexcept -> complex_number&;
+    constexpr auto operator-=(const complex_number&) noexcept -> complex_number&;
+    constexpr auto operator*=(const complex_number&) noexcept -> complex_number&;
+    // Accessor functions
+    constexpr auto real() const noexcept -> double { return m_real; }
+    constexpr auto imag() const noexcept -> double { return m_imag; }
+    constexpr auto modulus() const noexcept -> double { return m_real * m_real + m_imag * m_imag; }
+    // Non-member function which accesses private data
+    // This is needed to write things like
+    // double d; complex_number c; 
+    // complex_number e = d*c;
+    // Because the operator is called with the LHS as the refering object
+    friend constexpr auto operator*(double x, const complex_number&) noexcept -> complex_number;
+    // Teach ostream how to output your class
+    friend auto operator<<(std::ostream& os, const complex_number& c) -> std::ostream&;
+
+private: 
+    // Data variables. Private. Initialised.
+    double m_real=0,m_imag=0;
+};
+// Note syntax for initialisation. You can assign to m_real and m_imag in the
+// function body, but the initialiser syntax is more compact.
+constexpr complex_number::complex_number(double re, double im) noexcept : m_real{re},m_imag{im} {}
+// This is a constructor which gets its work done by calling another constructor.
+// This is allowed since C++11.
+constexpr complex_number::complex_number(double re) noexcept : complex_number{re,0} {}
+// Note the compact notation for the return statement. The "type" of the
+// return value is, of course, known from the function signature. So, 
+// the compiler knows that it has to construct a complex_number from
+// whatever that comes after the keyword "return". Since we have 
+// only a set of braces with two arguments, it looks for a constructor
+// with two double arguments, and uses that.
+// The following functions are not member functions. They don't need to be.
+constexpr auto operator+(const complex_number& b, const complex_number& c) noexcept -> complex_number
+{
+    return {b.real()+c.real(),b.imag()+c.imag()};
+}
+constexpr auto operator-(const complex_number& b, const complex_number& c) noexcept -> complex_number
+{
+    return {b.real()-c.real(),b.imag()-c.imag()};
+}
+constexpr auto operator*(const complex_number& b, const complex_number& c) noexcept -> complex_number
+{
+    return {b.real()*c.real()-b.imag()*c.imag(), b.real()*c.imag() + b.imag()*c.real()};
+}
+// Modifying operators do not construct a new object and return it, but
+// instead directly change the data in the calling object. Look how
+// the function returns the calling object through the "this" pointer!
+// This is needed to write nested statements like z=z1*(z2+=z1);
+constexpr auto complex_number::operator+=(const complex_number& c) noexcept -> complex_number&
+{
+    m_real+=c.m_real;
+    m_imag+=c.m_imag;
+    return *this;
+}
+constexpr auto complex_number::operator-=(const complex_number& c) noexcept -> complex_number&
+{
+    m_real-=c.m_real;
+    m_imag-=c.m_imag;
+    return *this;
+}
+constexpr auto complex_number::operator*=(const complex_number& c) noexcept -> complex_number&
+{
+    // Temporary store for new real value
+    double tmprl=m_real*c.m_real-m_imag*c.m_imag;
+    // Calculate new imaginary value using old real and imaginary parts
+    m_imag=m_real*c.m_imag+m_imag*c.m_real;
+    m_real=tmprl;
+    return *this;
+}
+constexpr auto operator*(double x, const complex_number& c) noexcept -> complex_number
+{
+    return {x*c.m_real,x*c.m_imag};
+}
+auto operator<<(std::ostream& os, const complex_number& c) -> std::ostream&
+{
+    os << c.m_real;
+    if (c.m_imag>0) os << '+';
+    os << c.m_imag << 'i';
+    return os;
+}
+
+auto main() -> int
+{
+    static_assert(complex_number { 0.1, -2.3 }.modulus() > 0, "");
+    complex_number z0{0.3,0.84}, z;
+    for (int i=0; i<10; ++i) {
+        z = z*z + z0;
+        std::cout << z << "\n";
+    }
+}
diff --git a/chapter_02/examples/desig2.cc b/chapter_02/examples/desig2.cc
new file mode 100644
index 0000000..4692652
--- /dev/null
+++ b/chapter_02/examples/desig2.cc
@@ -0,0 +1,19 @@
+#include <iostream>
+
+struct v3 { double x, y, z; };
+struct pars { int offset; v3 velocity; };
+auto operator<<(std::ostream & os, const v3 & v) -> std::ostream&
+{
+    return os << v.x << ", " << v.y << ", " << v.z << " ";
+}
+auto example_func(pars p)
+{
+    std::cout << p.offset << " with velocity " << p.velocity << "\n";
+}
+
+auto main() -> int
+{
+    example_func({.offset = 5, .velocity = {.x=1., .y = 2., .z=3.}});
+}
+
+
diff --git a/chapter_02/examples/geometry/CMakeLists.txt b/chapter_02/examples/geometry/CMakeLists.txt
new file mode 100644
index 0000000..f4bf718
--- /dev/null
+++ b/chapter_02/examples/geometry/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.28)
+set(CMAKE_CXX_STANDARD 23)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+project(geometry_exercise CXX)
+add_executable(${PROJECT_NAME} Point.cc Triangle.cc Circle.cc main.cc)
+
diff --git a/chapter_02/examples/geometry/Circle.cc b/chapter_02/examples/geometry/Circle.cc
new file mode 100644
index 0000000..096797e
--- /dev/null
+++ b/chapter_02/examples/geometry/Circle.cc
@@ -0,0 +1,31 @@
+#include "Circle.hh"
+
+constexpr double pi = 3.141592653589793;
+
+Circle::Circle(double rad, const Point& p)
+    : r{ rad }
+    , c{ p }
+{
+}
+
+auto Circle::name() const -> std::string
+{
+    return "Circle";
+}
+
+auto Circle::area() const -> double
+{
+    return pi * r * r;
+}
+
+auto Circle::perimeter() const -> double
+{
+    return 2 * pi * r;
+}
+
+void Circle::rotate(double phi) { phi = 0; }
+
+void Circle::translate(Point p)
+{
+    c += p;
+}
diff --git a/chapter_02/examples/geometry/Circle.hh b/chapter_02/examples/geometry/Circle.hh
new file mode 100644
index 0000000..615df31
--- /dev/null
+++ b/chapter_02/examples/geometry/Circle.hh
@@ -0,0 +1,27 @@
+#ifndef Circle_HH
+#define Circle_HH
+#include "Point.hh"
+#include "Shape.hh"
+#include <string>
+
+class Circle : public Shape {
+public:
+    Circle() = default;
+    Circle(double rad, const Point& p);
+    Circle(const Circle& cir) = default;
+    Circle(Circle&& cir) = default;
+    auto operator=(const Circle& cir) -> Circle& = default;
+    auto operator=(Circle&& cir) -> Circle& = default;
+    void rotate(double phi) override;
+    void translate(Point p) override;
+    auto area() const -> double override;
+    auto perimeter() const -> double override;
+    inline auto circumference() const -> double { return perimeter(); }
+    auto name() const -> std::string override;
+
+private:
+    double r{ 1.0 };
+    Point c{}; // Use default constructor of class Point to create c
+};
+
+#endif
diff --git a/chapter_02/examples/geometry/Point.cc b/chapter_02/examples/geometry/Point.cc
new file mode 100644
index 0000000..1b10c86
--- /dev/null
+++ b/chapter_02/examples/geometry/Point.cc
@@ -0,0 +1,53 @@
+#include "Point.hh"
+#include <iostream>
+
+Point::Point(double x, double y)
+    : X{ x }
+    , Y{ y }
+{
+}
+
+auto Point::operator+=(const Point& p) -> Point&
+{
+    X += p.X;
+    Y += p.Y;
+    return *this;
+}
+
+auto Point::operator-=(const Point& p) -> Point&
+{
+    X -= p.X;
+    Y -= p.Y;
+    return *this;
+}
+
+auto Point::operator+(const Point& p) const -> Point
+{
+    return { X + p.X, Y + p.Y };
+}
+
+auto Point::operator-(const Point& p) const -> Point
+{
+    return { X - p.X, Y - p.Y };
+}
+
+auto Point::operator*(const Point& p) const -> double
+{
+    return (X * p.X + Y * p.Y);
+}
+
+auto Point::operator*(double f) const -> Point
+{
+    return { f * X, f * Y };
+}
+
+auto operator*(double f, const Point& p) -> Point
+{
+    return { f * p.X, f * p.Y };
+}
+
+auto operator<<(std::ostream& os, const Point& p) -> std::ostream&
+{
+    os << "(" << p.X << ", " << p.Y << ")";
+    return os;
+}
diff --git a/chapter_02/examples/geometry/Point.hh b/chapter_02/examples/geometry/Point.hh
new file mode 100644
index 0000000..b68b4d4
--- /dev/null
+++ b/chapter_02/examples/geometry/Point.hh
@@ -0,0 +1,23 @@
+#ifndef Point_HH
+#define Point_HH
+class ostream;
+struct Point {
+    double X = 0, Y = 0;
+    Point() = default;
+    Point(const Point&) = default;
+    Point(Point&&) = default;
+    auto operator=(const Point& p) -> Point& = default;
+    auto operator=(Point&& p) -> Point& = default;
+    Point(double x, double y);
+    auto operator+=(const Point& p) -> Point&;
+    auto operator-=(const Point& p) -> Point&;
+    auto operator+(const Point& p) const -> Point;
+    auto operator-(const Point& p) const -> Point;
+    auto operator*(const Point& p) const -> double;
+    auto operator*(double f) const -> Point;
+};
+
+auto operator*(double f, const Point& p) -> Point;
+auto operator<<(ostream& os, const Point& p) -> ostream&;
+
+#endif
diff --git a/chapter_02/examples/geometry/Polygon.hh b/chapter_02/examples/geometry/Polygon.hh
new file mode 100644
index 0000000..293c82a
--- /dev/null
+++ b/chapter_02/examples/geometry/Polygon.hh
@@ -0,0 +1,50 @@
+#ifndef Polygon_HH
+#define Polygon_HH
+#include <array>
+#include <cmath>
+#include "Shape.hh"
+
+template <unsigned int NV>
+class Polygon : public Shape {
+    static_assert(NV > 2, "Can't have polygon with less than 3 sides");
+public:
+    Polygon() = default;
+    Polygon(const Polygon&) = default;
+    Polygon(Polygon&&) = default;
+    auto operator=(const Polygon& pg) -> Polygon& = default;
+    auto operator=(Polygon &&) -> Polygon& = default;
+    constexpr auto n_vertexes() const { return NV; }
+    auto name() const -> std::string override { return "Polygon<" + std::to_string(NV) + ">"; }
+
+    auto perimeter() const -> double override
+    {
+        double ans = 0;
+        for (size_t i = 1; i < vertex.size(); ++i) {
+            ans += sqrt((vertex[i] - vertex[i - 1]) * (vertex[i] - vertex[i - 1]));
+        }
+        ans += sqrt((vertex.front() - vertex.back()) * (vertex.front() - vertex.back()));
+        return ans;
+    }
+    void translate(Point p) override {
+        for (auto& pt : vertex)
+            pt += p;
+    }
+
+    void rotate(double phi) override{
+        Point center;
+        for (auto pt : vertex)
+            center += pt;
+        center = (1.0 / NV) * center;
+        double ct = cos(phi), st = sin(phi);
+        for (auto& pt : vertex) {
+            auto rel = pt - center;
+            pt = center + Point(ct * rel.X + st * rel.Y, -st * rel.X + ct * rel.Y);
+        }
+    }
+
+
+protected:
+    std::array<Point, NV> vertex;
+};
+
+#endif
diff --git a/chapter_02/examples/geometry/Shape.hh b/chapter_02/examples/geometry/Shape.hh
new file mode 100644
index 0000000..52c9289
--- /dev/null
+++ b/chapter_02/examples/geometry/Shape.hh
@@ -0,0 +1,17 @@
+#ifndef Shape_HH
+#define Shape_HH
+
+#include "Point.hh"
+#include <string>
+
+class Shape {
+public:
+    virtual ~Shape() = default;
+    virtual void rotate(double) = 0;
+    virtual void translate(Point) = 0;
+    virtual auto area() const -> double = 0;
+    virtual auto perimeter() const -> double = 0;
+    virtual auto name() const -> std::string = 0;
+};
+
+#endif
diff --git a/chapter_02/examples/geometry/Triangle.cc b/chapter_02/examples/geometry/Triangle.cc
new file mode 100644
index 0000000..c3f484c
--- /dev/null
+++ b/chapter_02/examples/geometry/Triangle.cc
@@ -0,0 +1,23 @@
+#include "Triangle.hh"
+#include <cmath>
+// A "static" function in a source file (not a static member function of a class)
+// is meant to be visible only in the same translation unit, most often just that
+// source file.
+static inline auto sqr(Point p) -> double { return p * p; }
+
+Triangle::Triangle(Point p1, Point p2, Point p3)
+    : Polygon<3>{}
+{
+    vertex[0] = p1;
+    vertex[1] = p2;
+    vertex[2] = p3;
+}
+
+auto Triangle::area() const -> double
+{
+    double s = 0.5 * perimeter();
+    double a = sqrt(sqr(vertex[0] - vertex[1]));
+    double b = sqrt(sqr(vertex[0] - vertex[2]));
+    double c = sqrt(sqr(vertex[1] - vertex[2]));
+    return sqrt(s * (s - a) * (s - b) * (s - c));
+}
diff --git a/chapter_02/examples/geometry/Triangle.hh b/chapter_02/examples/geometry/Triangle.hh
new file mode 100644
index 0000000..dcf5264
--- /dev/null
+++ b/chapter_02/examples/geometry/Triangle.hh
@@ -0,0 +1,24 @@
+#include "Polygon.hh"
+#include <string>
+#include <iostream>
+
+class Triangle : public Polygon<3> {
+public:
+    void sleep(int i, int j=4, int k=9) {
+        std::cout << i << "\t" << j << "\t" << k << "\n";
+    }
+    void sleeptest()
+    {
+        sleep(1);
+        sleep(9,3,4);
+        sleep(3,4);
+    }
+    Triangle() = default;
+    Triangle(Point p1, Point p2, Point p3);
+    Triangle(const Triangle&) = default;
+    Triangle(Triangle&&) = default;
+    auto operator=(const Triangle&) -> Triangle& = default;
+    auto operator=(Triangle &&) -> Triangle& = default;
+    auto area() const -> double override;
+    auto name() const -> std::string override { return "Triangle"; }
+};
diff --git a/chapter_02/examples/geometry/main.cc b/chapter_02/examples/geometry/main.cc
new file mode 100644
index 0000000..e83c0b5
--- /dev/null
+++ b/chapter_02/examples/geometry/main.cc
@@ -0,0 +1,19 @@
+#include "Circle.hh"
+#include "Triangle.hh"
+#include <iostream>
+#include <memory>
+#include <vector>
+
+auto main() -> int
+{
+    std::vector<std::unique_ptr<Shape>> shapes;
+    shapes.push_back(std::make_unique<Circle>(3.0, Point{ 2.3, 4.3 }));
+    shapes.push_back(std::make_unique<Circle>(2.7, Point{ 0.8, 1.9 }));
+    shapes.push_back(std::make_unique<Circle>(3.9, Point{ 2.8, 3.6 }));
+    shapes.push_back(std::make_unique<Triangle>(Point{ 1.8, 2.5 }, Point{ 4.3, 5.4 }, Point{ 2.1, 8.3 }));
+    for (auto i=0ul; i<shapes.size(); ++i) {
+        std::cout << i << ": " << shapes[i]->name()
+                  << " with area " << shapes[i]->area() << "\n";
+    }
+}
+
diff --git a/chapter_02/examples/literals.cc b/chapter_02/examples/literals.cc
new file mode 100644
index 0000000..0f322cb
--- /dev/null
+++ b/chapter_02/examples/literals.cc
@@ -0,0 +1,34 @@
+#include <iostream>
+
+class Temperature
+{
+    double v = 0.0;
+    public:
+    enum class Unit { K, C };
+    constexpr auto ConvFrom(double x, Unit u) {
+        switch (u) {
+            case Unit::K : return x;
+            case Unit::C : 
+            default:  return 273.15 + x;
+        };
+    }
+    Temperature() = default;
+    Temperature(double x, Unit u) : v{ ConvFrom(x, u) } {}
+    auto Kelvin() const noexcept -> double { return v; }
+    auto Celsius() const noexcept -> double { return v; }
+};
+auto operator "" _K(long double d) -> Temperature
+{
+    return { static_cast<double>(d), Temperature::Unit::K };
+}
+auto operator "" _C(long double d) -> Temperature
+{
+    return { static_cast<double>(d), Temperature::Unit::C };
+}
+
+auto main() -> int
+{
+    auto T1 = 273.15_K;
+    auto T2 = 100.0_C;
+    std::cout << T1.Celsius() << "\t" << T2.Celsius() << "\n";
+}
diff --git a/chapter_02/examples/onexcept.cc b/chapter_02/examples/onexcept.cc
new file mode 100644
index 0000000..38fbd67
--- /dev/null
+++ b/chapter_02/examples/onexcept.cc
@@ -0,0 +1,25 @@
+#include "Vbose.hh"
+
+void testfunc(int i)
+{
+    std::cout << "Entering testfunc with argument " << i << "\n";
+    Vbose v{"VFROMTESTFUNC"};
+    if (i > 2) {
+        std::cout << "Exiting testfunc() via exception.\n";
+        throw 2;
+    }
+    else std::cout << "Passed check point 2\n";
+    std::cout << "Last line of testfunc("<< i << ")\n"; 
+}
+
+auto main() -> int
+{
+    try {
+    testfunc(0);
+    testfunc(1);
+    testfunc(3);
+    testfunc(2);
+    } catch (int err) {
+        std::cout << "Caught exception " << err << "\n";
+    }
+}
diff --git a/chapter_02/examples/op_overload.cc b/chapter_02/examples/op_overload.cc
new file mode 100644
index 0000000..bb1fd10
--- /dev/null
+++ b/chapter_02/examples/op_overload.cc
@@ -0,0 +1,31 @@
+#include <iostream>
+
+class A {};
+class B {};
+
+auto operator+(A x, A y) -> A
+{
+    std::cout << "Called operator+() with two A type arguments\n";
+    return x;
+}
+auto operator+(B x, B y) -> B
+{
+    std::cout << "Called operator+() with two B type arguments\n";
+    return x;
+}
+auto operator@(A x, B y) -> A
+{
+    std::cout << "Called operator+() with one A type and one B type argument\n";
+    return x;
+}
+
+auto main() -> int
+{
+    A a1, a2;
+    B b1, b2;
+    a1 + a2;
+    a1 @ b1;
+    b1 + b2;
+//    b1 + a2;
+}
+
diff --git a/chapter_02/examples/trivialclassoverload.cc b/chapter_02/examples/trivialclassoverload.cc
new file mode 100644
index 0000000..f384557
--- /dev/null
+++ b/chapter_02/examples/trivialclassoverload.cc
@@ -0,0 +1,19 @@
+#include <iostream>
+class A{};
+class B{};
+
+void f(int i, A a)
+{
+    std::cout << "Called version with input types (int, A)\n";
+}
+void f(int i, B b)
+{
+    std::cout << "Called version with input types (int, B)\n";
+}
+auto main() -> int
+{
+    A xa;
+    B xb;
+    f(0, xa);
+    f(0, xb);
+}
diff --git a/chapter_02/examples/verbose_ctordtor.cc b/chapter_02/examples/verbose_ctordtor.cc
new file mode 100644
index 0000000..23beae7
--- /dev/null
+++ b/chapter_02/examples/verbose_ctordtor.cc
@@ -0,0 +1,57 @@
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include "Vbose.hh"
+
+auto operator<<(std::ostream& os, const Vbose* obj) -> std::ostream&
+{
+    os << std::hex << (size_t)(obj);
+    return os;
+}
+
+auto f(std::string a) -> Vbose
+{
+    std::cout << "Entering f()\n";
+    Vbose tmp(a);
+    if (tmp.getval() == "") {
+        std::cerr << "Warning! Empty string used to construct object!\n";
+    }
+    std::cout << "Leaving f()\n";
+    return tmp;
+}
+
+void g(Vbose v)
+{
+    std::cout << "Called g with " << &v << "(" << v.getval() << ")\n";
+    v.setval(v.getval() + "_modified");
+    std::cout << "g() : Modified v to " << v.getval() << "\n";
+}
+
+auto main() -> int
+{
+    std::cout << "Entering main()\n";
+    std::cout << "Constructing a and b\n";
+    Vbose a, b("Mercury");
+    {
+        Vbose c("UnusedVar");
+    }
+    std::cout << "Calling f and assigning result to a\n";
+    a = f("Venus");
+
+    std::cout << "Calling f without assigning the result\n";
+    f("Jupitor");
+    std::cout << "Statement after calling f without assigning result\n";
+
+    std::cout << "Calling g with b\n";
+    g(b);
+    std::cout << "Statement after calling g with b\n";
+    std::cout << "Value of b, after call to g, is " << b.getval() << "\n";
+
+    std::cout << "Calling g with a+b\n";
+    g(a + b);
+
+    std::cout << "Calling g with std::move(a)\n";
+    g(std::move(a));
+    std::cout << "Leaving main()\n";
+}
+
-- 
GitLab