diff --git a/chapter_08/examples/2_any/with_header_files/CMakeLists.txt b/chapter_08/examples/2_any/with_header_files/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e3af96a7ee4723f106b4d0f9ca31e5091243341 --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.28) + +project(polymorphic_w_any CXX) +set(CMAKE_CXX_EXTENSIONS OFF) +set (CMAKE_CXX_STANDARD 23) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set(Headers Point.hh Polygon.hh Triangle.hh Circle.hh) +add_executable(${PROJECT_NAME} Point.cc Triangle.cc Circle.cc main.cc ${Headers}) + diff --git a/chapter_08/examples/2_any/with_header_files/Circle.cc b/chapter_08/examples/2_any/with_header_files/Circle.cc new file mode 100644 index 0000000000000000000000000000000000000000..6ae0fa5582a3385e0d4f1c1480d956797f994e46 --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/Circle.cc @@ -0,0 +1,26 @@ +#include "Circle.hh" + +constexpr double pi = 3.141592653589793; + +Circle::Circle(double rad, const Point& p) + : r{ rad } + , c{ p } +{ +} + +double Circle::area() const +{ + return pi * r * r; +} + +double Circle::perimeter() const +{ + return 2 * pi * r; +} + +void Circle::rotate(double phi) { phi = 0; } + +void Circle::translate(Point p) +{ + c += p; +} diff --git a/chapter_08/examples/2_any/with_header_files/Circle.hh b/chapter_08/examples/2_any/with_header_files/Circle.hh new file mode 100644 index 0000000000000000000000000000000000000000..adee3d74ca3c027cd74ddd180782fb4aed86fef9 --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/Circle.hh @@ -0,0 +1,26 @@ +#ifndef Circle_HH +#define Circle_HH +#include "Point.hh" +#include <string> + +class Circle { +public: + Circle() = default; + Circle(double rad, const Point& p); + Circle(const Circle& cir) = default; + Circle(Circle&& cir) = default; + Circle& operator=(const Circle& cir) = default; + Circle& operator=(Circle&& cir) = default; + std::string name() const { return "Circle"; } + void rotate(double phi); + void translate(Point p); + double area() const; + double perimeter() const; + inline double circumference() const { return perimeter(); } + +private: + double r{ 1.0 }; + Point c{}; // Use default constructor of class Point to create c +}; + +#endif diff --git a/chapter_08/examples/2_any/with_header_files/Point.cc b/chapter_08/examples/2_any/with_header_files/Point.cc new file mode 100644 index 0000000000000000000000000000000000000000..74ddec6e7e995d547fd04da729d083a8c6ad1bfc --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/Point.cc @@ -0,0 +1,53 @@ +#include "Point.hh" +#include <iostream> + +Point::Point(double x, double y) + : X{ x } + , Y{ y } +{ +} + +Point& Point::operator+=(const Point& p) +{ + X += p.X; + Y += p.Y; + return *this; +} + +Point& Point::operator-=(const Point& p) +{ + X -= p.X; + Y -= p.Y; + return *this; +} + +Point Point::operator+(const Point& p) const +{ + return { X + p.X, Y + p.Y }; +} + +Point Point::operator-(const Point& p) const +{ + return { X - p.X, Y - p.Y }; +} + +double Point::operator*(const Point& p) const +{ + return (X * p.X + Y * p.Y); +} + +Point Point::operator*(double f) const +{ + return { f * X, f * Y }; +} + +Point operator*(double f, const Point& p) +{ + return { f * p.X, f * p.Y }; +} + +std::ostream& operator<<(std::ostream& os, const Point& p) +{ + os << "(" << p.X << ", " << p.Y << ")"; + return os; +} diff --git a/chapter_08/examples/2_any/with_header_files/Point.hh b/chapter_08/examples/2_any/with_header_files/Point.hh new file mode 100644 index 0000000000000000000000000000000000000000..009c99a6dcfddc155364e2465cc31da8ec785e0f --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/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; + Point& operator=(const Point& p) = default; + Point& operator=(Point&& p) = default; + Point(double x, double y); + Point& operator+=(const Point& p); + Point& operator-=(const Point& p); + Point operator+(const Point& p) const; + Point operator-(const Point& p) const; + double operator*(const Point& p) const; + Point operator*(double f) const; +}; + +Point operator*(double f, const Point& p); +ostream& operator<<(ostream& os, const Point& p); + +#endif diff --git a/chapter_08/examples/2_any/with_header_files/Polygon.hh b/chapter_08/examples/2_any/with_header_files/Polygon.hh new file mode 100644 index 0000000000000000000000000000000000000000..8c71504ae0e64fe47441651b1a339997f1c2676c --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/Polygon.hh @@ -0,0 +1,53 @@ +#ifndef Polygon_HH +#define Polygon_HH +#include "Point.hh" +#include <array> +#include <cmath> +#include <string> + +template <unsigned int NV> +class Polygon { + static_assert(NV > 2, "Can't have polygon with less than 3 sides"); + +public: + Polygon() = default; + Polygon(const Polygon&) = default; + Polygon(Polygon&&) = default; + Polygon& operator=(const Polygon& pg) = default; + Polygon& operator=(Polygon&&) = default; + constexpr auto n_vertexes() const { return NV; } + std::string name() const { return "Polygon<" + std::to_string(NV) + ">"; } + + double perimeter() const + { + 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) + { + for (auto& pt : vertex) + pt += p; + } + + void rotate(double phi) + { + 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_08/examples/2_any/with_header_files/Triangle.cc b/chapter_08/examples/2_any/with_header_files/Triangle.cc new file mode 100644 index 0000000000000000000000000000000000000000..b27b4ae583252f7ad306f65146f668e1f49adbba --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/Triangle.cc @@ -0,0 +1,22 @@ +#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 double sqr(Point p) { return p * p; } + +Triangle::Triangle(Point p1, Point p2, Point p3) +{ + vertex[0] = p1; + vertex[1] = p2; + vertex[2] = p3; +} + +double Triangle::area() const +{ + 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_08/examples/2_any/with_header_files/Triangle.hh b/chapter_08/examples/2_any/with_header_files/Triangle.hh new file mode 100644 index 0000000000000000000000000000000000000000..c360fc50f97a275711454e8fe1eda0c0680b9fbe --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/Triangle.hh @@ -0,0 +1,9 @@ +#include "Polygon.hh" + +class Triangle : public Polygon<3> { +public: + using Polygon<3>::Polygon; + using Polygon<3>::operator=; + Triangle(Point p1, Point p2, Point p3); + double area() const; +}; diff --git a/chapter_08/examples/2_any/with_header_files/main.cc b/chapter_08/examples/2_any/with_header_files/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..7bbab1e58c41f6d88dbd02fef9b19bb6e0a86efc --- /dev/null +++ b/chapter_08/examples/2_any/with_header_files/main.cc @@ -0,0 +1,91 @@ +#include "Circle.hh" +#include "Triangle.hh" +#include <chrono> +#include <random> +#include <iostream> +#include <vector> +#include <any> + +constexpr auto N = 100000ul; +std::mt19937_64 engine; +std::discrete_distribution sel{ 0.5, 0.5 }; +std::exponential_distribution length{ 1.0 }; + +using element_type = std::any; + +auto name(const std::vector<element_type>& v, size_t i) +{ + if (v[i].type() == typeid(Triangle)) { + return std::any_cast<Triangle>(v[i]).name(); + } else { + return std::any_cast<Circle>(v[i]).name(); + } +} + +auto area(const std::vector<element_type>& v, size_t i) +{ + if (v[i].type() == typeid(Triangle)) { + return std::any_cast<Triangle>(v[i]).area(); + } else { + return std::any_cast<Circle>(v[i]).area(); + } +} + +void construct_objects(std::vector<element_type>& v) +{ + for (auto i = 0ul; i < N; ++i) { + auto isel = sel(engine); + switch (isel) { + case 0: { + auto radius = length(engine); + auto centrepos = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Circle>{}, radius, centrepos); + break; + } + case 1: { + auto v1 = Point(length(engine), length(engine)); + auto v2 = Point(length(engine), length(engine)); + auto v3 = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Triangle>{}, v1, v2, v3); + break; + } + }; + } +} + +void calc_area_all(const std::vector<element_type>& v) +{ + auto max_loc = 0ul; + auto max_area = 0.; + for (size_t i = 0; i < v.size(); ++i) { + auto ar = area(v, i); + if (i < 5) { + std::cout << i << ": " << name(v, i) << " with area " + << ar << "\n"; + } + if (ar > max_area) { + max_loc = i; + } + } + + std::cout << "Largest object: \n"; + auto nm = name(v, max_loc); + auto ar = area(v, max_loc); + std::cout << "Name : " << nm << ", area = " << ar << "\n"; +} + +int main() +{ + std::vector<element_type> shapes; + shapes.reserve(N); + + auto t0 = std::chrono::steady_clock::now(); + construct_objects(shapes); + auto t1 = std::chrono::steady_clock::now(); + calc_area_all(shapes); + auto t2 = std::chrono::steady_clock::now(); + std::cout << "Object creation time for " << N << " objects, " + << std::chrono::duration<double>(t1 - t0).count() << "\n" + << "Area evaluation time for " << N << " objects, " + << std::chrono::duration<double>(t2 - t1).count() << "\n"; +} diff --git a/chapter_08/examples/2_any/with_modules/CMakeLists.txt b/chapter_08/examples/2_any/with_modules/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..60572bd768811f0f3b5947266a9513ae1b23d4bd --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +project(polymorphic_with_any) + +add_executable(polyany) +target_sources(polyany + PUBLIC + main.cc +) +target_sources(polyany + PUBLIC + FILE_SET geometry_modules TYPE CXX_MODULES FILES + Point.ixx + Circle.ixx + Polygon.ixx + Triangle.ixx +) + diff --git a/chapter_08/examples/2_any/with_modules/Circle.ixx b/chapter_08/examples/2_any/with_modules/Circle.ixx new file mode 100644 index 0000000000000000000000000000000000000000..4793712c1f61e1ffb7d04a53ff039f4025120880 --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/Circle.ixx @@ -0,0 +1,51 @@ +module; +#include <string> +#include <numbers> +export module Circle; +import Point; + +using std::numbers::pi; + +export class Circle { +public: + Circle() = default; + Circle(double rad, const Point& p); + Circle(const Circle& cir) = default; + Circle(Circle&& cir) = default; + Circle& operator=(const Circle& cir) = default; + Circle& operator=(Circle&& cir) = default; + std::string name() const { return "Circle"; } + void rotate(double phi); + void translate(Point p); + double area() const; + double perimeter() const; + inline double circumference() const { return perimeter(); } + +private: + double r{ 1.0 }; + Point c{}; // Use default constructor of class Point to create c +}; + + +Circle::Circle(double rad, const Point& p) + : r{ rad } + , c{ p } +{ +} + +double Circle::area() const +{ + return pi * r * r; +} + +double Circle::perimeter() const +{ + return 2 * pi * r; +} + +void Circle::rotate(double phi) { phi = 0; } + +void Circle::translate(Point p) +{ + c += p; +} diff --git a/chapter_08/examples/2_any/with_modules/Point.ixx b/chapter_08/examples/2_any/with_modules/Point.ixx new file mode 100644 index 0000000000000000000000000000000000000000..436926852116750d9402f9e9fa808a121cdce786 --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/Point.ixx @@ -0,0 +1,73 @@ +export module Point; + +export struct Point { + double X = 0, Y = 0; + Point() = default; + Point(const Point&) = default; + Point(Point&&) = default; + Point& operator=(const Point& p) = default; + Point& operator=(Point&& p) = default; + Point(double x, double y); + Point& operator+=(const Point& p); + Point& operator-=(const Point& p); + Point operator+(const Point& p) const; + Point operator-(const Point& p) const; + double operator*(const Point& p) const; + Point operator*(double f) const; +}; + +export auto operator*(double f, const Point& p) -> Point; +// export auto operator<<(std::ostream& os, const Point& p) -> std::ostream&; + +Point::Point(double x, double y) + : X{ x } + , Y{ y } +{ +} + +Point& Point::operator+=(const Point& p) +{ + X += p.X; + Y += p.Y; + return *this; +} + +Point& Point::operator-=(const Point& p) +{ + X -= p.X; + Y -= p.Y; + return *this; +} + +Point Point::operator+(const Point& p) const +{ + return { X + p.X, Y + p.Y }; +} + +Point Point::operator-(const Point& p) const +{ + return { X - p.X, Y - p.Y }; +} + +double Point::operator*(const Point& p) const +{ + return (X * p.X + Y * p.Y); +} + +Point Point::operator*(double f) const +{ + return { f * X, f * Y }; +} + +Point operator*(double f, const Point& p) +{ + return { f * p.X, f * p.Y }; +} + +/* +std::ostream& operator<<(std::ostream& os, const Point& p) +{ + os << "(" << p.X << ", " << p.Y << ")"; + return os; +} +*/ diff --git a/chapter_08/examples/2_any/with_modules/Polygon.ixx b/chapter_08/examples/2_any/with_modules/Polygon.ixx new file mode 100644 index 0000000000000000000000000000000000000000..43ca1acb177bb4ac5ddbe3c5d3de20a21bb7ee6c --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/Polygon.ixx @@ -0,0 +1,52 @@ +module; +#include <cmath> +#include <array> +#include <string> +export module Polygon; +import Point; + +export template <unsigned int NV> +class Polygon { + static_assert(NV > 2, "Can't have polygon with less than 3 sides"); + +public: + Polygon() = default; + Polygon(const Polygon&) = default; + Polygon(Polygon&&) = default; + Polygon& operator=(const Polygon& pg) = default; + Polygon& operator=(Polygon&&) = default; + constexpr auto n_vertexes() const { return NV; } + inline std::string name() const { return "Polygon<" + std::to_string(NV) + ">"; } + + double perimeter() const + { + 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) + { + for (auto& pt : vertex) + pt += p; + } + + void rotate(double phi) + { + 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; +}; + diff --git a/chapter_08/examples/2_any/with_modules/Triangle.ixx b/chapter_08/examples/2_any/with_modules/Triangle.ixx new file mode 100644 index 0000000000000000000000000000000000000000..94a4b22783e70a43eea42577f21de8d4414b4df0 --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/Triangle.ixx @@ -0,0 +1,33 @@ +module; +#include <array> +#include <cmath> +export module Triangle; +import Polygon; +import Point; + +export class Triangle : public Polygon<3U> { +public: + using Polygon<3U>::Polygon; + using Polygon<3U>::operator=; + using Polygon<3U>::name; + Triangle(Point p1, Point p2, Point p3); + double area() const; +}; +// not exported +inline double sqr(Point p) { return p * p; } + +Triangle::Triangle(Point p1, Point p2, Point p3) +{ + vertex[0] = p1; + vertex[1] = p2; + vertex[2] = p3; +} + +double Triangle::area() const +{ + 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_08/examples/2_any/with_modules/clean.sh b/chapter_08/examples/2_any/with_modules/clean.sh new file mode 100755 index 0000000000000000000000000000000000000000..aff1f9ecfd5fea4825d71d4e1af2f0c4ccfe57f6 --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/clean.sh @@ -0,0 +1,4 @@ +rm -f *.o *.pcm *.gcm +rm -rf gcm.cache +rm -f a.clg a.gcc + diff --git a/chapter_08/examples/2_any/with_modules/main.cc b/chapter_08/examples/2_any/with_modules/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..c8860ee6e2fc807705193f05c8517e780ec51b99 --- /dev/null +++ b/chapter_08/examples/2_any/with_modules/main.cc @@ -0,0 +1,95 @@ +#include <chrono> +#include <random> +#include <iostream> +#include <vector> +#include <array> +#include <any> +import Point; +import Polygon; +import Circle; +import Triangle; + +constexpr auto N = 100000UL; +std::mt19937_64 engine; +std::discrete_distribution sel{ 0.5, 0.5 }; +std::exponential_distribution length{ 1.0 }; + +using element_type = std::any; + +auto name(const std::vector<element_type>& v, size_t i) +{ + if (v[i].type() == typeid(Triangle)) { + return std::any_cast<Triangle>(v[i]).name(); + } else { + return std::any_cast<Circle>(v[i]).name(); + } +} + +auto area(const std::vector<element_type>& v, size_t i) +{ + if (v[i].type() == typeid(Triangle)) { + return std::any_cast<Triangle>(v[i]).area(); + } else { + return std::any_cast<Circle>(v[i]).area(); + } +} + +void construct_objects(std::vector<element_type>& v) +{ + for (auto i = 0ul; i < N; ++i) { + auto isel = sel(engine); + switch (isel) { + case 0: { + auto radius = length(engine); + auto centrepos = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Circle>{}, radius, centrepos); + break; + } + case 1: { + auto v1 = Point(length(engine), length(engine)); + auto v2 = Point(length(engine), length(engine)); + auto v3 = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Triangle>{}, v1, v2, v3); + break; + } + }; + } +} + +void calc_area_all(const std::vector<element_type>& v) +{ + auto max_loc = 0ul; + auto max_area = 0.; + for (size_t i = 0; i < v.size(); ++i) { + auto ar = area(v, i); + if (i < 5) { + std::cout << i << ": " << name(v, i) << " with area " + << ar << "\n"; + } + if (ar > max_area) { + max_loc = i; + } + } + + std::cout << "Largest object: \n"; + auto nm = name(v, max_loc); + auto ar = area(v, max_loc); + std::cout << "Name : " << nm << ", area = " << ar << "\n"; +} + +auto main() -> int +{ + std::vector<element_type> shapes; + shapes.reserve(N); + + auto t0 = std::chrono::steady_clock::now(); + construct_objects(shapes); + auto t1 = std::chrono::steady_clock::now(); + calc_area_all(shapes); + auto t2 = std::chrono::steady_clock::now(); + std::cout << "Object creation time for " << N << " objects, " + << std::chrono::duration<double>(t1 - t0).count() << "\n" + << "Area evaluation time for " << N << " objects, " + << std::chrono::duration<double>(t2 - t1).count() << "\n"; +} + diff --git a/chapter_08/examples/advent.cc b/chapter_08/examples/advent.cc new file mode 100644 index 0000000000000000000000000000000000000000..f13cf01b80a5ff80f2dce05c9c756acb11790b0c --- /dev/null +++ b/chapter_08/examples/advent.cc @@ -0,0 +1,34 @@ +/* +Calculates the date of the last advent in a given +year. Usage: + +(i) advent # calculate for current year +(ii) advent 2049 # for 2049 +*/ + +import <chrono>; +import <print>; +auto current_year() -> std::chrono::year +{ + using namespace std::chrono; + year_month_day date { floor<days>(system_clock::now()) }; + return date.year(); +} + +auto main(int argc, char* argv[]) -> int +{ + using namespace std::chrono; + using namespace std::chrono_literals; + using Date = year_month_day; + + year Y { argc == 1 ? current_year() : year {std::stoi(argv[1]) }}; + + Date s4 { Y / December / Sunday[4] }; + Date s3 { Y / December / Sunday[3] }; + Date xmas { Y / December / 25d }; + Date lastadv { s4 >= xmas ? s3 : s4 }; + + std::print("The last advent for the year {} falls on {}th of December.\n", + static_cast<int>(Y), static_cast<unsigned>(lastadv.day())); +} + diff --git a/chapter_08/examples/darray_module/CMakeLists.txt b/chapter_08/examples/darray_module/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9272b8bcf43e6aa9904c7da892f2f472f3f7af66 --- /dev/null +++ b/chapter_08/examples/darray_module/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +project(darray_modules) + +add_executable(darray_w_modules) +target_sources(darray_w_modules + PUBLIC + main.cc +) +target_sources(darray_w_modules + PUBLIC + FILE_SET own_modules TYPE CXX_MODULES FILES + mymodule.ixx +) + diff --git a/chapter_08/examples/darray_module/README.md b/chapter_08/examples/darray_module/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8206692c98d06e63334ee0be1e2d0b650b221d1c --- /dev/null +++ b/chapter_08/examples/darray_module/README.md @@ -0,0 +1,5 @@ +clang++ -std=c++23 -stdlib=libc++ -fmodules --precompile -xc++-module mymodule.ixx +clang++ -std=c++23 -fmodules -c mymodule.pcm +clang++ -std=c++23 -stdlib=libc++ -fmodules -fprebuilt-module-path=. mymodule.o main.cc + + diff --git a/chapter_08/examples/darray_module/main.cc b/chapter_08/examples/darray_module/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..29ffc819c827dcff1825e6d37d3445d3e8ddde6d --- /dev/null +++ b/chapter_08/examples/darray_module/main.cc @@ -0,0 +1,21 @@ +#include <string> +#include <iostream> +import mymodule; + +auto main() -> int +{ + darray d1 { 1, 2, 3, 4, 5 }; // CTAD works! + //darray<int> d1{1, 2, 3, 4, 5}; + darray<std::string> d2 { "a", "b", "c" }; + // Explicit type required above, since "a", "b" etc are C-strings, and we want C++ strings + auto d3 = d1; //Copy + darray<int> d4 { 10 }; // This and the following line have a subtle difference + darray<int> d5(10); // This is the only situation where constructor calls with {} and () mean different things. + darray<std::string> d6 { std::move(d2) }; // d2 should be empty and d6 must have all data after this. + std::cout << "d1 = " << d1 << "\n"; + std::cout << "d2 = " << d2 << "\n"; + std::cout << "d3 = " << d3 << "\n"; + std::cout << "d4 = " << d4 << "\n"; + std::cout << "d5 = " << d5 << "\n"; + std::cout << "d6 = " << d6 << "\n"; +} diff --git a/chapter_08/examples/darray_module/mymodule.ixx b/chapter_08/examples/darray_module/mymodule.ixx new file mode 100644 index 0000000000000000000000000000000000000000..8c9f3bbab6290aadf88158fbb5395393c1c2c8a5 --- /dev/null +++ b/chapter_08/examples/darray_module/mymodule.ixx @@ -0,0 +1,103 @@ +module; +#include <initializer_list> +#include <iostream> +#include <memory> +export module mymodule; + +export template <class T> +class darray { +private: + // swap function + void swap(darray& oth) + { + std::swap(arr, oth.arr); + std::swap(sz, oth.sz); + } + std::unique_ptr<T[]> arr; + size_t sz = 0; + +public: + // read-only access for array elements + auto operator[](size_t i) const -> T { return arr[i]; } + // read-write access for array elements + auto operator[](size_t i) -> T& { return arr[i]; } + + // This is needed if you want to use range based for loops on your class + auto begin() const -> T* { return arr; } + // This is needed if you want to use range based for loops on your class + auto end() const -> T* { return arr + size(); } + + // returns the size + auto size() const -> decltype(sz) { return sz; } + + // Sums up array and returns the result + auto sum() const -> T + { + T a {}; + for (auto el : (*this)) { + a += el; + } // Use the range based for loop. //Well, why not! + return a; + } + + // Default constructor, also defaulted. + darray() = default; + // Destructor. With storage managed by a unique_ptr, we don't need to delete it. + ~darray() = default; + + // Constructor with a given size + darray(size_t N) + { + if (N != 0) { + arr = std::make_unique<T[]>(N); + sz = N; + } + } + + // Copy constructor + darray(const darray<T>& oth) + { + if (oth.sz != 0) { + sz = oth.sz; + arr = std::make_unique<T[]>(sz); + } + for (size_t i = 0; i < sz; ++i) + arr[i] = oth.arr[i]; + } + darray(darray<T>&& oth) noexcept + { + swap(oth); + } + // Initialiser list constructor + darray(std::initializer_list<T> l) + { + arr = std::make_unique<T[]>(l.size()); + sz = l.size(); + size_t i = 0; + for (auto el : l) + arr[i++] = el; + } + // Assignment operator using the copy and swap idiom + auto operator=(darray d) -> darray& + { + swap(d); + return *this; + } +}; + +// Output operator. No need to make it a friend, since we can use only +// public functions of the class to do all the work! +export template <typename T> +auto operator<<(std::ostream& os, const darray<T>& d) -> std::ostream& +{ + os << '['; + for (size_t i = 0; i < d.size(); ++i) { + os << d[i]; + if (i != (d.size() - 1)) + os << ','; + } + os << ']'; + return os; // This function returns os so that you can write cout << d1 << " other things\n"; + // That is interpreted as (cout << d1) << " other things\n"; +} + diff --git a/chapter_08/examples/feb.cc b/chapter_08/examples/feb.cc new file mode 100644 index 0000000000000000000000000000000000000000..1d7641b6df132948a1fea1f96e35f287a8a12457 --- /dev/null +++ b/chapter_08/examples/feb.cc @@ -0,0 +1,42 @@ +/* +Calculates the years in a given interval, in which +the month of February had 5 Sundays. + +Usage: +(i) feb # Start: current year, End: 100 years from now +(ii) feb 1975 # Start: 1975, End: current year +(iii) feb 2075 # Start: current year, End: 2075 +(iv) feb 1800 2000 # Start 1800, End 2000 + +Build: +clang++ -std=c++20 -stdlib=libc++ feb.cc -o feb + +*/ + +import <chrono>; +import <iostream>; + +auto current_year() -> std::chrono::year +{ + using namespace std::chrono; + year_month_day date { floor<days>(system_clock::now()) }; + return date.year(); +} + +auto main(int argc, char* argv[]) -> int +{ + using namespace std::chrono; + using namespace std::chrono_literals; + auto Y0 { current_year() }; + auto Y1 = Y0 + years{100}; + if (argc > 1) Y1 = year{std::stoi(argv[1])}; + if (argc > 2) Y0 = year{std::stoi(argv[2])}; + if (Y1 < Y0) std::swap(Y1, Y0); + + for (auto y = Y0; y < Y1; ++y) { + auto d = y / February / Sunday[5]; + if (d.ok()) + std::cout << static_cast<int>(y) << "\n"; + } +} + diff --git a/chapter_08/examples/hello_m.cc b/chapter_08/examples/hello_m.cc new file mode 100644 index 0000000000000000000000000000000000000000..f743015337ce7b049038e5ba97b0fca1e08ae90d --- /dev/null +++ b/chapter_08/examples/hello_m.cc @@ -0,0 +1,7 @@ +import <iostream>; + +auto main() -> int +{ + std::cout << "Hello, world!\n"; +} + diff --git a/chapter_08/examples/saxpy/with_header_files/README.md b/chapter_08/examples/saxpy/with_header_files/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0160e21ce20f5af2b2cea1bec9871d436d33623f --- /dev/null +++ b/chapter_08/examples/saxpy/with_header_files/README.md @@ -0,0 +1,9 @@ +To compile with clang: + +clang++ -std=c++23 -stdlib=libc++ usesaxpy.cc + +With gcc: + +g++ -std=c++23 usesaxpy.cc + + diff --git a/chapter_08/examples/saxpy/with_header_files/saxpy.hh b/chapter_08/examples/saxpy/with_header_files/saxpy.hh new file mode 100644 index 0000000000000000000000000000000000000000..b52279919867975fe7df82da673a55b6e071eae5 --- /dev/null +++ b/chapter_08/examples/saxpy/with_header_files/saxpy.hh @@ -0,0 +1,21 @@ +#ifndef SAXPY_HH +#define SAXPY_HH +#include <algorithm> +#include <span> + +template <class T> +concept Number = std::floating_point<T> + or std::integral<T>; +template <Number T> +auto saxpy(T a, std::span<const T> x, + std::span<const T> y, + std::span<T> z) +{ + std::transform(x.begin(), x.end(), + y.begin(), z.begin(), + [a](T X, T Y) { + return a * X + Y; + }); +} +#endif + diff --git a/chapter_08/examples/saxpy/with_header_files/usesaxpy.cc b/chapter_08/examples/saxpy/with_header_files/usesaxpy.cc new file mode 100644 index 0000000000000000000000000000000000000000..2a5386031e9b5790e757398d286489e9282fb5ed --- /dev/null +++ b/chapter_08/examples/saxpy/with_header_files/usesaxpy.cc @@ -0,0 +1,18 @@ +#include <iostream> +#include <array> +#include <vector> +#include <span> +#include "saxpy.hh" + +auto main() -> int +{ + using namespace std; + const array inp1 { 1., 2., 3., 4., 5. }; + const array inp2 { 9., 8., 7., 6., 5. }; + vector outp(inp1.size(), 0.); + + saxpy(10., {inp1}, {inp2}, {outp}); + for (auto x : outp) cout << x << "\n"; + cout << ":::::::::::::::::::::\n"; +} + diff --git a/chapter_08/examples/saxpy/with_modules/CMakeLists.txt b/chapter_08/examples/saxpy/with_modules/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d480a51ccd817cf0e1cc8b6494b4dc3aec31080f --- /dev/null +++ b/chapter_08/examples/saxpy/with_modules/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.28) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +project(use_saxpy-example) + +add_executable(use_saxpy) +target_sources(use_saxpy + PUBLIC + usesaxpy.cc +) +target_sources(use_saxpy + PUBLIC + FILE_SET saxpy_module TYPE CXX_MODULES FILES + saxpy.ixx +) + diff --git a/chapter_08/examples/saxpy/with_modules/README.md b/chapter_08/examples/saxpy/with_modules/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9841fe5317e832131a2c507f8d31f6fccee81711 --- /dev/null +++ b/chapter_08/examples/saxpy/with_modules/README.md @@ -0,0 +1,7 @@ +Compile with clang: + +mkdir build +cd build +CC=clang CXX=clang++ cmake -DCMAKE_GENERATOR=Ninja .. +ninja + diff --git a/chapter_08/examples/saxpy/with_modules/saxpy.ixx b/chapter_08/examples/saxpy/with_modules/saxpy.ixx new file mode 100644 index 0000000000000000000000000000000000000000..47a8a348ed4b296b74159d5b4474d57467e32bc3 --- /dev/null +++ b/chapter_08/examples/saxpy/with_modules/saxpy.ixx @@ -0,0 +1,29 @@ +/* We are using #includes rather than imports +to be able to compile with gcc. Clang works +fine with the imports of STL headers. GCC, +even after generating header units with +-xc++-system-header crashes with an internal +compiler error. +*/ + +module; +#include <algorithm> +#include <span> +export module saxpy; + +template <class T> +concept Number = std::floating_point<T> + or std::integral<T>; + +export template <Number T> +auto saxpy(T a, std::span<const T> x, + std::span<const T> y, + std::span<T> z) +{ + std::transform(x.begin(), x.end(), + y.begin(), z.begin(), + [a](T X, T Y) { + return a * X + Y; + }); +} + diff --git a/chapter_08/examples/saxpy/with_modules/usesaxpy.cc b/chapter_08/examples/saxpy/with_modules/usesaxpy.cc new file mode 100644 index 0000000000000000000000000000000000000000..54fd6eb5420189b492dac7b557d7441d14455073 --- /dev/null +++ b/chapter_08/examples/saxpy/with_modules/usesaxpy.cc @@ -0,0 +1,18 @@ +#include <iostream> +#include <array> +#include <vector> +#include <span> +import saxpy; + +auto main() -> int +{ + using namespace std; + const array inp1 { 1., 2., 3., 4., 5. }; + const array inp2 { 9., 8., 7., 6., 5. }; + vector outp(inp1.size(), 0.); + + saxpy(10., {inp1}, {inp2}, {outp}); + for (auto x : outp) cout << x << "\n"; + cout << ":::::::::::::::::::::\n"; +} +