diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 90e94ea..83f25f5 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -15,5 +15,5 @@ jobs: - name: Run clang-format style check for C/C++/Protobuf programs. uses: jidicula/clang-format-action@v4.13.0 with: - clang-format-version: '16' + clang-format-version: '19' check-path: ${{ github.workspace }} diff --git a/CMakeLists.txt b/CMakeLists.txt index b3d20a8..3480f66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ include_directories(shared) ##### subdirectories ##### -set(DIRS week-W week-2 week-3 week-4 week-5 week-6 week-7 week-8 week-9 week-10 week-11) +set(DIRS week-W week-2 week-3 week-4 week-5 week-6 week-7 week-8 week-9 week-10 week-11 week-12) foreach(DIR ${DIRS}) add_subdirectory(${DIR}) endforeach() diff --git a/week-12/CMakeLists.txt b/week-12/CMakeLists.txt new file mode 100644 index 0000000..480dea5 --- /dev/null +++ b/week-12/CMakeLists.txt @@ -0,0 +1,4 @@ +set(DIRS task-11-01 task-11-03 task-11-06) +foreach(DIR ${DIRS}) + add_subdirectory(${DIR}) +endforeach() diff --git a/week-12/task-11-01/CMakeLists.txt b/week-12/task-11-01/CMakeLists.txt new file mode 100644 index 0000000..0857059 --- /dev/null +++ b/week-12/task-11-01/CMakeLists.txt @@ -0,0 +1,3 @@ +include_directories(include) + +add_subdirectory(test) diff --git a/week-12/task-11-01/include/self-pointed-function.hpp b/week-12/task-11-01/include/self-pointed-function.hpp new file mode 100644 index 0000000..39d1085 --- /dev/null +++ b/week-12/task-11-01/include/self-pointed-function.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace functions { + +/** + * function returns itself. + */ +std::function do_it(int& param) { + param++; + static int counter = 1; + std::cout << "do it #" << counter << ": param = " << param << std::endl; + counter++; + return [](int& p) { do_it(p); }; +} + +void* foo(); +template +void* addressF() { + return reinterpret_cast(F); +} +/** + * function returns pointer to itself. + */ +void* foo() { return addressF(); } + +} // namespace functions diff --git a/week-12/task-11-01/test/CMakeLists.txt b/week-12/task-11-01/test/CMakeLists.txt new file mode 100644 index 0000000..a18d496 --- /dev/null +++ b/week-12/task-11-01/test/CMakeLists.txt @@ -0,0 +1 @@ +cpp_test(test-11-01.cpp) diff --git a/week-12/task-11-01/test/test-11-01.cpp b/week-12/task-11-01/test/test-11-01.cpp new file mode 100644 index 0000000..bb9a0de --- /dev/null +++ b/week-12/task-11-01/test/test-11-01.cpp @@ -0,0 +1,20 @@ +#include "gtest/gtest.h" +#include "self-pointed-function.hpp" + +using functions::do_it; +using functions::foo; + +TEST(FunctionReternsItself, Call) { + constexpr int initial = 42; + int var = initial; // NOLINT + auto do_it_again = do_it(var); + EXPECT_EQ(var, initial + 1); + do_it_again(var); + EXPECT_EQ(var, initial + 2); +} + +TEST(SelfPointedFunction, Call) { + void* pfoo = foo(); + auto func_ptr = reinterpret_cast(pfoo); + EXPECT_EQ(func_ptr(), foo()); +} diff --git a/week-12/task-11-03/CMakeLists.txt b/week-12/task-11-03/CMakeLists.txt new file mode 100644 index 0000000..0857059 --- /dev/null +++ b/week-12/task-11-03/CMakeLists.txt @@ -0,0 +1,3 @@ +include_directories(include) + +add_subdirectory(test) diff --git a/week-12/task-11-03/include/factory.hpp b/week-12/task-11-03/include/factory.hpp new file mode 100644 index 0000000..cd07b57 --- /dev/null +++ b/week-12/task-11-03/include/factory.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace patterns { + +template +class Factory { +public: + using creator_type = std::variant...>; + explicit Factory(std::function... args) : map_{{typeid(Ts), creator_type(args)}...} {} + + template + T create() const { + return std::get>(map_.find(typeid(T))->second)(); + } + +private: + std::unordered_map...>> map_; +}; + +} // namespace patterns diff --git a/week-12/task-11-03/test/CMakeLists.txt b/week-12/task-11-03/test/CMakeLists.txt new file mode 100644 index 0000000..6645278 --- /dev/null +++ b/week-12/task-11-03/test/CMakeLists.txt @@ -0,0 +1 @@ +cpp_test(test-11-03.cpp) diff --git a/week-12/task-11-03/test/test-11-03.cpp b/week-12/task-11-03/test/test-11-03.cpp new file mode 100644 index 0000000..f43e819 --- /dev/null +++ b/week-12/task-11-03/test/test-11-03.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +#include "factory.hpp" +#include "gtest/gtest.h" + +using patterns::Factory; + +struct Point { + int x, y; + bool operator==(const Point& other) const { return x == other.x && y == other.y; } +}; + +class Person { +public: + Person(std::string name, int age) : name_(std::move(name)), age_(age) {} + std::string getName() const { return name_; } + int getAge() const { return age_; } + bool operator==(const Person& other) const { + return name_ == other.name_ && age_ == other.age_; + } + +private: + std::string name_; + int age_; +}; + +TEST(Factory, StandardTypes) { + constexpr int ret1 = 42; + constexpr double ret2 = 42.42; + const Factory factory{[]() { return ret1; }, []() { return ret2; }}; + EXPECT_EQ(factory.create(), ret1); + EXPECT_FLOAT_EQ(factory.create(), ret2); +} + +TEST(Factory, CustomTypes) { + const Point point{10, 20}; + const Person person{"Alice", 30}; + const Factory factory{[point]() { return point; }, + [person]() { return person; }}; + EXPECT_EQ(factory.create(), point); + EXPECT_EQ(factory.create(), person); +} + +// @todo #15:30m/DEV find a way to check if a compilation error has occurred or not. +#ifdef TEST_COMPILE_ERROR +TEST(Factory, UnregisteredType) { + const Factory factory{[]() { return 42; }, []() { return 3.14; }}; + EXPECT_THROW(factory.create(), std::runtime_error); +} + +TEST(Factory, EmptyFactory) { + const Factory factory{}; + EXPECT_THROW(factory.create(), std::runtime_error); + EXPECT_THROW(factory.create(), std::runtime_error); +} +#endif + +TEST(Factory, ComplexConstructors) { + const Factory, std::string> factory{ + []() { return std::vector{1, 2, 3}; }, []() { return std::string("Hello, World!"); }}; + const std::vector expected_vector{1, 2, 3}; + const std::string expected_string = "Hello, World!"; + EXPECT_EQ(factory.create>(), expected_vector); + EXPECT_EQ(factory.create(), expected_string); +} + +TEST(Factory, SmartPointers) { + constexpr int ret1 = 42; + constexpr double ret2 = 3.14; + const Factory, std::shared_ptr> factory{ + [ret1]() { return std::make_unique(ret1); }, + [ret2]() { return std::make_shared(ret2); }}; + auto unique_ptr = factory.create>(); + auto shared_ptr = factory.create>(); + EXPECT_EQ(*unique_ptr, ret1); + EXPECT_DOUBLE_EQ(*shared_ptr, ret2); +} + +TEST(Factory, OptionalTypes) { + const Factory, std::optional> factory{ + []() { return std::optional(42); }, // NOLINT + []() { return std::optional("Hello"); }}; + EXPECT_EQ(factory.create>().value(), 42); + EXPECT_EQ(factory.create>().value(), "Hello"); +} diff --git a/week-12/task-11-06/CMakeLists.txt b/week-12/task-11-06/CMakeLists.txt new file mode 100644 index 0000000..0857059 --- /dev/null +++ b/week-12/task-11-06/CMakeLists.txt @@ -0,0 +1,3 @@ +include_directories(include) + +add_subdirectory(test) diff --git a/week-12/task-11-06/include/circle.hpp b/week-12/task-11-06/include/circle.hpp new file mode 100644 index 0000000..cb10f6a --- /dev/null +++ b/week-12/task-11-06/include/circle.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "ivisitor.hpp" +#include "shape.hpp" + +namespace geometry2d { + +class Circle : public Shape { +public: + static constexpr double pi = std::numbers::pi; + + explicit Circle(double r) : radius_(r) { + if (radius_ <= 0) { + throw std::invalid_argument("Radius must be positive."); + } + } + + double radius() const { return radius_; } + + double perimeter() const override { return 2 * pi * radius_; } + double area() const override { return pi * radius_ * radius_; } + + void visit_by(const IVisitor& visitor) const override { visitor.visit(*this); } + +private: + double radius_; +}; + +} // namespace geometry2d diff --git a/week-12/task-11-06/include/ivisitor.hpp b/week-12/task-11-06/include/ivisitor.hpp new file mode 100644 index 0000000..cd264ad --- /dev/null +++ b/week-12/task-11-06/include/ivisitor.hpp @@ -0,0 +1,19 @@ +#pragma once + +namespace geometry2d { + +class Circle; +class Square; +class Triangle; +class Shape; + +class IVisitor { +public: + virtual ~IVisitor() = default; + + virtual void visit(const Circle& c) const = 0; + virtual void visit(const Square& s) const = 0; + virtual void visit(const Triangle& t) const = 0; +}; + +} // namespace geometry2d diff --git a/week-12/task-11-06/include/serializer.hpp b/week-12/task-11-06/include/serializer.hpp new file mode 100644 index 0000000..ca3428d --- /dev/null +++ b/week-12/task-11-06/include/serializer.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "circle.hpp" +#include "ivisitor.hpp" +#include "square.hpp" +#include "triangle.hpp" + +namespace geometry2d { + +class Serializer : public IVisitor { +public: + void visit(const Circle &c) const override { + std::cout << "Circle:" << std::endl; + std::cout << "\tradius: " << c.radius() << std::endl; + std::cout << "\t\tperimeter: " << c.perimeter() << std::endl; + std::cout << "\t\tarea: " << c.area() << std::endl; + } + void visit(const Square &s) const override { + std::cout << "Square:" << std::endl; + std::cout << "\tside: " << s.side() << std::endl; + std::cout << "\t\tperimeter: " << s.perimeter() << std::endl; + std::cout << "\t\tarea: " << s.area() << std::endl; + } + void visit(const Triangle &t) const override { + std::cout << "Triangle:" << std::endl; + std::cout << "\tside1: " << t.side1() << std::endl; + std::cout << "\tside2: " << t.side2() << std::endl; + std::cout << "\tside3: " << t.side3() << std::endl; + std::cout << "\t\tperimeter: " << t.perimeter() << std::endl; + std::cout << "\t\tarea: " << t.area() << std::endl; + } +}; + +} // namespace geometry2d diff --git a/week-12/task-11-06/include/shape.hpp b/week-12/task-11-06/include/shape.hpp new file mode 100644 index 0000000..6959161 --- /dev/null +++ b/week-12/task-11-06/include/shape.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "ivisitor.hpp" + +namespace geometry2d { + +class Shape { +public: + virtual ~Shape() = default; + + virtual void visit_by(const IVisitor& visitor) const = 0; + virtual double perimeter() const = 0; + virtual double area() const = 0; +}; + +} // namespace geometry2d diff --git a/week-12/task-11-06/include/square.hpp b/week-12/task-11-06/include/square.hpp new file mode 100644 index 0000000..75bf96a --- /dev/null +++ b/week-12/task-11-06/include/square.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "ivisitor.hpp" +#include "shape.hpp" + +namespace geometry2d { + +class Square : public Shape { +public: + explicit Square(double s) : side_(s) { + if (side_ <= 0) { + throw std::invalid_argument("Side must be positive."); + } + } + + double side() const { return side_; } + + double perimeter() const override { return 4 * side_; } + double area() const override { return side_ * side_; } + + void visit_by(const IVisitor& visitor) const override { visitor.visit(*this); } + +private: + double side_; +}; + +} // namespace geometry2d diff --git a/week-12/task-11-06/include/triangle.hpp b/week-12/task-11-06/include/triangle.hpp new file mode 100644 index 0000000..1be0c71 --- /dev/null +++ b/week-12/task-11-06/include/triangle.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "shape.hpp" + +namespace geometry2d { + +class Triangle : public Shape { +public: + Triangle(double s1, double s2, double s3) : side1_(s1), side2_(s2), side3_(s3) { + if (side1_ <= 0 || side2_ <= 0 || side3_ <= 0) { + throw std::invalid_argument("All sides must be positive."); + } + if (side1_ + side2_ <= side3_ || side1_ + side3_ <= side2_ || side2_ + side3_ <= side1_) { + throw std::invalid_argument("Invalid triangle sides."); + } + } + + double side1() const { return side1_; } + double side2() const { return side2_; } + double side3() const { return side3_; } + + double perimeter() const override { return side1_ + side2_ + side3_; } + double area() const override { + const double p = perimeter() / 2; + return std::sqrt(p * (p - side1_) * (p - side2_) * (p - side3_)); + } + + void visit_by(const IVisitor& visitor) const override { visitor.visit(*this); } + +private: + double side1_, side2_, side3_; +}; + +} // namespace geometry2d diff --git a/week-12/task-11-06/test/CMakeLists.txt b/week-12/task-11-06/test/CMakeLists.txt new file mode 100644 index 0000000..6162276 --- /dev/null +++ b/week-12/task-11-06/test/CMakeLists.txt @@ -0,0 +1 @@ +cpp_test(test-11-06.cpp) diff --git a/week-12/task-11-06/test/test-11-06.cpp b/week-12/task-11-06/test/test-11-06.cpp new file mode 100644 index 0000000..5cffc02 --- /dev/null +++ b/week-12/task-11-06/test/test-11-06.cpp @@ -0,0 +1,65 @@ +#include + +#include +#include +#include + +#include "serializer.hpp" + +using geometry2d::Circle; +using geometry2d::Serializer; +using geometry2d::Shape; +using geometry2d::Square; +using geometry2d::Triangle; + +#define EXPECT_STDOUT_EQ(printing_statement, expected_output) \ + { \ + testing::internal::CaptureStdout(); \ + printing_statement; \ + const std::string output = testing::internal::GetCapturedStdout(); \ + EXPECT_EQ(output, expected_output); \ + } + +TEST(Serializer, ShapeCircle) { + const Serializer serializer{}; + const std::unique_ptr shape = std::make_unique(4); + const std::string expected_output = + "Circle:\n" + "\tradius: 4\n" + "\t\tperimeter: 25.1327\n" + "\t\tarea: 50.2655\n"; + EXPECT_STDOUT_EQ(shape->visit_by(serializer), expected_output); +} + +TEST(Serializer, ShapeSquare) { + const Serializer serializer{}; + const std::unique_ptr shape = std::make_unique(5); + const std::string expected_output = + "Square:\n" + "\tside: 5\n" + "\t\tperimeter: 20\n" + "\t\tarea: 25\n"; + EXPECT_STDOUT_EQ(shape->visit_by(serializer), expected_output); +} + +TEST(Serializer, ShapeTriangle) { + const Serializer serializer{}; + const std::unique_ptr shape = std::make_unique(3, 4, 5); + const std::string expected_output = + "Triangle:\n" + "\tside1: 3\n" + "\tside2: 4\n" + "\tside3: 5\n" + "\t\tperimeter: 12\n" + "\t\tarea: 6\n"; + EXPECT_STDOUT_EQ(shape->visit_by(serializer), expected_output); +} + +TEST(Serializer, InvalidCircleRadius) { EXPECT_THROW(Circle(-1), std::invalid_argument); } + +TEST(Serializer, InvalidSquareSide) { EXPECT_THROW(Square(0), std::invalid_argument); } + +TEST(Serializer, InvalidTriangleSides) { + EXPECT_THROW(Triangle(1, 2, 3), std::invalid_argument); + EXPECT_THROW(Triangle(-1, 2, 2), std::invalid_argument); +}