Skip to content
Snippets Groups Projects
Commit 8224ffb9 authored by Sandipan Mohanty's avatar Sandipan Mohanty
Browse files

Chapter 2 examples

parent f0551846
No related branches found
No related tags found
No related merge requests found
Showing
with 601 additions and 0 deletions
#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";
}
}
#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";
}
}
#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.}});
}
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)
#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;
}
#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
#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;
}
#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
#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
#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
#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));
}
#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"; }
};
#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";
}
}
#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";
}
#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";
}
}
#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;
}
#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);
}
#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";
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment