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