Initial Course

This commit is contained in:
David Gil de Gómez Pérez
2026-08-11 17:40:36 +03:00
commit 9aed1d1d86
202 changed files with 3420 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
add_course_exercise(NAME constexpr_frontier MODULE 14_cpp26 STANDARD 23
SOURCES src/constexpr_frontier.cpp test/constexpr_frontier_test.cpp)
add_course_exercise(NAME monadic_expected MODULE 14_cpp26 STANDARD 23
SOURCES src/monadic_expected.cpp test/monadic_expected_test.cpp)
add_course_exercise(NAME pipeline_ranges MODULE 14_cpp26 STANDARD 23
SOURCES src/pipeline_ranges.cpp test/pipeline_ranges_test.cpp)
add_course_exercise(NAME custom_formatter MODULE 14_cpp26 STANDARD 23
SOURCES src/custom_formatter.cpp test/custom_formatter_test.cpp)
+20
View File
@@ -0,0 +1,20 @@
# Module 14: C++26 and the Modern Frontier
## Learning Goals
- Push `constexpr` and `consteval` to compile-time limits
- Chain operations on `std::expected` in a monadic style
- Build expressive ranges pipelines for real data tasks
- Specialize `std::formatter` for domain types (library direction through C++26)
> **Note:** C++26 is still evolving. These exercises use C++23 as the baseline and
> focus on techniques and library directions that remain central in C++26.
## Exercises
| Exercise | Topic |
|----------|-------|
| `constexpr_frontier` | consteval, compile-time algorithms |
| `monadic_expected` | Monadic expected composition |
| `pipeline_ranges` | Advanced ranges pipelines |
| `custom_formatter` | std::formatter specialization |
@@ -0,0 +1,12 @@
#pragma once
#include <array>
#include <cstddef>
namespace cpp26 {
[[nodiscard]] auto compile_time_factorial(int n) -> long long;
[[nodiscard]] auto compile_time_sum(std::array<int, 5> values) -> int;
[[nodiscard]] auto is_sorted(std::array<int, 5> values) -> bool;
} // namespace cpp26
@@ -0,0 +1,24 @@
#pragma once
#include <format>
#include <string>
namespace cpp26 {
struct Point {
int x;
int y;
};
[[nodiscard]] auto format_point(const Point& point) -> std::string;
[[nodiscard]] auto format_points(const Point& a, const Point& b) -> std::string;
} // namespace cpp26
template <>
struct std::formatter<cpp26::Point> : std::formatter<std::string> {
auto format(const cpp26::Point& point, std::format_context& ctx) const {
return std::formatter<std::string>::format(
std::format("({}, {})", point.x, point.y), ctx);
}
};
@@ -0,0 +1,14 @@
#pragma once
#include <expected>
#include <string>
namespace cpp26 {
enum class ErrorCode { NotFound, Invalid };
[[nodiscard]] auto parse_id(const std::string& text) -> std::expected<int, ErrorCode>;
[[nodiscard]] auto load_user(int id) -> std::expected<std::string, ErrorCode>;
[[nodiscard]] auto load_user_name(const std::string& text) -> std::expected<std::string, ErrorCode>;
} // namespace cpp26
@@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <vector>
namespace cpp26 {
struct Record {
std::string category;
int value;
};
[[nodiscard]] auto top_values_by_category(const std::vector<Record>& records, int min_value)
-> std::vector<Record>;
[[nodiscard]] auto total_by_category(const std::vector<Record>& records)
-> std::vector<std::pair<std::string, int>>;
} // namespace cpp26
@@ -0,0 +1,20 @@
#include "constexpr_frontier.hpp"
namespace cpp26 {
auto compile_time_factorial(int n) -> long long {
(void)n;
return 1;
}
auto compile_time_sum(std::array<int, 5> values) -> int {
(void)values;
return 0;
}
auto is_sorted(std::array<int, 5> values) -> bool {
(void)values;
return false;
}
} // namespace cpp26
+15
View File
@@ -0,0 +1,15 @@
#include "custom_formatter.hpp"
namespace cpp26 {
auto format_point(const Point& point) -> std::string {
(void)point;
return {};
}
auto format_points(const Point& a, const Point& b) -> std::string {
(void)a; (void)b;
return {};
}
} // namespace cpp26
+20
View File
@@ -0,0 +1,20 @@
#include "monadic_expected.hpp"
namespace cpp26 {
auto parse_id(const std::string& text) -> std::expected<int, ErrorCode> {
(void)text;
return std::unexpected(ErrorCode::Invalid);
}
auto load_user(int id) -> std::expected<std::string, ErrorCode> {
(void)id;
return std::unexpected(ErrorCode::NotFound);
}
auto load_user_name(const std::string& text) -> std::expected<std::string, ErrorCode> {
(void)text;
return std::unexpected(ErrorCode::Invalid);
}
} // namespace cpp26
+17
View File
@@ -0,0 +1,17 @@
#include "pipeline_ranges.hpp"
namespace cpp26 {
auto top_values_by_category(const std::vector<Record>& records, int min_value)
-> std::vector<Record> {
(void)records; (void)min_value;
return {};
}
auto total_by_category(const std::vector<Record>& records)
-> std::vector<std::pair<std::string, int>> {
(void)records;
return {};
}
} // namespace cpp26
@@ -0,0 +1,15 @@
#include "constexpr_frontier.hpp"
#include <gtest/gtest.h>
TEST(ConstexprFrontier, Factorial) {
EXPECT_EQ(cpp26::compile_time_factorial(5), 120);
}
TEST(ConstexprFrontier, CompileTimeSum) {
EXPECT_EQ(cpp26::compile_time_sum({1, 2, 3, 4, 5}), 15);
}
TEST(ConstexprFrontier, IsSorted) {
EXPECT_TRUE(cpp26::is_sorted({1, 2, 3, 4, 5}));
EXPECT_FALSE(cpp26::is_sorted({1, 3, 2, 4, 5}));
}
@@ -0,0 +1,10 @@
#include "custom_formatter.hpp"
#include <gtest/gtest.h>
TEST(CustomFormatter, FormatPoint) {
EXPECT_EQ(cpp26::format_point({3, 4}), "(3, 4)");
}
TEST(CustomFormatter, FormatPoints) {
EXPECT_EQ(cpp26::format_points({1, 2}, {3, 4}), "(1, 2) -> (3, 4)");
}
@@ -0,0 +1,8 @@
#include "monadic_expected.hpp"
#include <gtest/gtest.h>
TEST(MonadicExpected, LoadUserName) {
EXPECT_EQ(*cpp26::load_user_name("42"), "User-42");
EXPECT_EQ(cpp26::load_user_name("x"), std::unexpected(cpp26::ErrorCode::Invalid));
EXPECT_EQ(cpp26::load_user_name("999"), std::unexpected(cpp26::ErrorCode::NotFound));
}
@@ -0,0 +1,19 @@
#include "pipeline_ranges.hpp"
#include <gtest/gtest.h>
TEST(PipelineRanges, TopValuesByCategory) {
const std::vector<cpp26::Record> records{
{"math", 10}, {"math", 50}, {"code", 30}, {"code", 5}};
const auto result = cpp26::top_values_by_category(records, 20);
ASSERT_EQ(result.size(), 2U);
EXPECT_EQ(result[0].category, "math");
EXPECT_EQ(result[0].value, 50);
}
TEST(PipelineRanges, TotalByCategory) {
const std::vector<cpp26::Record> records{{"math", 10}, {"math", 5}, {"code", 7}};
const auto totals = cpp26::total_by_category(records);
ASSERT_EQ(totals.size(), 2U);
EXPECT_EQ(totals[0].first, "code");
EXPECT_EQ(totals[0].second, 7);
}