commit 9aed1d1d86f2f22ea2c6c7a4257d504df2cb228e Author: David Gil de Gómez Pérez Date: Tue Aug 11 17:40:36 2026 +0300 Initial Course diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca938c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Build directories +build/ +cmake-build-*/ +out/ + +# CLion / IDE +.idea/ +*.iml +.vscode/ + +# Compiled artifacts +*.o +*.obj +*.a +*.so +*.dylib +*.exe + +# Test artifacts +Testing/ +*.gcno +*.gcda + +# OS +.DS_Store +Thumbs.db diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7fa4821 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.20) + +project( + modern_cpp_course + VERSION 1.0.0 + DESCRIPTION "Modern C++ course from basics to C++26" + LANGUAGES CXX +) + +set(CMAKE_CXX_STANDARD 26) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(COURSE_BUILD_TESTS "Build Google Test exercise targets" ON) +option(COURSE_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(CourseOptions) +include(CourseExercise) + +if(COURSE_BUILD_TESTS) + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.15.2 + ) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + enable_testing() + include(GoogleTest) +endif() + +add_subdirectory(modules) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e2cd28 --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +# Modern C++ Course (Basics → C++26) + +A hands-on C++ course with **56 Google Test exercises** organized into **14 modules**, from your first functions to modern standard-library features. + +## Requirements + +- **CMake** 3.20+ +- **C++ compiler** with C++23 support for later modules + - Apple Clang 15+, GCC 13+, or MSVC 19.34+ recommended +- **CLion** (recommended) or any CMake-aware IDE +- Internet access on first configure (Google Test is fetched automatically) + +## Quick Start (CLion) + +1. Open this folder in CLion (`File → Open…`). +2. CLion detects `CMakeLists.txt` and configures automatically. +3. Pick an exercise target (e.g. `01_fundamentals_hello_world`) in the run configuration dropdown. +4. Implement the TODOs in the matching `src/*.cpp` file. +5. Run the test target until all assertions pass. + +## Quick Start (Terminal) + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug +cmake --build build +ctest --test-dir build --output-on-failure +``` + +Run a single exercise: + +```bash +cmake --build build --target 01_fundamentals_hello_world +./build/modules/01_fundamentals/01_fundamentals_hello_world +``` + +## Project Structure + +``` +cppc/ +├── CMakeLists.txt # Root project + Google Test +├── cmake/ +│ ├── CourseExercise.cmake # add_course_exercise() helper +│ └── CourseOptions.cmake # Warnings and standards +└── modules/ + ├── 01_fundamentals/ # Types, operators, I/O + ├── 02_control_flow/ # if, switch, loops + ├── 03_functions/ # Functions and recursion + ├── 04_arrays_and_strings/ # Arrays, string, vector + ├── 05_pointers_and_references/ + ├── 06_oop_basics/ # Classes, inheritance + ├── 07_stl_containers/ + ├── 08_stl_algorithms/ + ├── 09_cpp11/ # C++11 features + ├── 10_cpp14/ + ├── 11_cpp17/ + ├── 12_cpp20/ # Concepts, ranges, <=> + ├── 13_cpp23/ # expected, mdspan, format + └── 14_cpp26/ # Frontier / C++26 direction +``` + +Each module contains: + +- `README.md` — learning goals and exercise list +- `include/` — function and class declarations (your contract) +- `src/` — **your implementation** (start here) +- `test/` — Google Test files (do not edit unless extending) + +## How to Study + +1. Read the module `README.md`. +2. Open the exercise header to understand the required API. +3. Implement TODOs in the corresponding `src/` file. +4. Run that exercise’s test target in CLion or via CTest. +5. Move to the next exercise only when tests pass. + +## Course Map + +| Module | Topic | Exercises | C++ Standard | +|--------|-------|-----------|--------------| +| 01 | Fundamentals | 4 | C++17 | +| 02 | Control flow | 4 | C++17 | +| 03 | Functions | 4 | C++17 | +| 04 | Arrays & strings | 4 | C++17 | +| 05 | Pointers & references | 4 | C++17 | +| 06 | OOP basics | 4 | C++17 | +| 07 | STL containers | 4 | C++17 | +| 08 | STL algorithms | 4 | C++17 | +| 09 | C++11 | 4 | C++11 | +| 10 | C++14 | 4 | C++14 | +| 11 | C++17 | 4 | C++17 | +| 12 | C++20 | 4 | C++20 | +| 13 | C++23 | 4 | C++23 | +| 14 | C++26 frontier | 4 | C++23+ | + +## CMake Options + +| Option | Default | Description | +|--------|---------|-------------| +| `COURSE_BUILD_TESTS` | ON | Build Google Test exercise targets | +| `COURSE_WARNINGS_AS_ERRORS` | OFF | Treat warnings as errors | + +Example: + +```bash +cmake -S . -B build -DCOURSE_WARNINGS_AS_ERRORS=ON +``` + +## CLion Tips + +- Use **Run | Run…** and filter by module prefix (e.g. `12_cpp20`). +- Enable **Google Test** integration in CLion to see individual tests in the tree view. +- Set breakpoints in your `src/` implementation while debugging failing tests. +- `compile_commands.json` is exported for clangd/clang-tidy if you use external tools. + +## Notes on C++26 + +C++26 is still in active standardization. Module 14 focuses on techniques and library directions that remain relevant—deep `constexpr`, monadic `expected`, ranges pipelines, and `std::formatter`—using C++23 as the practical baseline. + +Happy learning! diff --git a/cmake/CourseExercise.cmake b/cmake/CourseExercise.cmake new file mode 100644 index 0000000..f7022d8 --- /dev/null +++ b/cmake/CourseExercise.cmake @@ -0,0 +1,52 @@ +function(add_course_exercise) + set(options "") + set(oneValueArgs NAME MODULE STANDARD) + set(multiValueArgs SOURCES) + cmake_parse_arguments( + EXERCISE + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + + if(NOT EXERCISE_NAME) + message(FATAL_ERROR "add_course_exercise requires NAME") + endif() + if(NOT EXERCISE_MODULE) + message(FATAL_ERROR "add_course_exercise requires MODULE") + endif() + if(NOT EXERCISE_SOURCES) + message(FATAL_ERROR "add_course_exercise requires SOURCES") + endif() + + if(NOT EXERCISE_STANDARD) + set(EXERCISE_STANDARD 23) + endif() + + set(target_name "${EXERCISE_MODULE}_${EXERCISE_NAME}") + + add_executable(${target_name} ${EXERCISE_SOURCES}) + target_include_directories( + ${target_name} + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" + ) + course_set_cxx_standard(${target_name} ${EXERCISE_STANDARD}) + course_apply_warnings(${target_name}) + + if(COURSE_BUILD_TESTS) + target_link_libraries(${target_name} PRIVATE GTest::gtest_main) + gtest_discover_tests( + ${target_name} + DISCOVERY_MODE PRE_TEST + PROPERTIES LABELS "${EXERCISE_MODULE};${EXERCISE_NAME}" + ) + endif() + + set_target_properties( + ${target_name} + PROPERTIES + FOLDER "modules/${EXERCISE_MODULE}" + ) +endfunction() diff --git a/cmake/CourseOptions.cmake b/cmake/CourseOptions.cmake new file mode 100644 index 0000000..f7df5cc --- /dev/null +++ b/cmake/CourseOptions.cmake @@ -0,0 +1,25 @@ +function(course_apply_warnings target_name) + if(MSVC) + target_compile_options(${target_name} PRIVATE /W4 /permissive-) + if(COURSE_WARNINGS_AS_ERRORS) + target_compile_options(${target_name} PRIVATE /WX) + endif() + else() + target_compile_options( + ${target_name} + PRIVATE + -Wall + -Wextra + -Wpedantic + -Wconversion + -Wshadow + ) + if(COURSE_WARNINGS_AS_ERRORS) + target_compile_options(${target_name} PRIVATE -Werror) + endif() + endif() +endfunction() + +function(course_set_cxx_standard target_name standard) + target_compile_features(${target_name} PRIVATE cxx_std_${standard}) +endfunction() diff --git a/modules/01_fundamentals/CMakeLists.txt b/modules/01_fundamentals/CMakeLists.txt new file mode 100644 index 0000000..8969e1b --- /dev/null +++ b/modules/01_fundamentals/CMakeLists.txt @@ -0,0 +1,35 @@ +add_course_exercise( + NAME hello_world + MODULE 01_fundamentals + STANDARD 17 + SOURCES + src/hello_world.cpp + test/hello_world_test.cpp +) + +add_course_exercise( + NAME variables_and_types + MODULE 01_fundamentals + STANDARD 17 + SOURCES + src/variables_and_types.cpp + test/variables_and_types_test.cpp +) + +add_course_exercise( + NAME operators + MODULE 01_fundamentals + STANDARD 17 + SOURCES + src/operators.cpp + test/operators_test.cpp +) + +add_course_exercise( + NAME basic_io + MODULE 01_fundamentals + STANDARD 17 + SOURCES + src/basic_io.cpp + test/basic_io_test.cpp +) diff --git a/modules/01_fundamentals/README.md b/modules/01_fundamentals/README.md new file mode 100644 index 0000000..7bfacd1 --- /dev/null +++ b/modules/01_fundamentals/README.md @@ -0,0 +1,34 @@ +# Module 01: C++ Fundamentals + +## Learning Goals + +- Understand the structure of a C++ program (`main`, headers, translation units) +- Work with fundamental types (`int`, `double`, `bool`, `char`) +- Use literals, constants, and basic I/O +- Apply arithmetic, relational, and logical operators +- Read and write formatted output + +## Exercises + +| Exercise | Topic | Standard | +|----------|-------|----------| +| `hello_world` | First program, returning values from functions | C++17 | +| `variables_and_types` | Types, `auto`, type aliases | C++17 | +| `operators` | Arithmetic, comparisons, bitwise ops | C++17 | +| `basic_io` | Streams, string formatting | C++17 | + +## How to Work + +1. Open the exercise header in `include/` to see the required API. +2. Implement the functions in `src/`. +3. Run the corresponding test target in CLion or with CTest. +4. All tests must pass before moving on. + +## Run Tests (CLion) + +Select a target like `01_fundamentals_hello_world` and click Run, or use: + +```bash +cmake --build build --target 01_fundamentals_hello_world +ctest --test-dir build -R 01_fundamentals_hello_world +``` diff --git a/modules/01_fundamentals/include/basic_io.hpp b/modules/01_fundamentals/include/basic_io.hpp new file mode 100644 index 0000000..9fd7f66 --- /dev/null +++ b/modules/01_fundamentals/include/basic_io.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace fundamentals { + +[[nodiscard]] auto join_words(const std::vector& words, char separator) -> std::string; +[[nodiscard]] auto parse_integers(const std::string& csv) -> std::vector; +[[nodiscard]] auto format_score(const std::string& player, int score) -> std::string; + +} // namespace fundamentals diff --git a/modules/01_fundamentals/include/hello_world.hpp b/modules/01_fundamentals/include/hello_world.hpp new file mode 100644 index 0000000..3d68e9f --- /dev/null +++ b/modules/01_fundamentals/include/hello_world.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace fundamentals { + +// Return a greeting message for the given name. +// Example: greet("Ada") -> "Hello, Ada!" +[[nodiscard]] auto greet(const char* name) -> const char*; + +// Return the program entry convention value for success. +[[nodiscard]] constexpr auto program_exit_success() -> int { return 0; } + +} // namespace fundamentals diff --git a/modules/01_fundamentals/include/operators.hpp b/modules/01_fundamentals/include/operators.hpp new file mode 100644 index 0000000..7650f92 --- /dev/null +++ b/modules/01_fundamentals/include/operators.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace fundamentals { + +[[nodiscard]] auto absolute_difference(int lhs, int rhs) -> int; +[[nodiscard]] auto is_in_range(int value, int min, int max) -> bool; +[[nodiscard]] auto count_set_bits(std::uint8_t value) -> int; +[[nodiscard]] auto logical_and(bool lhs, bool rhs) -> bool; +[[nodiscard]] auto logical_or(bool lhs, bool rhs) -> bool; + +} // namespace fundamentals diff --git a/modules/01_fundamentals/include/variables_and_types.hpp b/modules/01_fundamentals/include/variables_and_types.hpp new file mode 100644 index 0000000..9c1b64a --- /dev/null +++ b/modules/01_fundamentals/include/variables_and_types.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace fundamentals { + +[[nodiscard]] auto add_integers(int lhs, int rhs) -> int; +[[nodiscard]] auto multiply_doubles(double lhs, double rhs) -> double; +[[nodiscard]] auto is_even(std::int64_t value) -> bool; +[[nodiscard]] auto describe_type(int value) -> std::string; +[[nodiscard]] auto describe_type(double value) -> std::string; +[[nodiscard]] auto describe_type(bool value) -> std::string; + +} // namespace fundamentals diff --git a/modules/01_fundamentals/src/basic_io.cpp b/modules/01_fundamentals/src/basic_io.cpp new file mode 100644 index 0000000..8086fce --- /dev/null +++ b/modules/01_fundamentals/src/basic_io.cpp @@ -0,0 +1,28 @@ +#include "basic_io.hpp" + +#include +#include + +namespace fundamentals { + +auto join_words(const std::vector& words, char separator) -> std::string { + // TODO: Join words with separator, no trailing separator. + (void)words; + (void)separator; + return {}; +} + +auto parse_integers(const std::string& csv) -> std::vector { + // TODO: Parse comma-separated integers (e.g. "1,2,3"). + (void)csv; + return {}; +} + +auto format_score(const std::string& player, int score) -> std::string { + // TODO: Return " scored points". + (void)player; + (void)score; + return {}; +} + +} // namespace fundamentals diff --git a/modules/01_fundamentals/src/hello_world.cpp b/modules/01_fundamentals/src/hello_world.cpp new file mode 100644 index 0000000..2a24a75 --- /dev/null +++ b/modules/01_fundamentals/src/hello_world.cpp @@ -0,0 +1,11 @@ +#include "hello_world.hpp" + +namespace fundamentals { + +auto greet(const char* name) -> const char* { + // TODO: Return a static string in the format "Hello, !" + (void)name; + return "Hello, World!"; +} + +} // namespace fundamentals diff --git a/modules/01_fundamentals/src/operators.cpp b/modules/01_fundamentals/src/operators.cpp new file mode 100644 index 0000000..7eb6855 --- /dev/null +++ b/modules/01_fundamentals/src/operators.cpp @@ -0,0 +1,40 @@ +#include "operators.hpp" + +namespace fundamentals { + +auto absolute_difference(int lhs, int rhs) -> int { + // TODO: Return |lhs - rhs| without using std::abs. + (void)lhs; + (void)rhs; + return 0; +} + +auto is_in_range(int value, int min, int max) -> bool { + // TODO: Return true when min <= value <= max (inclusive). + (void)value; + (void)min; + (void)max; + return false; +} + +auto count_set_bits(std::uint8_t value) -> int { + // TODO: Count how many bits are set to 1. + (void)value; + return 0; +} + +auto logical_and(bool lhs, bool rhs) -> bool { + // TODO: Implement logical AND without using &&. + (void)lhs; + (void)rhs; + return false; +} + +auto logical_or(bool lhs, bool rhs) -> bool { + // TODO: Implement logical OR without using ||. + (void)lhs; + (void)rhs; + return false; +} + +} // namespace fundamentals diff --git a/modules/01_fundamentals/src/variables_and_types.cpp b/modules/01_fundamentals/src/variables_and_types.cpp new file mode 100644 index 0000000..3da84fa --- /dev/null +++ b/modules/01_fundamentals/src/variables_and_types.cpp @@ -0,0 +1,43 @@ +#include "variables_and_types.hpp" + +namespace fundamentals { + +auto add_integers(int lhs, int rhs) -> int { + // TODO: Return the sum. + (void)lhs; + (void)rhs; + return 0; +} + +auto multiply_doubles(double lhs, double rhs) -> double { + // TODO: Return the product. + (void)lhs; + (void)rhs; + return 0.0; +} + +auto is_even(std::int64_t value) -> bool { + // TODO: Return true when value is divisible by 2. + (void)value; + return false; +} + +auto describe_type(int value) -> std::string { + // TODO: Return "int:" followed by the decimal representation. + (void)value; + return "int:0"; +} + +auto describe_type(double value) -> std::string { + // TODO: Return "double:" followed by the value with one decimal place. + (void)value; + return "double:0.0"; +} + +auto describe_type(bool value) -> std::string { + // TODO: Return "bool:true" or "bool:false". + (void)value; + return "bool:false"; +} + +} // namespace fundamentals diff --git a/modules/01_fundamentals/test/basic_io_test.cpp b/modules/01_fundamentals/test/basic_io_test.cpp new file mode 100644 index 0000000..c7ed611 --- /dev/null +++ b/modules/01_fundamentals/test/basic_io_test.cpp @@ -0,0 +1,18 @@ +#include "basic_io.hpp" + +#include + +TEST(BasicIo, JoinWords) { + EXPECT_EQ(fundamentals::join_words({"C", "Plus", "Plus"}, '-'), "C-Plus-Plus"); + EXPECT_EQ(fundamentals::join_words({"solo"}, ','), "solo"); + EXPECT_EQ(fundamentals::join_words({}, ','), ""); +} + +TEST(BasicIo, ParseIntegers) { + EXPECT_EQ(fundamentals::parse_integers("1,2,3"), (std::vector{1, 2, 3})); + EXPECT_EQ(fundamentals::parse_integers("42"), (std::vector{42})); +} + +TEST(BasicIo, FormatScore) { + EXPECT_EQ(fundamentals::format_score("Alice", 150), "Alice scored 150 points"); +} diff --git a/modules/01_fundamentals/test/hello_world_test.cpp b/modules/01_fundamentals/test/hello_world_test.cpp new file mode 100644 index 0000000..56dc49c --- /dev/null +++ b/modules/01_fundamentals/test/hello_world_test.cpp @@ -0,0 +1,12 @@ +#include "hello_world.hpp" + +#include + +TEST(HelloWorld, GreetsByName) { + EXPECT_STREQ(fundamentals::greet("Ada"), "Hello, Ada!"); + EXPECT_STREQ(fundamentals::greet("Bjarne"), "Hello, Bjarne!"); +} + +TEST(HelloWorld, ExitSuccessIsZero) { + EXPECT_EQ(fundamentals::program_exit_success(), 0); +} diff --git a/modules/01_fundamentals/test/operators_test.cpp b/modules/01_fundamentals/test/operators_test.cpp new file mode 100644 index 0000000..d75f789 --- /dev/null +++ b/modules/01_fundamentals/test/operators_test.cpp @@ -0,0 +1,25 @@ +#include "operators.hpp" + +#include + +TEST(Operators, AbsoluteDifference) { + EXPECT_EQ(fundamentals::absolute_difference(5, 9), 4); + EXPECT_EQ(fundamentals::absolute_difference(-3, 2), 5); +} + +TEST(Operators, RangeCheck) { + EXPECT_TRUE(fundamentals::is_in_range(5, 1, 10)); + EXPECT_FALSE(fundamentals::is_in_range(0, 1, 10)); +} + +TEST(Operators, CountSetBits) { + EXPECT_EQ(fundamentals::count_set_bits(0b00001111), 4); + EXPECT_EQ(fundamentals::count_set_bits(0b10101010), 4); +} + +TEST(Operators, LogicalOperations) { + EXPECT_TRUE(fundamentals::logical_and(true, true)); + EXPECT_FALSE(fundamentals::logical_and(true, false)); + EXPECT_TRUE(fundamentals::logical_or(false, true)); + EXPECT_FALSE(fundamentals::logical_or(false, false)); +} diff --git a/modules/01_fundamentals/test/variables_and_types_test.cpp b/modules/01_fundamentals/test/variables_and_types_test.cpp new file mode 100644 index 0000000..232f5df --- /dev/null +++ b/modules/01_fundamentals/test/variables_and_types_test.cpp @@ -0,0 +1,25 @@ +#include "variables_and_types.hpp" + +#include + +TEST(VariablesAndTypes, AddsIntegers) { + EXPECT_EQ(fundamentals::add_integers(2, 3), 5); + EXPECT_EQ(fundamentals::add_integers(-4, 10), 6); +} + +TEST(VariablesAndTypes, MultipliesDoubles) { + EXPECT_DOUBLE_EQ(fundamentals::multiply_doubles(2.5, 4.0), 10.0); +} + +TEST(VariablesAndTypes, DetectsEvenNumbers) { + EXPECT_TRUE(fundamentals::is_even(0)); + EXPECT_TRUE(fundamentals::is_even(42)); + EXPECT_FALSE(fundamentals::is_even(7)); +} + +TEST(VariablesAndTypes, DescribesTypes) { + EXPECT_EQ(fundamentals::describe_type(7), "int:7"); + EXPECT_EQ(fundamentals::describe_type(3.5), "double:3.5"); + EXPECT_EQ(fundamentals::describe_type(true), "bool:true"); + EXPECT_EQ(fundamentals::describe_type(false), "bool:false"); +} diff --git a/modules/02_control_flow/CMakeLists.txt b/modules/02_control_flow/CMakeLists.txt new file mode 100644 index 0000000..f049c1c --- /dev/null +++ b/modules/02_control_flow/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME conditionals MODULE 02_control_flow STANDARD 17 + SOURCES src/conditionals.cpp test/conditionals_test.cpp) +add_course_exercise(NAME switch_statements MODULE 02_control_flow STANDARD 17 + SOURCES src/switch_statements.cpp test/switch_statements_test.cpp) +add_course_exercise(NAME loops MODULE 02_control_flow STANDARD 17 + SOURCES src/loops.cpp test/loops_test.cpp) +add_course_exercise(NAME break_continue MODULE 02_control_flow STANDARD 17 + SOURCES src/break_continue.cpp test/break_continue_test.cpp) diff --git a/modules/02_control_flow/README.md b/modules/02_control_flow/README.md new file mode 100644 index 0000000..3bba50a --- /dev/null +++ b/modules/02_control_flow/README.md @@ -0,0 +1,17 @@ +# Module 02: Control Flow + +## Learning Goals + +- Write conditional logic with `if`, `else if`, and `else` +- Use `switch` statements and understand fall-through +- Implement loops: `for`, `while`, and range-based patterns +- Control loop execution with `break` and `continue` + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `conditionals` | Classification, min/max, sign | +| `switch_statements` | Menu dispatch, enum handling | +| `loops` | Summation, factorial, search | +| `break_continue` | Filtering, early exit | diff --git a/modules/02_control_flow/include/break_continue.hpp b/modules/02_control_flow/include/break_continue.hpp new file mode 100644 index 0000000..0994b9a --- /dev/null +++ b/modules/02_control_flow/include/break_continue.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace control_flow { + +[[nodiscard]] auto sum_positive(const std::vector& data) -> int; +[[nodiscard]] auto first_multiple_of(const std::vector& data, int divisor) -> int; +[[nodiscard]] auto collect_until_negative(const std::vector& data) -> std::vector; + +} // namespace control_flow diff --git a/modules/02_control_flow/include/conditionals.hpp b/modules/02_control_flow/include/conditionals.hpp new file mode 100644 index 0000000..e7c077b --- /dev/null +++ b/modules/02_control_flow/include/conditionals.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace control_flow { + +enum class Grade { A, B, C, D, F }; + +[[nodiscard]] auto classify_score(int score) -> Grade; +[[nodiscard]] auto max_of_three(int a, int b, int c) -> int; +[[nodiscard]] auto sign_of(int value) -> int; + +} // namespace control_flow diff --git a/modules/02_control_flow/include/loops.hpp b/modules/02_control_flow/include/loops.hpp new file mode 100644 index 0000000..640195f --- /dev/null +++ b/modules/02_control_flow/include/loops.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace control_flow { + +[[nodiscard]] auto sum_range(int from, int to) -> int; +[[nodiscard]] auto factorial(int n) -> long long; +[[nodiscard]] auto count_occurrences(const std::vector& data, int target) -> int; +[[nodiscard]] auto first_index_of(const std::vector& data, int target) -> int; + +} // namespace control_flow diff --git a/modules/02_control_flow/include/switch_statements.hpp b/modules/02_control_flow/include/switch_statements.hpp new file mode 100644 index 0000000..8a5de1c --- /dev/null +++ b/modules/02_control_flow/include/switch_statements.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace control_flow { + +enum class Operation { Add, Subtract, Multiply, Divide, Unknown }; + +[[nodiscard]] auto parse_operation(char op) -> Operation; +[[nodiscard]] auto apply_operation(Operation op, int lhs, int rhs) -> int; +[[nodiscard]] auto day_name(int day) -> std::string; + +} // namespace control_flow diff --git a/modules/02_control_flow/src/break_continue.cpp b/modules/02_control_flow/src/break_continue.cpp new file mode 100644 index 0000000..1ed870f --- /dev/null +++ b/modules/02_control_flow/src/break_continue.cpp @@ -0,0 +1,20 @@ +#include "break_continue.hpp" + +namespace control_flow { + +auto sum_positive(const std::vector& data) -> int { + (void)data; + return 0; +} + +auto first_multiple_of(const std::vector& data, int divisor) -> int { + (void)data; (void)divisor; + return -1; +} + +auto collect_until_negative(const std::vector& data) -> std::vector { + (void)data; + return {}; +} + +} // namespace control_flow diff --git a/modules/02_control_flow/src/conditionals.cpp b/modules/02_control_flow/src/conditionals.cpp new file mode 100644 index 0000000..b8931ae --- /dev/null +++ b/modules/02_control_flow/src/conditionals.cpp @@ -0,0 +1,22 @@ +#include "conditionals.hpp" + +namespace control_flow { + +auto classify_score(int score) -> Grade { + // TODO: A>=90, B>=80, C>=70, D>=60, else F + (void)score; + return Grade::F; +} + +auto max_of_three(int a, int b, int c) -> int { + (void)a; (void)b; (void)c; + return 0; +} + +auto sign_of(int value) -> int { + // TODO: Return -1, 0, or 1 + (void)value; + return 0; +} + +} // namespace control_flow diff --git a/modules/02_control_flow/src/loops.cpp b/modules/02_control_flow/src/loops.cpp new file mode 100644 index 0000000..c53cc09 --- /dev/null +++ b/modules/02_control_flow/src/loops.cpp @@ -0,0 +1,26 @@ +#include "loops.hpp" + +namespace control_flow { + +auto sum_range(int from, int to) -> int { + (void)from; (void)to; + return 0; +} + +auto factorial(int n) -> long long { + (void)n; + return 1; +} + +auto count_occurrences(const std::vector& data, int target) -> int { + (void)data; (void)target; + return 0; +} + +auto first_index_of(const std::vector& data, int target) -> int { + // TODO: Return index or -1 if not found + (void)data; (void)target; + return -1; +} + +} // namespace control_flow diff --git a/modules/02_control_flow/src/switch_statements.cpp b/modules/02_control_flow/src/switch_statements.cpp new file mode 100644 index 0000000..0f72466 --- /dev/null +++ b/modules/02_control_flow/src/switch_statements.cpp @@ -0,0 +1,21 @@ +#include "switch_statements.hpp" + +namespace control_flow { + +auto parse_operation(char op) -> Operation { + (void)op; + return Operation::Unknown; +} + +auto apply_operation(Operation op, int lhs, int rhs) -> int { + (void)op; (void)lhs; (void)rhs; + return 0; +} + +auto day_name(int day) -> std::string { + // TODO: 1=Monday ... 7=Sunday, else "Invalid" + (void)day; + return "Invalid"; +} + +} // namespace control_flow diff --git a/modules/02_control_flow/test/break_continue_test.cpp b/modules/02_control_flow/test/break_continue_test.cpp new file mode 100644 index 0000000..0aa2d29 --- /dev/null +++ b/modules/02_control_flow/test/break_continue_test.cpp @@ -0,0 +1,15 @@ +#include "break_continue.hpp" +#include + +TEST(BreakContinue, SumPositive) { + EXPECT_EQ(control_flow::sum_positive({1, -2, 3, -4, 5}), 9); +} + +TEST(BreakContinue, FirstMultipleOf) { + EXPECT_EQ(control_flow::first_multiple_of({1, 3, 4, 8}, 4), 8); + EXPECT_EQ(control_flow::first_multiple_of({1, 3, 5}, 4), -1); +} + +TEST(BreakContinue, CollectUntilNegative) { + EXPECT_EQ(control_flow::collect_until_negative({1, 2, 3, -1, 4}), (std::vector{1, 2, 3})); +} diff --git a/modules/02_control_flow/test/conditionals_test.cpp b/modules/02_control_flow/test/conditionals_test.cpp new file mode 100644 index 0000000..f01dc04 --- /dev/null +++ b/modules/02_control_flow/test/conditionals_test.cpp @@ -0,0 +1,19 @@ +#include "conditionals.hpp" +#include + +TEST(Conditionals, ClassifyScore) { + EXPECT_EQ(control_flow::classify_score(95), control_flow::Grade::A); + EXPECT_EQ(control_flow::classify_score(82), control_flow::Grade::B); + EXPECT_EQ(control_flow::classify_score(55), control_flow::Grade::F); +} + +TEST(Conditionals, MaxOfThree) { + EXPECT_EQ(control_flow::max_of_three(1, 9, 3), 9); + EXPECT_EQ(control_flow::max_of_three(-1, -5, -2), -1); +} + +TEST(Conditionals, SignOf) { + EXPECT_EQ(control_flow::sign_of(10), 1); + EXPECT_EQ(control_flow::sign_of(0), 0); + EXPECT_EQ(control_flow::sign_of(-3), -1); +} diff --git a/modules/02_control_flow/test/loops_test.cpp b/modules/02_control_flow/test/loops_test.cpp new file mode 100644 index 0000000..b2b706c --- /dev/null +++ b/modules/02_control_flow/test/loops_test.cpp @@ -0,0 +1,21 @@ +#include "loops.hpp" +#include + +TEST(Loops, SumRange) { + EXPECT_EQ(control_flow::sum_range(1, 5), 15); + EXPECT_EQ(control_flow::sum_range(5, 5), 5); +} + +TEST(Loops, Factorial) { + EXPECT_EQ(control_flow::factorial(0), 1); + EXPECT_EQ(control_flow::factorial(5), 120); +} + +TEST(Loops, CountOccurrences) { + EXPECT_EQ(control_flow::count_occurrences({1, 2, 2, 3}, 2), 2); +} + +TEST(Loops, FirstIndexOf) { + EXPECT_EQ(control_flow::first_index_of({4, 5, 6}, 5), 1); + EXPECT_EQ(control_flow::first_index_of({4, 5, 6}, 9), -1); +} diff --git a/modules/02_control_flow/test/switch_statements_test.cpp b/modules/02_control_flow/test/switch_statements_test.cpp new file mode 100644 index 0000000..f36ef32 --- /dev/null +++ b/modules/02_control_flow/test/switch_statements_test.cpp @@ -0,0 +1,18 @@ +#include "switch_statements.hpp" +#include + +TEST(SwitchStatements, ParseOperation) { + EXPECT_EQ(control_flow::parse_operation('+'), control_flow::Operation::Add); + EXPECT_EQ(control_flow::parse_operation('?'), control_flow::Operation::Unknown); +} + +TEST(SwitchStatements, ApplyOperation) { + EXPECT_EQ(control_flow::apply_operation(control_flow::Operation::Add, 2, 3), 5); + EXPECT_EQ(control_flow::apply_operation(control_flow::Operation::Multiply, 4, 5), 20); +} + +TEST(SwitchStatements, DayName) { + EXPECT_EQ(control_flow::day_name(1), "Monday"); + EXPECT_EQ(control_flow::day_name(7), "Sunday"); + EXPECT_EQ(control_flow::day_name(0), "Invalid"); +} diff --git a/modules/03_functions/CMakeLists.txt b/modules/03_functions/CMakeLists.txt new file mode 100644 index 0000000..52df6ec --- /dev/null +++ b/modules/03_functions/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME function_basics MODULE 03_functions STANDARD 17 + SOURCES src/function_basics.cpp test/function_basics_test.cpp) +add_course_exercise(NAME overloading MODULE 03_functions STANDARD 17 + SOURCES src/overloading.cpp test/overloading_test.cpp) +add_course_exercise(NAME default_parameters MODULE 03_functions STANDARD 17 + SOURCES src/default_parameters.cpp test/default_parameters_test.cpp) +add_course_exercise(NAME recursion MODULE 03_functions STANDARD 17 + SOURCES src/recursion.cpp test/recursion_test.cpp) diff --git a/modules/03_functions/README.md b/modules/03_functions/README.md new file mode 100644 index 0000000..13ac57d --- /dev/null +++ b/modules/03_functions/README.md @@ -0,0 +1,17 @@ +# Module 03: Functions + +## Learning Goals + +- Declare and define functions with clear interfaces +- Pass arguments by value, reference, and `const` reference +- Overload functions and use default parameters +- Implement recursive algorithms safely + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `function_basics` | Value/reference parameters, return values | +| `overloading` | Function overloading | +| `default_parameters` | Default arguments | +| `recursion` | Recursive algorithms | diff --git a/modules/03_functions/include/default_parameters.hpp b/modules/03_functions/include/default_parameters.hpp new file mode 100644 index 0000000..7213e06 --- /dev/null +++ b/modules/03_functions/include/default_parameters.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace functions { + +[[nodiscard]] auto repeat(char ch, int count = 1) -> std::string; +[[nodiscard]] auto power(int base, int exponent = 2) -> long long; +[[nodiscard]] auto clamp(int value, int min = 0, int max = 100) -> int; + +} // namespace functions diff --git a/modules/03_functions/include/function_basics.hpp b/modules/03_functions/include/function_basics.hpp new file mode 100644 index 0000000..39dd63f --- /dev/null +++ b/modules/03_functions/include/function_basics.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace functions { + +[[nodiscard]] auto square(int value) -> int; +void swap_integers(int& lhs, int& rhs); +[[nodiscard]] auto concatenate(const std::vector& parts) -> std::string; +void append_suffix(std::string& text, const std::string& suffix); + +} // namespace functions diff --git a/modules/03_functions/include/overloading.hpp b/modules/03_functions/include/overloading.hpp new file mode 100644 index 0000000..2c5bf1c --- /dev/null +++ b/modules/03_functions/include/overloading.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace functions { + +[[nodiscard]] auto max_value(int a, int b) -> int; +[[nodiscard]] auto max_value(double a, double b) -> double; +[[nodiscard]] auto max_value(const std::string& a, const std::string& b) -> std::string; +[[nodiscard]] auto area(int side) -> int; +[[nodiscard]] auto area(int width, int height) -> int; + +} // namespace functions diff --git a/modules/03_functions/include/recursion.hpp b/modules/03_functions/include/recursion.hpp new file mode 100644 index 0000000..93858da --- /dev/null +++ b/modules/03_functions/include/recursion.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace functions { + +[[nodiscard]] auto fibonacci(int n) -> long long; +[[nodiscard]] auto sum_digits(int n) -> int; +[[nodiscard]] auto is_palindrome(int n) -> bool; + +} // namespace functions diff --git a/modules/03_functions/src/default_parameters.cpp b/modules/03_functions/src/default_parameters.cpp new file mode 100644 index 0000000..2c547d3 --- /dev/null +++ b/modules/03_functions/src/default_parameters.cpp @@ -0,0 +1,9 @@ +#include "default_parameters.hpp" + +namespace functions { + +auto repeat(char ch, int count) -> std::string { (void)ch; (void)count; return {}; } +auto power(int base, int exponent) -> long long { (void)base; (void)exponent; return 0; } +auto clamp(int value, int min, int max) -> int { (void)value; (void)min; (void)max; return 0; } + +} // namespace functions diff --git a/modules/03_functions/src/function_basics.cpp b/modules/03_functions/src/function_basics.cpp new file mode 100644 index 0000000..e6b2246 --- /dev/null +++ b/modules/03_functions/src/function_basics.cpp @@ -0,0 +1,10 @@ +#include "function_basics.hpp" + +namespace functions { + +auto square(int value) -> int { (void)value; return 0; } +void swap_integers(int& lhs, int& rhs) { (void)lhs; (void)rhs; } +auto concatenate(const std::vector& parts) -> std::string { (void)parts; return {}; } +void append_suffix(std::string& text, const std::string& suffix) { (void)text; (void)suffix; } + +} // namespace functions diff --git a/modules/03_functions/src/overloading.cpp b/modules/03_functions/src/overloading.cpp new file mode 100644 index 0000000..5f4a7b4 --- /dev/null +++ b/modules/03_functions/src/overloading.cpp @@ -0,0 +1,11 @@ +#include "overloading.hpp" + +namespace functions { + +auto max_value(int a, int b) -> int { (void)a; (void)b; return 0; } +auto max_value(double a, double b) -> double { (void)a; (void)b; return 0.0; } +auto max_value(const std::string& a, const std::string& b) -> std::string { (void)a; (void)b; return {}; } +auto area(int side) -> int { (void)side; return 0; } +auto area(int width, int height) -> int { (void)width; (void)height; return 0; } + +} // namespace functions diff --git a/modules/03_functions/src/recursion.cpp b/modules/03_functions/src/recursion.cpp new file mode 100644 index 0000000..8e3bee4 --- /dev/null +++ b/modules/03_functions/src/recursion.cpp @@ -0,0 +1,9 @@ +#include "recursion.hpp" + +namespace functions { + +auto fibonacci(int n) -> long long { (void)n; return 0; } +auto sum_digits(int n) -> int { (void)n; return 0; } +auto is_palindrome(int n) -> bool { (void)n; return false; } + +} // namespace functions diff --git a/modules/03_functions/test/default_parameters_test.cpp b/modules/03_functions/test/default_parameters_test.cpp new file mode 100644 index 0000000..3035ba6 --- /dev/null +++ b/modules/03_functions/test/default_parameters_test.cpp @@ -0,0 +1,18 @@ +#include "default_parameters.hpp" +#include + +TEST(DefaultParameters, Repeat) { + EXPECT_EQ(functions::repeat('*'), "*"); + EXPECT_EQ(functions::repeat('-', 3), "---"); +} + +TEST(DefaultParameters, Power) { + EXPECT_EQ(functions::power(5), 25); + EXPECT_EQ(functions::power(2, 10), 1024); +} + +TEST(DefaultParameters, Clamp) { + EXPECT_EQ(functions::clamp(150), 100); + EXPECT_EQ(functions::clamp(-5), 0); + EXPECT_EQ(functions::clamp(42, 10, 50), 42); +} diff --git a/modules/03_functions/test/function_basics_test.cpp b/modules/03_functions/test/function_basics_test.cpp new file mode 100644 index 0000000..17828e6 --- /dev/null +++ b/modules/03_functions/test/function_basics_test.cpp @@ -0,0 +1,21 @@ +#include "function_basics.hpp" +#include + +TEST(FunctionBasics, Square) { EXPECT_EQ(functions::square(7), 49); } + +TEST(FunctionBasics, SwapIntegers) { + int a = 1, b = 2; + functions::swap_integers(a, b); + EXPECT_EQ(a, 2); + EXPECT_EQ(b, 1); +} + +TEST(FunctionBasics, Concatenate) { + EXPECT_EQ(functions::concatenate({"C", "++"}), "C++"); +} + +TEST(FunctionBasics, AppendSuffix) { + std::string text = "modern"; + functions::append_suffix(text, "_cpp"); + EXPECT_EQ(text, "modern_cpp"); +} diff --git a/modules/03_functions/test/overloading_test.cpp b/modules/03_functions/test/overloading_test.cpp new file mode 100644 index 0000000..fb7f601 --- /dev/null +++ b/modules/03_functions/test/overloading_test.cpp @@ -0,0 +1,10 @@ +#include "overloading.hpp" +#include + +TEST(Overloading, MaxInt) { EXPECT_EQ(functions::max_value(3, 9), 9); } +TEST(Overloading, MaxDouble) { EXPECT_DOUBLE_EQ(functions::max_value(3.1, 2.9), 3.1); } +TEST(Overloading, MaxString) { EXPECT_EQ(functions::max_value("abc", "abd"), "abd"); } +TEST(Overloading, Area) { + EXPECT_EQ(functions::area(4), 16); + EXPECT_EQ(functions::area(3, 5), 15); +} diff --git a/modules/03_functions/test/recursion_test.cpp b/modules/03_functions/test/recursion_test.cpp new file mode 100644 index 0000000..5842d40 --- /dev/null +++ b/modules/03_functions/test/recursion_test.cpp @@ -0,0 +1,17 @@ +#include "recursion.hpp" +#include + +TEST(Recursion, Fibonacci) { + EXPECT_EQ(functions::fibonacci(0), 0); + EXPECT_EQ(functions::fibonacci(1), 1); + EXPECT_EQ(functions::fibonacci(10), 55); +} + +TEST(Recursion, SumDigits) { + EXPECT_EQ(functions::sum_digits(12345), 15); +} + +TEST(Recursion, IsPalindrome) { + EXPECT_TRUE(functions::is_palindrome(121)); + EXPECT_FALSE(functions::is_palindrome(123)); +} diff --git a/modules/04_arrays_and_strings/CMakeLists.txt b/modules/04_arrays_and_strings/CMakeLists.txt new file mode 100644 index 0000000..606cf50 --- /dev/null +++ b/modules/04_arrays_and_strings/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME c_arrays MODULE 04_arrays_and_strings STANDARD 17 + SOURCES src/c_arrays.cpp test/c_arrays_test.cpp) +add_course_exercise(NAME std_string MODULE 04_arrays_and_strings STANDARD 17 + SOURCES src/std_string.cpp test/std_string_test.cpp) +add_course_exercise(NAME std_vector MODULE 04_arrays_and_strings STANDARD 17 + SOURCES src/std_vector.cpp test/std_vector_test.cpp) +add_course_exercise(NAME multidimensional MODULE 04_arrays_and_strings STANDARD 17 + SOURCES src/multidimensional.cpp test/multidimensional_test.cpp) diff --git a/modules/04_arrays_and_strings/README.md b/modules/04_arrays_and_strings/README.md new file mode 100644 index 0000000..bf60fab --- /dev/null +++ b/modules/04_arrays_and_strings/README.md @@ -0,0 +1,17 @@ +# Module 04: Arrays and Strings + +## Learning Goals + +- Use C-style arrays and understand their limitations +- Work with `std::string` manipulation +- Master `std::vector` as a dynamic array +- Handle multi-dimensional data structures + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `c_arrays` | Fixed-size arrays, bounds | +| `std_string` | String operations | +| `std_vector` | Dynamic arrays | +| `multidimensional` | 2D vectors and matrices | diff --git a/modules/04_arrays_and_strings/include/c_arrays.hpp b/modules/04_arrays_and_strings/include/c_arrays.hpp new file mode 100644 index 0000000..cb20468 --- /dev/null +++ b/modules/04_arrays_and_strings/include/c_arrays.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace arrays { + +[[nodiscard]] auto array_sum(const int* data, std::size_t size) -> int; +[[nodiscard]] auto array_max(const int* data, std::size_t size) -> int; +void reverse_array(int* data, std::size_t size); + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/include/multidimensional.hpp b/modules/04_arrays_and_strings/include/multidimensional.hpp new file mode 100644 index 0000000..0252ab6 --- /dev/null +++ b/modules/04_arrays_and_strings/include/multidimensional.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace arrays { + +using Matrix = std::vector>; + +[[nodiscard]] auto create_matrix(int rows, int cols, int fill) -> Matrix; +[[nodiscard]] auto transpose(const Matrix& matrix) -> Matrix; +[[nodiscard]] auto row_sums(const Matrix& matrix) -> std::vector; + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/include/std_string.hpp b/modules/04_arrays_and_strings/include/std_string.hpp new file mode 100644 index 0000000..1b0fa9a --- /dev/null +++ b/modules/04_arrays_and_strings/include/std_string.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace arrays { + +[[nodiscard]] auto to_uppercase(std::string text) -> std::string; +[[nodiscard]] auto trim(const std::string& text) -> std::string; +[[nodiscard]] auto replace_all(std::string text, char from, char to) -> std::string; +[[nodiscard]] auto starts_with(const std::string& text, const std::string& prefix) -> bool; + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/include/std_vector.hpp b/modules/04_arrays_and_strings/include/std_vector.hpp new file mode 100644 index 0000000..a5fb442 --- /dev/null +++ b/modules/04_arrays_and_strings/include/std_vector.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace arrays { + +[[nodiscard]] auto vector_sum(const std::vector& data) -> int; +void remove_value(std::vector& data, int value); +[[nodiscard]] auto unique_sorted(std::vector data) -> std::vector; +[[nodiscard]] auto chunk(const std::vector& data, std::size_t size) -> std::vector>; + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/src/c_arrays.cpp b/modules/04_arrays_and_strings/src/c_arrays.cpp new file mode 100644 index 0000000..9290afb --- /dev/null +++ b/modules/04_arrays_and_strings/src/c_arrays.cpp @@ -0,0 +1,9 @@ +#include "c_arrays.hpp" + +namespace arrays { + +auto array_sum(const int* data, std::size_t size) -> int { (void)data; (void)size; return 0; } +auto array_max(const int* data, std::size_t size) -> int { (void)data; (void)size; return 0; } +void reverse_array(int* data, std::size_t size) { (void)data; (void)size; } + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/src/multidimensional.cpp b/modules/04_arrays_and_strings/src/multidimensional.cpp new file mode 100644 index 0000000..83a6c79 --- /dev/null +++ b/modules/04_arrays_and_strings/src/multidimensional.cpp @@ -0,0 +1,9 @@ +#include "multidimensional.hpp" + +namespace arrays { + +auto create_matrix(int rows, int cols, int fill) -> Matrix { (void)rows; (void)cols; (void)fill; return {}; } +auto transpose(const Matrix& matrix) -> Matrix { (void)matrix; return {}; } +auto row_sums(const Matrix& matrix) -> std::vector { (void)matrix; return {}; } + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/src/std_string.cpp b/modules/04_arrays_and_strings/src/std_string.cpp new file mode 100644 index 0000000..9f1f729 --- /dev/null +++ b/modules/04_arrays_and_strings/src/std_string.cpp @@ -0,0 +1,10 @@ +#include "std_string.hpp" + +namespace arrays { + +auto to_uppercase(std::string text) -> std::string { (void)text; return {}; } +auto trim(const std::string& text) -> std::string { (void)text; return {}; } +auto replace_all(std::string text, char from, char to) -> std::string { (void)text; (void)from; (void)to; return {}; } +auto starts_with(const std::string& text, const std::string& prefix) -> bool { (void)text; (void)prefix; return false; } + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/src/std_vector.cpp b/modules/04_arrays_and_strings/src/std_vector.cpp new file mode 100644 index 0000000..070362e --- /dev/null +++ b/modules/04_arrays_and_strings/src/std_vector.cpp @@ -0,0 +1,10 @@ +#include "std_vector.hpp" + +namespace arrays { + +auto vector_sum(const std::vector& data) -> int { (void)data; return 0; } +void remove_value(std::vector& data, int value) { (void)data; (void)value; } +auto unique_sorted(std::vector data) -> std::vector { (void)data; return {}; } +auto chunk(const std::vector& data, std::size_t size) -> std::vector> { (void)data; (void)size; return {}; } + +} // namespace arrays diff --git a/modules/04_arrays_and_strings/test/c_arrays_test.cpp b/modules/04_arrays_and_strings/test/c_arrays_test.cpp new file mode 100644 index 0000000..505d935 --- /dev/null +++ b/modules/04_arrays_and_strings/test/c_arrays_test.cpp @@ -0,0 +1,19 @@ +#include "c_arrays.hpp" +#include + +TEST(CArrays, Sum) { + const int data[] = {1, 2, 3, 4}; + EXPECT_EQ(arrays::array_sum(data, 4), 10); +} + +TEST(CArrays, Max) { + const int data[] = {1, 9, 3}; + EXPECT_EQ(arrays::array_max(data, 3), 9); +} + +TEST(CArrays, Reverse) { + int data[] = {1, 2, 3, 4}; + arrays::reverse_array(data, 4); + EXPECT_EQ(data[0], 4); + EXPECT_EQ(data[3], 1); +} diff --git a/modules/04_arrays_and_strings/test/multidimensional_test.cpp b/modules/04_arrays_and_strings/test/multidimensional_test.cpp new file mode 100644 index 0000000..03d5128 --- /dev/null +++ b/modules/04_arrays_and_strings/test/multidimensional_test.cpp @@ -0,0 +1,20 @@ +#include "multidimensional.hpp" +#include + +TEST(Multidimensional, CreateMatrix) { + const auto m = arrays::create_matrix(2, 3, 7); + ASSERT_EQ(m.size(), 2U); + ASSERT_EQ(m[0].size(), 3U); + EXPECT_EQ(m[1][2], 7); +} + +TEST(Multidimensional, Transpose) { + const arrays::Matrix m{{1, 2}, {3, 4}}; + const auto t = arrays::transpose(m); + EXPECT_EQ(t, (arrays::Matrix{{1, 3}, {2, 4}})); +} + +TEST(Multidimensional, RowSums) { + const arrays::Matrix m{{1, 2, 3}, {4, 5, 6}}; + EXPECT_EQ(arrays::row_sums(m), (std::vector{6, 15})); +} diff --git a/modules/04_arrays_and_strings/test/std_string_test.cpp b/modules/04_arrays_and_strings/test/std_string_test.cpp new file mode 100644 index 0000000..4cd21d1 --- /dev/null +++ b/modules/04_arrays_and_strings/test/std_string_test.cpp @@ -0,0 +1,19 @@ +#include "std_string.hpp" +#include + +TEST(StdString, ToUppercase) { + EXPECT_EQ(arrays::to_uppercase("Hello"), "HELLO"); +} + +TEST(StdString, Trim) { + EXPECT_EQ(arrays::trim(" hi "), "hi"); +} + +TEST(StdString, ReplaceAll) { + EXPECT_EQ(arrays::replace_all("a-b-c", '-', '_'), "a_b_c"); +} + +TEST(StdString, StartsWith) { + EXPECT_TRUE(arrays::starts_with("C++26", "C++")); + EXPECT_FALSE(arrays::starts_with("C++26", "Java")); +} diff --git a/modules/04_arrays_and_strings/test/std_vector_test.cpp b/modules/04_arrays_and_strings/test/std_vector_test.cpp new file mode 100644 index 0000000..31a58d8 --- /dev/null +++ b/modules/04_arrays_and_strings/test/std_vector_test.cpp @@ -0,0 +1,21 @@ +#include "std_vector.hpp" +#include + +TEST(StdVector, Sum) { + EXPECT_EQ(arrays::vector_sum({1, 2, 3}), 6); +} + +TEST(StdVector, RemoveValue) { + std::vector data = {1, 2, 2, 3}; + arrays::remove_value(data, 2); + EXPECT_EQ(data, (std::vector{1, 3})); +} + +TEST(StdVector, UniqueSorted) { + EXPECT_EQ(arrays::unique_sorted({3, 1, 2, 2, 3}), (std::vector{1, 2, 3})); +} + +TEST(StdVector, Chunk) { + EXPECT_EQ(arrays::chunk({1, 2, 3, 4, 5}, 2), + (std::vector>{{1, 2}, {3, 4}, {5}})); +} diff --git a/modules/05_pointers_and_references/CMakeLists.txt b/modules/05_pointers_and_references/CMakeLists.txt new file mode 100644 index 0000000..8acef1d --- /dev/null +++ b/modules/05_pointers_and_references/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME pointer_basics MODULE 05_pointers_and_references STANDARD 17 + SOURCES src/pointer_basics.cpp test/pointer_basics_test.cpp) +add_course_exercise(NAME references MODULE 05_pointers_and_references STANDARD 17 + SOURCES src/references.cpp test/references_test.cpp) +add_course_exercise(NAME pointer_arithmetic MODULE 05_pointers_and_references STANDARD 17 + SOURCES src/pointer_arithmetic.cpp test/pointer_arithmetic_test.cpp) +add_course_exercise(NAME dynamic_memory MODULE 05_pointers_and_references STANDARD 17 + SOURCES src/dynamic_memory.cpp test/dynamic_memory_test.cpp) diff --git a/modules/05_pointers_and_references/README.md b/modules/05_pointers_and_references/README.md new file mode 100644 index 0000000..ad4fd39 --- /dev/null +++ b/modules/05_pointers_and_references/README.md @@ -0,0 +1,17 @@ +# Module 05: Pointers and References + +## Learning Goals + +- Understand pointers, addresses, and dereferencing +- Use references as aliases and for function parameters +- Apply pointer arithmetic carefully +- Manage dynamic memory with `new`/`delete` and prefer smart pointers later + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `pointer_basics` | Address-of, dereference, null | +| `references` | Reference parameters and return | +| `pointer_arithmetic` | Iterating with pointers | +| `dynamic_memory` | Manual allocation patterns | diff --git a/modules/05_pointers_and_references/include/dynamic_memory.hpp b/modules/05_pointers_and_references/include/dynamic_memory.hpp new file mode 100644 index 0000000..6e7fced --- /dev/null +++ b/modules/05_pointers_and_references/include/dynamic_memory.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace pointers { + +[[nodiscard]] auto allocate_and_fill(std::size_t count, int value) -> int*; +void deallocate(int* ptr); +[[nodiscard]] auto clone_array(const int* source, std::size_t count) -> int*; + +} // namespace pointers diff --git a/modules/05_pointers_and_references/include/pointer_arithmetic.hpp b/modules/05_pointers_and_references/include/pointer_arithmetic.hpp new file mode 100644 index 0000000..be2ece5 --- /dev/null +++ b/modules/05_pointers_and_references/include/pointer_arithmetic.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace pointers { + +[[nodiscard]] auto pointer_distance(const int* begin, const int* end) -> std::size_t; +[[nodiscard]] auto find_pointer(const int* begin, const int* end, int target) -> const int*; +auto reverse_in_place(int* begin, int* end) -> void; + +} // namespace pointers diff --git a/modules/05_pointers_and_references/include/pointer_basics.hpp b/modules/05_pointers_and_references/include/pointer_basics.hpp new file mode 100644 index 0000000..2b739fe --- /dev/null +++ b/modules/05_pointers_and_references/include/pointer_basics.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace pointers { + +[[nodiscard]] auto get_value(int* ptr) -> int; +void set_value(int* ptr, int value); +[[nodiscard]] auto is_null(const int* ptr) -> bool; +void swap_via_pointers(int* a, int* b); + +} // namespace pointers diff --git a/modules/05_pointers_and_references/include/references.hpp b/modules/05_pointers_and_references/include/references.hpp new file mode 100644 index 0000000..6e36e9e --- /dev/null +++ b/modules/05_pointers_and_references/include/references.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace pointers { + +[[nodiscard]] auto double_value(const int& value) -> int; +void increment(int& value); +[[nodiscard]] auto max_ref(const int& a, const int& b) -> const int&; +[[nodiscard]] auto sum_three(const int& a, const int& b, const int& c) -> int; + +} // namespace pointers diff --git a/modules/05_pointers_and_references/src/dynamic_memory.cpp b/modules/05_pointers_and_references/src/dynamic_memory.cpp new file mode 100644 index 0000000..ed1e3bb --- /dev/null +++ b/modules/05_pointers_and_references/src/dynamic_memory.cpp @@ -0,0 +1,9 @@ +#include "dynamic_memory.hpp" + +namespace pointers { + +auto allocate_and_fill(std::size_t count, int value) -> int* { (void)count; (void)value; return nullptr; } +void deallocate(int* ptr) { delete[] ptr; } +auto clone_array(const int* source, std::size_t count) -> int* { (void)source; (void)count; return nullptr; } + +} // namespace pointers diff --git a/modules/05_pointers_and_references/src/pointer_arithmetic.cpp b/modules/05_pointers_and_references/src/pointer_arithmetic.cpp new file mode 100644 index 0000000..054e91e --- /dev/null +++ b/modules/05_pointers_and_references/src/pointer_arithmetic.cpp @@ -0,0 +1,9 @@ +#include "pointer_arithmetic.hpp" + +namespace pointers { + +auto pointer_distance(const int* begin, const int* end) -> std::size_t { (void)begin; (void)end; return 0; } +auto find_pointer(const int* begin, const int* end, int target) -> const int* { (void)begin; (void)end; (void)target; return end; } +void reverse_in_place(int* begin, int* end) { (void)begin; (void)end; } + +} // namespace pointers diff --git a/modules/05_pointers_and_references/src/pointer_basics.cpp b/modules/05_pointers_and_references/src/pointer_basics.cpp new file mode 100644 index 0000000..9c8d103 --- /dev/null +++ b/modules/05_pointers_and_references/src/pointer_basics.cpp @@ -0,0 +1,10 @@ +#include "pointer_basics.hpp" + +namespace pointers { + +auto get_value(int* ptr) -> int { (void)ptr; return 0; } +void set_value(int* ptr, int value) { (void)ptr; (void)value; } +auto is_null(const int* ptr) -> bool { (void)ptr; return true; } +void swap_via_pointers(int* a, int* b) { (void)a; (void)b; } + +} // namespace pointers diff --git a/modules/05_pointers_and_references/src/references.cpp b/modules/05_pointers_and_references/src/references.cpp new file mode 100644 index 0000000..10e369c --- /dev/null +++ b/modules/05_pointers_and_references/src/references.cpp @@ -0,0 +1,10 @@ +#include "references.hpp" + +namespace pointers { + +auto double_value(const int& value) -> int { (void)value; return 0; } +void increment(int& value) { (void)value; } +auto max_ref(const int& a, const int& b) -> const int& { (void)a; (void)b; return a; } +auto sum_three(const int& a, const int& b, const int& c) -> int { (void)a; (void)b; (void)c; return 0; } + +} // namespace pointers diff --git a/modules/05_pointers_and_references/test/dynamic_memory_test.cpp b/modules/05_pointers_and_references/test/dynamic_memory_test.cpp new file mode 100644 index 0000000..241fca4 --- /dev/null +++ b/modules/05_pointers_and_references/test/dynamic_memory_test.cpp @@ -0,0 +1,18 @@ +#include "dynamic_memory.hpp" +#include + +TEST(DynamicMemory, AllocateAndFill) { + int* data = pointers::allocate_and_fill(3, 7); + ASSERT_NE(data, nullptr); + EXPECT_EQ(data[0], 7); + EXPECT_EQ(data[2], 7); + pointers::deallocate(data); +} + +TEST(DynamicMemory, CloneArray) { + int source[] = {1, 2, 3}; + int* copy = pointers::clone_array(source, 3); + ASSERT_NE(copy, nullptr); + EXPECT_EQ(copy[1], 2); + pointers::deallocate(copy); +} diff --git a/modules/05_pointers_and_references/test/pointer_arithmetic_test.cpp b/modules/05_pointers_and_references/test/pointer_arithmetic_test.cpp new file mode 100644 index 0000000..91a1e6e --- /dev/null +++ b/modules/05_pointers_and_references/test/pointer_arithmetic_test.cpp @@ -0,0 +1,19 @@ +#include "pointer_arithmetic.hpp" +#include + +TEST(PointerArithmetic, Distance) { + int data[] = {1, 2, 3, 4}; + EXPECT_EQ(pointers::pointer_distance(data, data + 4), 4U); +} + +TEST(PointerArithmetic, FindPointer) { + int data[] = {1, 2, 3}; + EXPECT_EQ(pointers::find_pointer(data, data + 3, 2), data + 1); +} + +TEST(PointerArithmetic, ReverseInPlace) { + int data[] = {1, 2, 3, 4}; + pointers::reverse_in_place(data, data + 4); + EXPECT_EQ(data[0], 4); + EXPECT_EQ(data[3], 1); +} diff --git a/modules/05_pointers_and_references/test/pointer_basics_test.cpp b/modules/05_pointers_and_references/test/pointer_basics_test.cpp new file mode 100644 index 0000000..6ffc1ad --- /dev/null +++ b/modules/05_pointers_and_references/test/pointer_basics_test.cpp @@ -0,0 +1,21 @@ +#include "pointer_basics.hpp" +#include + +TEST(PointerBasics, GetSetValue) { + int x = 42; + pointers::set_value(&x, 100); + EXPECT_EQ(pointers::get_value(&x), 100); +} + +TEST(PointerBasics, IsNull) { + EXPECT_TRUE(pointers::is_null(nullptr)); + int x = 1; + EXPECT_FALSE(pointers::is_null(&x)); +} + +TEST(PointerBasics, SwapViaPointers) { + int a = 1, b = 2; + pointers::swap_via_pointers(&a, &b); + EXPECT_EQ(a, 2); + EXPECT_EQ(b, 1); +} diff --git a/modules/05_pointers_and_references/test/references_test.cpp b/modules/05_pointers_and_references/test/references_test.cpp new file mode 100644 index 0000000..66832ec --- /dev/null +++ b/modules/05_pointers_and_references/test/references_test.cpp @@ -0,0 +1,19 @@ +#include "references.hpp" +#include + +TEST(References, DoubleValue) { EXPECT_EQ(pointers::double_value(21), 42); } + +TEST(References, Increment) { + int x = 5; + pointers::increment(x); + EXPECT_EQ(x, 6); +} + +TEST(References, MaxRef) { + int a = 3, b = 9; + EXPECT_EQ(&pointers::max_ref(a, b), &b); +} + +TEST(References, SumThree) { + EXPECT_EQ(pointers::sum_three(1, 2, 3), 6); +} diff --git a/modules/06_oop_basics/CMakeLists.txt b/modules/06_oop_basics/CMakeLists.txt new file mode 100644 index 0000000..e91bdb8 --- /dev/null +++ b/modules/06_oop_basics/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME classes_and_objects MODULE 06_oop_basics STANDARD 17 + SOURCES src/classes_and_objects.cpp test/classes_and_objects_test.cpp) +add_course_exercise(NAME constructors MODULE 06_oop_basics STANDARD 17 + SOURCES src/constructors.cpp test/constructors_test.cpp) +add_course_exercise(NAME inheritance MODULE 06_oop_basics STANDARD 17 + SOURCES src/inheritance.cpp test/inheritance_test.cpp) +add_course_exercise(NAME polymorphism MODULE 06_oop_basics STANDARD 17 + SOURCES src/polymorphism.cpp src/inheritance.cpp test/polymorphism_test.cpp) diff --git a/modules/06_oop_basics/README.md b/modules/06_oop_basics/README.md new file mode 100644 index 0000000..d9f716c --- /dev/null +++ b/modules/06_oop_basics/README.md @@ -0,0 +1,17 @@ +# Module 06: Object-Oriented Programming Basics + +## Learning Goals + +- Design classes with encapsulation +- Implement constructors, destructors, and the Rule of Three/Five +- Use inheritance and virtual functions +- Apply polymorphism through base class interfaces + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `classes_and_objects` | Members, methods, invariants | +| `constructors` | RAII, special members | +| `inheritance` | Base/derived classes | +| `polymorphism` | Virtual functions, overriding | diff --git a/modules/06_oop_basics/include/classes_and_objects.hpp b/modules/06_oop_basics/include/classes_and_objects.hpp new file mode 100644 index 0000000..6a962ca --- /dev/null +++ b/modules/06_oop_basics/include/classes_and_objects.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace oop { + +class BankAccount { +public: + explicit BankAccount(std::string owner, double balance = 0.0); + + [[nodiscard]] auto owner() const -> const std::string&; + [[nodiscard]] auto balance() const -> double; + + void deposit(double amount); + auto withdraw(double amount) -> bool; + +private: + std::string owner_; + double balance_; +}; + +} // namespace oop diff --git a/modules/06_oop_basics/include/constructors.hpp b/modules/06_oop_basics/include/constructors.hpp new file mode 100644 index 0000000..c6c595e --- /dev/null +++ b/modules/06_oop_basics/include/constructors.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace oop { + +class StringBuilder { +public: + StringBuilder() = default; + explicit StringBuilder(std::string initial); + StringBuilder(const StringBuilder& other); + auto operator=(const StringBuilder& other) -> StringBuilder&; + ~StringBuilder() = default; + + void append(const std::string& text); + [[nodiscard]] auto str() const -> std::string; + [[nodiscard]] auto size() const -> std::size_t; + +private: + std::vector buffer_; +}; + +} // namespace oop diff --git a/modules/06_oop_basics/include/inheritance.hpp b/modules/06_oop_basics/include/inheritance.hpp new file mode 100644 index 0000000..66ef8f1 --- /dev/null +++ b/modules/06_oop_basics/include/inheritance.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace oop { + +class Shape { +public: + virtual ~Shape() = default; + [[nodiscard]] virtual auto name() const -> std::string = 0; + [[nodiscard]] virtual auto area() const -> double = 0; +}; + +class Circle : public Shape { +public: + explicit Circle(double radius); + [[nodiscard]] auto name() const -> std::string override; + [[nodiscard]] auto area() const -> double override; + +private: + double radius_; +}; + +class Rectangle : public Shape { +public: + Rectangle(double width, double height); + [[nodiscard]] auto name() const -> std::string override; + [[nodiscard]] auto area() const -> double override; + +private: + double width_; + double height_; +}; + +} // namespace oop diff --git a/modules/06_oop_basics/include/polymorphism.hpp b/modules/06_oop_basics/include/polymorphism.hpp new file mode 100644 index 0000000..a416a67 --- /dev/null +++ b/modules/06_oop_basics/include/polymorphism.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "inheritance.hpp" + +#include +#include +#include + +namespace oop { + +[[nodiscard]] auto total_area(const std::vector>& shapes) -> double; +[[nodiscard]] auto shape_names(const std::vector>& shapes) + -> std::vector; + +class Counter { +public: + Counter() = default; + Counter(const Counter&) { ++copies_; } + auto operator=(const Counter&) -> Counter& { + ++copies_; + return *this; + } + + [[nodiscard]] static auto copies() -> int; + +private: + inline static int copies_ = 0; +}; + +} // namespace oop diff --git a/modules/06_oop_basics/src/classes_and_objects.cpp b/modules/06_oop_basics/src/classes_and_objects.cpp new file mode 100644 index 0000000..18e3da1 --- /dev/null +++ b/modules/06_oop_basics/src/classes_and_objects.cpp @@ -0,0 +1,22 @@ +#include "classes_and_objects.hpp" + +namespace oop { + +BankAccount::BankAccount(std::string owner, double balance) + : owner_(std::move(owner)), balance_(balance) {} + +auto BankAccount::owner() const -> const std::string& { return owner_; } +auto BankAccount::balance() const -> double { return balance_; } + +void BankAccount::deposit(double amount) { + // TODO: Add amount when positive + (void)amount; +} + +auto BankAccount::withdraw(double amount) -> bool { + // TODO: Return false if insufficient funds, else subtract and return true + (void)amount; + return false; +} + +} // namespace oop diff --git a/modules/06_oop_basics/src/constructors.cpp b/modules/06_oop_basics/src/constructors.cpp new file mode 100644 index 0000000..71e77dd --- /dev/null +++ b/modules/06_oop_basics/src/constructors.cpp @@ -0,0 +1,30 @@ +#include "constructors.hpp" + +namespace oop { + +StringBuilder::StringBuilder(std::string initial) { + // TODO: Initialize buffer_ from initial + (void)initial; +} + +StringBuilder::StringBuilder(const StringBuilder& other) : buffer_(other.buffer_) {} + +auto StringBuilder::operator=(const StringBuilder& other) -> StringBuilder& { + if (this != &other) { + buffer_ = other.buffer_; + } + return *this; +} + +void StringBuilder::append(const std::string& text) { + // TODO: Append all characters from text + (void)text; +} + +auto StringBuilder::str() const -> std::string { + return std::string(buffer_.begin(), buffer_.end()); +} + +auto StringBuilder::size() const -> std::size_t { return buffer_.size(); } + +} // namespace oop diff --git a/modules/06_oop_basics/src/inheritance.cpp b/modules/06_oop_basics/src/inheritance.cpp new file mode 100644 index 0000000..afc0143 --- /dev/null +++ b/modules/06_oop_basics/src/inheritance.cpp @@ -0,0 +1,13 @@ +#include "inheritance.hpp" + +namespace oop { + +Circle::Circle(double radius) : radius_(radius) {} +auto Circle::name() const -> std::string { return "Circle"; } +auto Circle::area() const -> double { return 3.141592653589793 * radius_ * radius_; } + +Rectangle::Rectangle(double width, double height) : width_(width), height_(height) {} +auto Rectangle::name() const -> std::string { return "Rectangle"; } +auto Rectangle::area() const -> double { return width_ * height_; } + +} // namespace oop diff --git a/modules/06_oop_basics/src/polymorphism.cpp b/modules/06_oop_basics/src/polymorphism.cpp new file mode 100644 index 0000000..cafd0a3 --- /dev/null +++ b/modules/06_oop_basics/src/polymorphism.cpp @@ -0,0 +1,26 @@ +#include "polymorphism.hpp" + +namespace oop { + +auto total_area(const std::vector>& shapes) -> double { + double total = 0.0; + for (const auto& shape : shapes) { + // TODO: Add each shape's area using polymorphism + (void)shape; + } + return total; +} + +auto shape_names(const std::vector>& shapes) + -> std::vector { + std::vector names; + for (const auto& shape : shapes) { + // TODO: Collect shape->name() + (void)shape; + } + return names; +} + +auto Counter::copies() -> int { return copies_; } + +} // namespace oop diff --git a/modules/06_oop_basics/test/classes_and_objects_test.cpp b/modules/06_oop_basics/test/classes_and_objects_test.cpp new file mode 100644 index 0000000..cd1d28c --- /dev/null +++ b/modules/06_oop_basics/test/classes_and_objects_test.cpp @@ -0,0 +1,16 @@ +#include "classes_and_objects.hpp" +#include + +TEST(ClassesAndObjects, DepositAndWithdraw) { + oop::BankAccount account("Ada", 100.0); + account.deposit(50.0); + EXPECT_DOUBLE_EQ(account.balance(), 150.0); + EXPECT_TRUE(account.withdraw(30.0)); + EXPECT_DOUBLE_EQ(account.balance(), 120.0); + EXPECT_FALSE(account.withdraw(1000.0)); +} + +TEST(ClassesAndObjects, OwnerName) { + oop::BankAccount account("Grace"); + EXPECT_EQ(account.owner(), "Grace"); +} diff --git a/modules/06_oop_basics/test/constructors_test.cpp b/modules/06_oop_basics/test/constructors_test.cpp new file mode 100644 index 0000000..ea9e032 --- /dev/null +++ b/modules/06_oop_basics/test/constructors_test.cpp @@ -0,0 +1,16 @@ +#include "constructors.hpp" +#include + +TEST(Constructors, InitialAndAppend) { + oop::StringBuilder builder("Hi"); + builder.append(" there"); + EXPECT_EQ(builder.str(), "Hi there"); +} + +TEST(Constructors, CopySemantics) { + oop::StringBuilder original("C++"); + oop::StringBuilder copy = original; + copy.append("11"); + EXPECT_EQ(original.str(), "C++"); + EXPECT_EQ(copy.str(), "C++11"); +} diff --git a/modules/06_oop_basics/test/inheritance_test.cpp b/modules/06_oop_basics/test/inheritance_test.cpp new file mode 100644 index 0000000..32cf761 --- /dev/null +++ b/modules/06_oop_basics/test/inheritance_test.cpp @@ -0,0 +1,12 @@ +#include "inheritance.hpp" +#include + +TEST(Inheritance, CircleArea) { + oop::Circle circle(2.0); + EXPECT_NEAR(circle.area(), 12.566370614359172, 1e-9); +} + +TEST(Inheritance, RectangleArea) { + oop::Rectangle rect(3.0, 4.0); + EXPECT_DOUBLE_EQ(rect.area(), 12.0); +} diff --git a/modules/06_oop_basics/test/polymorphism_test.cpp b/modules/06_oop_basics/test/polymorphism_test.cpp new file mode 100644 index 0000000..2e5fa4e --- /dev/null +++ b/modules/06_oop_basics/test/polymorphism_test.cpp @@ -0,0 +1,16 @@ +#include "polymorphism.hpp" +#include + +TEST(Polymorphism, TotalArea) { + std::vector> shapes; + shapes.push_back(std::make_unique(2.0, 3.0)); + shapes.push_back(std::make_unique(1.0)); + EXPECT_NEAR(oop::total_area(shapes), 6.0 + 3.141592653589793, 1e-9); +} + +TEST(Polymorphism, ShapeNames) { + std::vector> shapes; + shapes.push_back(std::make_unique(1.0)); + shapes.push_back(std::make_unique(1.0, 2.0)); + EXPECT_EQ(oop::shape_names(shapes), (std::vector{"Circle", "Rectangle"})); +} diff --git a/modules/07_stl_containers/CMakeLists.txt b/modules/07_stl_containers/CMakeLists.txt new file mode 100644 index 0000000..7928d4d --- /dev/null +++ b/modules/07_stl_containers/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME sequential_containers MODULE 07_stl_containers STANDARD 17 + SOURCES src/sequential_containers.cpp test/sequential_containers_test.cpp) +add_course_exercise(NAME associative_containers MODULE 07_stl_containers STANDARD 17 + SOURCES src/associative_containers.cpp test/associative_containers_test.cpp) +add_course_exercise(NAME unordered_containers MODULE 07_stl_containers STANDARD 17 + SOURCES src/unordered_containers.cpp test/unordered_containers_test.cpp) +add_course_exercise(NAME container_adapters MODULE 07_stl_containers STANDARD 17 + SOURCES src/container_adapters.cpp test/container_adapters_test.cpp) diff --git a/modules/07_stl_containers/README.md b/modules/07_stl_containers/README.md new file mode 100644 index 0000000..10b34f7 --- /dev/null +++ b/modules/07_stl_containers/README.md @@ -0,0 +1,17 @@ +# Module 07: STL Containers + +## Learning Goals + +- Choose the right sequential container for the job +- Use associative containers (`map`, `set`) effectively +- Leverage unordered containers for average O(1) lookup +- Apply container adapters (`stack`, `queue`) + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `sequential_containers` | vector, list, deque | +| `associative_containers` | map, set, multimap | +| `unordered_containers` | unordered_map, unordered_set | +| `container_adapters` | stack, queue, priority_queue | diff --git a/modules/07_stl_containers/include/associative_containers.hpp b/modules/07_stl_containers/include/associative_containers.hpp new file mode 100644 index 0000000..ab4137d --- /dev/null +++ b/modules/07_stl_containers/include/associative_containers.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include + +namespace stl_containers { + +[[nodiscard]] auto word_frequencies(const std::vector& words) + -> std::map; +[[nodiscard]] auto unique_sorted(const std::vector& data) -> std::set; +[[nodiscard]] auto invert_map(const std::map& input) + -> std::map; + +} // namespace stl_containers diff --git a/modules/07_stl_containers/include/container_adapters.hpp b/modules/07_stl_containers/include/container_adapters.hpp new file mode 100644 index 0000000..2d84354 --- /dev/null +++ b/modules/07_stl_containers/include/container_adapters.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include + +namespace stl_containers { + +[[nodiscard]] auto is_balanced_parentheses(const std::string& text) -> bool; +[[nodiscard]] auto simulate_queue(const std::vector& arrivals) -> std::vector; +[[nodiscard]] auto top_k_largest(const std::vector& data, int k) -> std::vector; + +} // namespace stl_containers diff --git a/modules/07_stl_containers/include/sequential_containers.hpp b/modules/07_stl_containers/include/sequential_containers.hpp new file mode 100644 index 0000000..921abb8 --- /dev/null +++ b/modules/07_stl_containers/include/sequential_containers.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace stl_containers { + +[[nodiscard]] auto merge_sorted_vectors(const std::vector& a, const std::vector& b) + -> std::vector; +[[nodiscard]] auto list_to_vector(const std::list& data) -> std::vector; +[[nodiscard]] auto rotate_deque(std::deque data, int steps) -> std::deque; + +} // namespace stl_containers diff --git a/modules/07_stl_containers/include/unordered_containers.hpp b/modules/07_stl_containers/include/unordered_containers.hpp new file mode 100644 index 0000000..7072909 --- /dev/null +++ b/modules/07_stl_containers/include/unordered_containers.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +namespace stl_containers { + +[[nodiscard]] auto first_unique(const std::vector& words) -> std::string; +[[nodiscard]] auto group_anagrams(const std::vector& words) + -> std::unordered_map>; +[[nodiscard]] auto has_duplicate(const std::vector& data) -> bool; + +} // namespace stl_containers diff --git a/modules/07_stl_containers/src/associative_containers.cpp b/modules/07_stl_containers/src/associative_containers.cpp new file mode 100644 index 0000000..90dfcef --- /dev/null +++ b/modules/07_stl_containers/src/associative_containers.cpp @@ -0,0 +1,21 @@ +#include "associative_containers.hpp" + +namespace stl_containers { + +auto word_frequencies(const std::vector& words) + -> std::map { + (void)words; + return {}; +} + +auto unique_sorted(const std::vector& data) -> std::set { + (void)data; + return {}; +} + +auto invert_map(const std::map& input) -> std::map { + (void)input; + return {}; +} + +} // namespace stl_containers diff --git a/modules/07_stl_containers/src/container_adapters.cpp b/modules/07_stl_containers/src/container_adapters.cpp new file mode 100644 index 0000000..a261174 --- /dev/null +++ b/modules/07_stl_containers/src/container_adapters.cpp @@ -0,0 +1,20 @@ +#include "container_adapters.hpp" + +namespace stl_containers { + +auto is_balanced_parentheses(const std::string& text) -> bool { + (void)text; + return false; +} + +auto simulate_queue(const std::vector& arrivals) -> std::vector { + (void)arrivals; + return {}; +} + +auto top_k_largest(const std::vector& data, int k) -> std::vector { + (void)data; (void)k; + return {}; +} + +} // namespace stl_containers diff --git a/modules/07_stl_containers/src/sequential_containers.cpp b/modules/07_stl_containers/src/sequential_containers.cpp new file mode 100644 index 0000000..6b69292 --- /dev/null +++ b/modules/07_stl_containers/src/sequential_containers.cpp @@ -0,0 +1,21 @@ +#include "sequential_containers.hpp" + +namespace stl_containers { + +auto merge_sorted_vectors(const std::vector& a, const std::vector& b) + -> std::vector { + (void)a; (void)b; + return {}; +} + +auto list_to_vector(const std::list& data) -> std::vector { + (void)data; + return {}; +} + +auto rotate_deque(std::deque data, int steps) -> std::deque { + (void)data; (void)steps; + return {}; +} + +} // namespace stl_containers diff --git a/modules/07_stl_containers/src/unordered_containers.cpp b/modules/07_stl_containers/src/unordered_containers.cpp new file mode 100644 index 0000000..038aba4 --- /dev/null +++ b/modules/07_stl_containers/src/unordered_containers.cpp @@ -0,0 +1,21 @@ +#include "unordered_containers.hpp" + +namespace stl_containers { + +auto first_unique(const std::vector& words) -> std::string { + (void)words; + return {}; +} + +auto group_anagrams(const std::vector& words) + -> std::unordered_map> { + (void)words; + return {}; +} + +auto has_duplicate(const std::vector& data) -> bool { + (void)data; + return false; +} + +} // namespace stl_containers diff --git a/modules/07_stl_containers/test/associative_containers_test.cpp b/modules/07_stl_containers/test/associative_containers_test.cpp new file mode 100644 index 0000000..d033402 --- /dev/null +++ b/modules/07_stl_containers/test/associative_containers_test.cpp @@ -0,0 +1,18 @@ +#include "associative_containers.hpp" +#include + +TEST(AssociativeContainers, WordFrequencies) { + const auto freq = stl_containers::word_frequencies({"a", "b", "a"}); + EXPECT_EQ(freq.at("a"), 2); + EXPECT_EQ(freq.at("b"), 1); +} + +TEST(AssociativeContainers, UniqueSorted) { + EXPECT_EQ(stl_containers::unique_sorted({3, 1, 2, 2}), (std::set{1, 2, 3})); +} + +TEST(AssociativeContainers, InvertMap) { + const std::map input{{1, "one"}, {2, "two"}}; + const auto inverted = stl_containers::invert_map(input); + EXPECT_EQ(inverted.at("one"), 1); +} diff --git a/modules/07_stl_containers/test/container_adapters_test.cpp b/modules/07_stl_containers/test/container_adapters_test.cpp new file mode 100644 index 0000000..31418e1 --- /dev/null +++ b/modules/07_stl_containers/test/container_adapters_test.cpp @@ -0,0 +1,15 @@ +#include "container_adapters.hpp" +#include + +TEST(ContainerAdapters, BalancedParentheses) { + EXPECT_TRUE(stl_containers::is_balanced_parentheses("()[]{}")); + EXPECT_FALSE(stl_containers::is_balanced_parentheses("([)]")); +} + +TEST(ContainerAdapters, SimulateQueue) { + EXPECT_EQ(stl_containers::simulate_queue({1, 2, 3}), (std::vector{1, 2, 3})); +} + +TEST(ContainerAdapters, TopKLargest) { + EXPECT_EQ(stl_containers::top_k_largest({3, 1, 4, 1, 5}, 3), (std::vector{5, 4, 3})); +} diff --git a/modules/07_stl_containers/test/sequential_containers_test.cpp b/modules/07_stl_containers/test/sequential_containers_test.cpp new file mode 100644 index 0000000..6ea5942 --- /dev/null +++ b/modules/07_stl_containers/test/sequential_containers_test.cpp @@ -0,0 +1,15 @@ +#include "sequential_containers.hpp" +#include + +TEST(SequentialContainers, MergeSorted) { + EXPECT_EQ(stl_containers::merge_sorted_vectors({1, 3, 5}, {2, 4, 6}), + (std::vector{1, 2, 3, 4, 5, 6})); +} + +TEST(SequentialContainers, ListToVector) { + EXPECT_EQ(stl_containers::list_to_vector({1, 2, 3}), (std::vector{1, 2, 3})); +} + +TEST(SequentialContainers, RotateDeque) { + EXPECT_EQ(stl_containers::rotate_deque({1, 2, 3, 4}, 1), (std::deque{2, 3, 4, 1})); +} diff --git a/modules/07_stl_containers/test/unordered_containers_test.cpp b/modules/07_stl_containers/test/unordered_containers_test.cpp new file mode 100644 index 0000000..21abe54 --- /dev/null +++ b/modules/07_stl_containers/test/unordered_containers_test.cpp @@ -0,0 +1,11 @@ +#include "unordered_containers.hpp" +#include + +TEST(UnorderedContainers, FirstUnique) { + EXPECT_EQ(stl_containers::first_unique({"a", "b", "a", "c"}), "b"); +} + +TEST(UnorderedContainers, HasDuplicate) { + EXPECT_TRUE(stl_containers::has_duplicate({1, 2, 2})); + EXPECT_FALSE(stl_containers::has_duplicate({1, 2, 3})); +} diff --git a/modules/08_stl_algorithms/CMakeLists.txt b/modules/08_stl_algorithms/CMakeLists.txt new file mode 100644 index 0000000..c3cab2f --- /dev/null +++ b/modules/08_stl_algorithms/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME sort_and_find MODULE 08_stl_algorithms STANDARD 17 + SOURCES src/sort_and_find.cpp test/sort_and_find_test.cpp) +add_course_exercise(NAME transform_reduce MODULE 08_stl_algorithms STANDARD 17 + SOURCES src/transform_reduce.cpp test/transform_reduce_test.cpp) +add_course_exercise(NAME iterators MODULE 08_stl_algorithms STANDARD 17 + SOURCES src/iterators.cpp test/iterators_test.cpp) +add_course_exercise(NAME algorithm_lambdas MODULE 08_stl_algorithms STANDARD 17 + SOURCES src/algorithm_lambdas.cpp test/algorithm_lambdas_test.cpp) diff --git a/modules/08_stl_algorithms/README.md b/modules/08_stl_algorithms/README.md new file mode 100644 index 0000000..a11eef0 --- /dev/null +++ b/modules/08_stl_algorithms/README.md @@ -0,0 +1,17 @@ +# Module 08: STL Algorithms + +## Learning Goals + +- Use `` for sorting, searching, and transforming +- Understand iterator categories and valid operations +- Combine algorithms with function objects and lambdas +- Write expressive data-processing pipelines + +## Exercises + +| Exercise | Topic | +|----------|-------| +| `sort_and_find` | sort, binary_search, find | +| `transform_reduce` | transform, accumulate, reduce | +| `iterators` | Iterator patterns and adapters | +| `algorithm_lambdas` | Lambdas with STL algorithms | diff --git a/modules/08_stl_algorithms/include/algorithm_lambdas.hpp b/modules/08_stl_algorithms/include/algorithm_lambdas.hpp new file mode 100644 index 0000000..1e3b6f0 --- /dev/null +++ b/modules/08_stl_algorithms/include/algorithm_lambdas.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace stl_algorithms { + +[[nodiscard]] auto filter_length(const std::vector& words, std::size_t min_len) + -> std::vector; +[[nodiscard]] auto map_to_lengths(const std::vector& words) -> std::vector; +[[nodiscard]] auto first_match(const std::vector& data, int threshold) -> int; + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/include/iterators.hpp b/modules/08_stl_algorithms/include/iterators.hpp new file mode 100644 index 0000000..1488062 --- /dev/null +++ b/modules/08_stl_algorithms/include/iterators.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace stl_algorithms { + +[[nodiscard]] auto reverse_copy(const std::vector& data) -> std::vector; +[[nodiscard]] auto countGreaterThan(const std::vector& data, int threshold) -> int; +[[nodiscard]] auto everyEven(const std::vector& data) -> bool; + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/include/sort_and_find.hpp b/modules/08_stl_algorithms/include/sort_and_find.hpp new file mode 100644 index 0000000..e5710ad --- /dev/null +++ b/modules/08_stl_algorithms/include/sort_and_find.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace stl_algorithms { + +[[nodiscard]] auto sorted_copy(std::vector data) -> std::vector; +[[nodiscard]] auto contains(const std::vector& data, int value) -> bool; +[[nodiscard]] auto lower_bound_index(const std::vector& sorted, int value) -> int; + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/include/transform_reduce.hpp b/modules/08_stl_algorithms/include/transform_reduce.hpp new file mode 100644 index 0000000..8e42353 --- /dev/null +++ b/modules/08_stl_algorithms/include/transform_reduce.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace stl_algorithms { + +[[nodiscard]] auto square_all(const std::vector& data) -> std::vector; +[[nodiscard]] auto sum_all(const std::vector& data) -> int; +[[nodiscard]] auto product_positive(const std::vector& data) -> long long; + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/src/algorithm_lambdas.cpp b/modules/08_stl_algorithms/src/algorithm_lambdas.cpp new file mode 100644 index 0000000..a25941d --- /dev/null +++ b/modules/08_stl_algorithms/src/algorithm_lambdas.cpp @@ -0,0 +1,21 @@ +#include "algorithm_lambdas.hpp" + +namespace stl_algorithms { + +auto filter_length(const std::vector& words, std::size_t min_len) + -> std::vector { + (void)words; (void)min_len; + return {}; +} + +auto map_to_lengths(const std::vector& words) -> std::vector { + (void)words; + return {}; +} + +auto first_match(const std::vector& data, int threshold) -> int { + (void)data; (void)threshold; + return -1; +} + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/src/iterators.cpp b/modules/08_stl_algorithms/src/iterators.cpp new file mode 100644 index 0000000..551a220 --- /dev/null +++ b/modules/08_stl_algorithms/src/iterators.cpp @@ -0,0 +1,9 @@ +#include "iterators.hpp" + +namespace stl_algorithms { + +auto reverse_copy(const std::vector& data) -> std::vector { (void)data; return {}; } +auto countGreaterThan(const std::vector& data, int threshold) -> int { (void)data; (void)threshold; return 0; } +auto everyEven(const std::vector& data) -> bool { (void)data; return false; } + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/src/sort_and_find.cpp b/modules/08_stl_algorithms/src/sort_and_find.cpp new file mode 100644 index 0000000..19a7384 --- /dev/null +++ b/modules/08_stl_algorithms/src/sort_and_find.cpp @@ -0,0 +1,9 @@ +#include "sort_and_find.hpp" + +namespace stl_algorithms { + +auto sorted_copy(std::vector data) -> std::vector { (void)data; return {}; } +auto contains(const std::vector& data, int value) -> bool { (void)data; (void)value; return false; } +auto lower_bound_index(const std::vector& sorted, int value) -> int { (void)sorted; (void)value; return -1; } + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/src/transform_reduce.cpp b/modules/08_stl_algorithms/src/transform_reduce.cpp new file mode 100644 index 0000000..0e17e8b --- /dev/null +++ b/modules/08_stl_algorithms/src/transform_reduce.cpp @@ -0,0 +1,9 @@ +#include "transform_reduce.hpp" + +namespace stl_algorithms { + +auto square_all(const std::vector& data) -> std::vector { (void)data; return {}; } +auto sum_all(const std::vector& data) -> int { (void)data; return 0; } +auto product_positive(const std::vector& data) -> long long { (void)data; return 0; } + +} // namespace stl_algorithms diff --git a/modules/08_stl_algorithms/test/algorithm_lambdas_test.cpp b/modules/08_stl_algorithms/test/algorithm_lambdas_test.cpp new file mode 100644 index 0000000..7017030 --- /dev/null +++ b/modules/08_stl_algorithms/test/algorithm_lambdas_test.cpp @@ -0,0 +1,15 @@ +#include "algorithm_lambdas.hpp" +#include + +TEST(AlgorithmLambdas, FilterLength) { + EXPECT_EQ(stl_algorithms::filter_length({"a", "ab", "abc"}, 2), + (std::vector{"ab", "abc"})); +} + +TEST(AlgorithmLambdas, MapToLengths) { + EXPECT_EQ(stl_algorithms::map_to_lengths({"hi", "there"}), (std::vector{2, 5})); +} + +TEST(AlgorithmLambdas, FirstMatch) { + EXPECT_EQ(stl_algorithms::first_match({1, 5, 9}, 4), 5); +} diff --git a/modules/08_stl_algorithms/test/iterators_test.cpp b/modules/08_stl_algorithms/test/iterators_test.cpp new file mode 100644 index 0000000..8356239 --- /dev/null +++ b/modules/08_stl_algorithms/test/iterators_test.cpp @@ -0,0 +1,15 @@ +#include "iterators.hpp" +#include + +TEST(Iterators, ReverseCopy) { + EXPECT_EQ(stl_algorithms::reverse_copy({1, 2, 3}), (std::vector{3, 2, 1})); +} + +TEST(Iterators, CountGreaterThan) { + EXPECT_EQ(stl_algorithms::countGreaterThan({1, 5, 9}, 4), 2); +} + +TEST(Iterators, EveryEven) { + EXPECT_TRUE(stl_algorithms::everyEven({2, 4, 6})); + EXPECT_FALSE(stl_algorithms::everyEven({2, 3})); +} diff --git a/modules/08_stl_algorithms/test/sort_and_find_test.cpp b/modules/08_stl_algorithms/test/sort_and_find_test.cpp new file mode 100644 index 0000000..76d9d1a --- /dev/null +++ b/modules/08_stl_algorithms/test/sort_and_find_test.cpp @@ -0,0 +1,14 @@ +#include "sort_and_find.hpp" +#include + +TEST(SortAndFind, SortedCopy) { + EXPECT_EQ(stl_algorithms::sorted_copy({3, 1, 2}), (std::vector{1, 2, 3})); +} + +TEST(SortAndFind, Contains) { + EXPECT_TRUE(stl_algorithms::contains({1, 2, 3}, 2)); +} + +TEST(SortAndFind, LowerBoundIndex) { + EXPECT_EQ(stl_algorithms::lower_bound_index({1, 3, 5, 7}, 5), 2); +} diff --git a/modules/08_stl_algorithms/test/transform_reduce_test.cpp b/modules/08_stl_algorithms/test/transform_reduce_test.cpp new file mode 100644 index 0000000..84073c2 --- /dev/null +++ b/modules/08_stl_algorithms/test/transform_reduce_test.cpp @@ -0,0 +1,14 @@ +#include "transform_reduce.hpp" +#include + +TEST(TransformReduce, SquareAll) { + EXPECT_EQ(stl_algorithms::square_all({1, 2, 3}), (std::vector{1, 4, 9})); +} + +TEST(TransformReduce, SumAll) { + EXPECT_EQ(stl_algorithms::sum_all({1, 2, 3}), 6); +} + +TEST(TransformReduce, ProductPositive) { + EXPECT_EQ(stl_algorithms::product_positive({-1, 2, 3, -4}), 6); +} diff --git a/modules/09_cpp11/CMakeLists.txt b/modules/09_cpp11/CMakeLists.txt new file mode 100644 index 0000000..0df8cb0 --- /dev/null +++ b/modules/09_cpp11/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME auto_and_range_for MODULE 09_cpp11 STANDARD 11 + SOURCES src/auto_and_range_for.cpp test/auto_and_range_for_test.cpp) +add_course_exercise(NAME smart_pointers MODULE 09_cpp11 STANDARD 11 + SOURCES src/smart_pointers.cpp test/smart_pointers_test.cpp) +add_course_exercise(NAME move_semantics MODULE 09_cpp11 STANDARD 11 + SOURCES src/move_semantics.cpp test/move_semantics_test.cpp) +add_course_exercise(NAME constexpr_nullptr MODULE 09_cpp11 STANDARD 11 + SOURCES src/constexpr_nullptr.cpp test/constexpr_nullptr_test.cpp) diff --git a/modules/09_cpp11/README.md b/modules/09_cpp11/README.md new file mode 100644 index 0000000..026cf0b --- /dev/null +++ b/modules/09_cpp11/README.md @@ -0,0 +1,17 @@ +# Module 09: C++11 Foundations + +## Learning Goals + +- Use `auto`, range-based for, and uniform initialization +- Manage ownership with smart pointers +- Understand move semantics and rvalue references +- Apply `constexpr`, `nullptr`, and `enum class` + +## Exercises + +| Exercise | Standard Feature | +|----------|------------------| +| `auto_and_range_for` | Type deduction, range-for | +| `smart_pointers` | unique_ptr, shared_ptr | +| `move_semantics` | std::move, move constructors | +| `constexpr_nullptr` | constexpr functions, nullptr, enum class | diff --git a/modules/09_cpp11/include/auto_and_range_for.hpp b/modules/09_cpp11/include/auto_and_range_for.hpp new file mode 100644 index 0000000..91a8146 --- /dev/null +++ b/modules/09_cpp11/include/auto_and_range_for.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace cpp11 { + +[[nodiscard]] auto double_values(const std::vector& input) -> std::vector; +[[nodiscard]] auto join_with_auto(const std::vector& parts) -> std::string; +[[nodiscard]] auto count_if_positive(const std::vector& input) -> int; + +} // namespace cpp11 diff --git a/modules/09_cpp11/include/constexpr_nullptr.hpp b/modules/09_cpp11/include/constexpr_nullptr.hpp new file mode 100644 index 0000000..4eb7ba0 --- /dev/null +++ b/modules/09_cpp11/include/constexpr_nullptr.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace cpp11 { + +enum class Color { Red, Green, Blue }; + +[[nodiscard]] auto to_index(Color color) -> int; +[[nodiscard]] auto square(int value) -> int; +[[nodiscard]] auto find_null(int* ptr) -> int*; + +} // namespace cpp11 diff --git a/modules/09_cpp11/include/move_semantics.hpp b/modules/09_cpp11/include/move_semantics.hpp new file mode 100644 index 0000000..1bf5e9c --- /dev/null +++ b/modules/09_cpp11/include/move_semantics.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace cpp11 { + +class MovableBuffer { +public: + MovableBuffer() = default; + explicit MovableBuffer(std::vector data); + MovableBuffer(const MovableBuffer&) = delete; + MovableBuffer(MovableBuffer&& other) noexcept; + auto operator=(MovableBuffer&& other) noexcept -> MovableBuffer&; + + [[nodiscard]] auto size() const -> std::size_t; + [[nodiscard]] auto data() const -> const std::vector&; + +private: + std::vector data_; +}; + +[[nodiscard]] auto consume(MovableBuffer buffer) -> std::size_t; + +} // namespace cpp11 diff --git a/modules/09_cpp11/include/smart_pointers.hpp b/modules/09_cpp11/include/smart_pointers.hpp new file mode 100644 index 0000000..3fbec60 --- /dev/null +++ b/modules/09_cpp11/include/smart_pointers.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace cpp11 { + +[[nodiscard]] auto make_counter(int start) -> std::unique_ptr; +[[nodiscard]] auto shared_total(const std::vector>& values) -> int; +[[nodiscard]] auto clone_unique(const std::unique_ptr& value) -> std::unique_ptr; + +} // namespace cpp11 diff --git a/modules/09_cpp11/src/auto_and_range_for.cpp b/modules/09_cpp11/src/auto_and_range_for.cpp new file mode 100644 index 0000000..7830dc8 --- /dev/null +++ b/modules/09_cpp11/src/auto_and_range_for.cpp @@ -0,0 +1,20 @@ +#include "auto_and_range_for.hpp" + +namespace cpp11 { + +auto double_values(const std::vector& input) -> std::vector { + (void)input; + return {}; +} + +auto join_with_auto(const std::vector& parts) -> std::string { + (void)parts; + return {}; +} + +auto count_if_positive(const std::vector& input) -> int { + (void)input; + return 0; +} + +} // namespace cpp11 diff --git a/modules/09_cpp11/src/constexpr_nullptr.cpp b/modules/09_cpp11/src/constexpr_nullptr.cpp new file mode 100644 index 0000000..445a404 --- /dev/null +++ b/modules/09_cpp11/src/constexpr_nullptr.cpp @@ -0,0 +1,24 @@ +#include "constexpr_nullptr.hpp" + +namespace cpp11 { + +auto to_index(Color color) -> int { + switch (color) { + case Color::Red: return 0; + case Color::Green: return 1; + case Color::Blue: return 2; + } + return -1; +} + +auto square(int value) -> int { + (void)value; + return 0; +} + +auto find_null(int* ptr) -> int* { + (void)ptr; + return nullptr; +} + +} // namespace cpp11 diff --git a/modules/09_cpp11/src/move_semantics.cpp b/modules/09_cpp11/src/move_semantics.cpp new file mode 100644 index 0000000..1ab142f --- /dev/null +++ b/modules/09_cpp11/src/move_semantics.cpp @@ -0,0 +1,24 @@ +#include "move_semantics.hpp" + +namespace cpp11 { + +MovableBuffer::MovableBuffer(std::vector data) : data_(std::move(data)) {} + +MovableBuffer::MovableBuffer(MovableBuffer&& other) noexcept : data_(std::move(other.data_)) {} + +auto MovableBuffer::operator=(MovableBuffer&& other) noexcept -> MovableBuffer& { + if (this != &other) { + data_ = std::move(other.data_); + } + return *this; +} + +auto MovableBuffer::size() const -> std::size_t { return data_.size(); } +auto MovableBuffer::data() const -> const std::vector& { return data_; } + +auto consume(MovableBuffer buffer) -> std::size_t { + (void)buffer; + return 0; +} + +} // namespace cpp11 diff --git a/modules/09_cpp11/src/smart_pointers.cpp b/modules/09_cpp11/src/smart_pointers.cpp new file mode 100644 index 0000000..072134a --- /dev/null +++ b/modules/09_cpp11/src/smart_pointers.cpp @@ -0,0 +1,20 @@ +#include "smart_pointers.hpp" + +namespace cpp11 { + +auto make_counter(int start) -> std::unique_ptr { + (void)start; + return nullptr; +} + +auto shared_total(const std::vector>& values) -> int { + (void)values; + return 0; +} + +auto clone_unique(const std::unique_ptr& value) -> std::unique_ptr { + (void)value; + return nullptr; +} + +} // namespace cpp11 diff --git a/modules/09_cpp11/test/auto_and_range_for_test.cpp b/modules/09_cpp11/test/auto_and_range_for_test.cpp new file mode 100644 index 0000000..f648c3d --- /dev/null +++ b/modules/09_cpp11/test/auto_and_range_for_test.cpp @@ -0,0 +1,14 @@ +#include "auto_and_range_for.hpp" +#include + +TEST(AutoAndRangeFor, DoubleValues) { + EXPECT_EQ(cpp11::double_values({1, 2, 3}), (std::vector{2, 4, 6})); +} + +TEST(AutoAndRangeFor, JoinWithAuto) { + EXPECT_EQ(cpp11::join_with_auto({"C", "++", "11"}), "C++11"); +} + +TEST(AutoAndRangeFor, CountIfPositive) { + EXPECT_EQ(cpp11::count_if_positive({-1, 0, 2, 3}), 2); +} diff --git a/modules/09_cpp11/test/constexpr_nullptr_test.cpp b/modules/09_cpp11/test/constexpr_nullptr_test.cpp new file mode 100644 index 0000000..a103ad8 --- /dev/null +++ b/modules/09_cpp11/test/constexpr_nullptr_test.cpp @@ -0,0 +1,13 @@ +#include "constexpr_nullptr.hpp" +#include + +TEST(ConstexprNullptr, EnumClass) { + EXPECT_EQ(cpp11::to_index(cpp11::Color::Green), 1); + EXPECT_EQ(cpp11::square(4), 16); +} + +TEST(ConstexprNullptr, FindNull) { + int x = 1; + EXPECT_EQ(cpp11::find_null(&x), &x); + EXPECT_EQ(cpp11::find_null(nullptr), nullptr); +} diff --git a/modules/09_cpp11/test/move_semantics_test.cpp b/modules/09_cpp11/test/move_semantics_test.cpp new file mode 100644 index 0000000..d51413a --- /dev/null +++ b/modules/09_cpp11/test/move_semantics_test.cpp @@ -0,0 +1,9 @@ +#include "move_semantics.hpp" +#include + +TEST(MoveSemantics, MoveTransfersOwnership) { + cpp11::MovableBuffer source{{1, 2, 3}}; + const auto moved_size = cpp11::consume(std::move(source)); + EXPECT_EQ(moved_size, 3U); + EXPECT_EQ(source.size(), 0U); +} diff --git a/modules/09_cpp11/test/smart_pointers_test.cpp b/modules/09_cpp11/test/smart_pointers_test.cpp new file mode 100644 index 0000000..b4b5c98 --- /dev/null +++ b/modules/09_cpp11/test/smart_pointers_test.cpp @@ -0,0 +1,22 @@ +#include "smart_pointers.hpp" +#include + +TEST(SmartPointers, MakeCounter) { + auto counter = cpp11::make_counter(42); + ASSERT_NE(counter, nullptr); + EXPECT_EQ(*counter, 42); +} + +TEST(SmartPointers, SharedTotal) { + std::vector> values; + values.push_back(std::make_shared(1)); + values.push_back(std::make_shared(2)); + EXPECT_EQ(cpp11::shared_total(values), 3); +} + +TEST(SmartPointers, CloneUnique) { + auto original = cpp11::make_counter(7); + auto copy = cpp11::clone_unique(original); + ASSERT_NE(copy, nullptr); + EXPECT_EQ(*copy, 7); +} diff --git a/modules/10_cpp14/CMakeLists.txt b/modules/10_cpp14/CMakeLists.txt new file mode 100644 index 0000000..5191e0c --- /dev/null +++ b/modules/10_cpp14/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME generic_lambdas MODULE 10_cpp14 STANDARD 14 + SOURCES src/generic_lambdas.cpp test/generic_lambdas_test.cpp) +add_course_exercise(NAME auto_return MODULE 10_cpp14 STANDARD 14 + SOURCES src/auto_return.cpp test/auto_return_test.cpp) +add_course_exercise(NAME make_unique MODULE 10_cpp14 STANDARD 14 + SOURCES src/make_unique.cpp test/make_unique_test.cpp) +add_course_exercise(NAME digit_separators MODULE 10_cpp14 STANDARD 14 + SOURCES src/digit_separators.cpp test/digit_separators_test.cpp) diff --git a/modules/10_cpp14/README.md b/modules/10_cpp14/README.md new file mode 100644 index 0000000..f58ad1f --- /dev/null +++ b/modules/10_cpp14/README.md @@ -0,0 +1,17 @@ +# Module 10: C++14 Improvements + +## Learning Goals + +- Write generic lambdas with `auto` parameters +- Use return type deduction with `auto` +- Prefer `std::make_unique` for heap ownership +- Read numeric literals with digit separators + +## Exercises + +| Exercise | Feature | +|----------|---------| +| `generic_lambdas` | Generic lambda parameters | +| `auto_return` | Decltype(auto) return deduction | +| `make_unique` | Factory helpers | +| `digit_separators` | Readable numeric literals | diff --git a/modules/10_cpp14/include/auto_return.hpp b/modules/10_cpp14/include/auto_return.hpp new file mode 100644 index 0000000..8df0275 --- /dev/null +++ b/modules/10_cpp14/include/auto_return.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace cpp14 { + +[[nodiscard]] inline auto copy_or_reference(const std::vector& input) -> decltype(auto) { + (void)input; + static std::vector empty; + return empty; +} + +[[nodiscard]] inline auto identity(int value) -> decltype(auto) { return value; } + +} // namespace cpp14 diff --git a/modules/10_cpp14/include/digit_separators.hpp b/modules/10_cpp14/include/digit_separators.hpp new file mode 100644 index 0000000..f4f014c --- /dev/null +++ b/modules/10_cpp14/include/digit_separators.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace cpp14 { + +[[nodiscard]] auto million() -> int; +[[nodiscard]] auto mask() -> std::uint32_t; +[[nodiscard]] auto bits_set() -> int; + +} // namespace cpp14 diff --git a/modules/10_cpp14/include/generic_lambdas.hpp b/modules/10_cpp14/include/generic_lambdas.hpp new file mode 100644 index 0000000..1a1dc60 --- /dev/null +++ b/modules/10_cpp14/include/generic_lambdas.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace cpp14 { + +[[nodiscard]] auto apply_twice(const std::vector& values) -> std::vector; +[[nodiscard]] auto concat_any(const std::vector& a, const std::vector& b) + -> std::vector; + +} // namespace cpp14 diff --git a/modules/10_cpp14/include/make_unique.hpp b/modules/10_cpp14/include/make_unique.hpp new file mode 100644 index 0000000..ca6f1c8 --- /dev/null +++ b/modules/10_cpp14/include/make_unique.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace cpp14 { + +struct Task { + std::string name; + int priority; +}; + +[[nodiscard]] auto make_task(std::string name, int priority) -> std::unique_ptr; +[[nodiscard]] auto clone_task(const Task& task) -> std::unique_ptr; +[[nodiscard]] auto total_priority(const std::vector>& tasks) -> int; + +} // namespace cpp14 diff --git a/modules/10_cpp14/src/auto_return.cpp b/modules/10_cpp14/src/auto_return.cpp new file mode 100644 index 0000000..f564488 --- /dev/null +++ b/modules/10_cpp14/src/auto_return.cpp @@ -0,0 +1,3 @@ +#include "auto_return.hpp" + +// Implementations live in the header because decltype(auto) requires a visible body. diff --git a/modules/10_cpp14/src/digit_separators.cpp b/modules/10_cpp14/src/digit_separators.cpp new file mode 100644 index 0000000..b3babbe --- /dev/null +++ b/modules/10_cpp14/src/digit_separators.cpp @@ -0,0 +1,20 @@ +#include "digit_separators.hpp" + +namespace cpp14 { + +auto million() -> int { + // TODO: Return 1'000'000 using digit separators + return 0; +} + +auto mask() -> std::uint32_t { + // TODO: Return 0xFF00'00FF + return 0; +} + +auto bits_set() -> int { + // TODO: Return population count of mask() + return 0; +} + +} // namespace cpp14 diff --git a/modules/10_cpp14/src/generic_lambdas.cpp b/modules/10_cpp14/src/generic_lambdas.cpp new file mode 100644 index 0000000..9870d74 --- /dev/null +++ b/modules/10_cpp14/src/generic_lambdas.cpp @@ -0,0 +1,16 @@ +#include "generic_lambdas.hpp" + +namespace cpp14 { + +auto apply_twice(const std::vector& values) -> std::vector { + (void)values; + return {}; +} + +auto concat_any(const std::vector& a, const std::vector& b) + -> std::vector { + (void)a; (void)b; + return {}; +} + +} // namespace cpp14 diff --git a/modules/10_cpp14/src/make_unique.cpp b/modules/10_cpp14/src/make_unique.cpp new file mode 100644 index 0000000..4afb563 --- /dev/null +++ b/modules/10_cpp14/src/make_unique.cpp @@ -0,0 +1,20 @@ +#include "make_unique.hpp" + +namespace cpp14 { + +auto make_task(std::string name, int priority) -> std::unique_ptr { + (void)name; (void)priority; + return nullptr; +} + +auto clone_task(const Task& task) -> std::unique_ptr { + (void)task; + return nullptr; +} + +auto total_priority(const std::vector>& tasks) -> int { + (void)tasks; + return 0; +} + +} // namespace cpp14 diff --git a/modules/10_cpp14/test/auto_return_test.cpp b/modules/10_cpp14/test/auto_return_test.cpp new file mode 100644 index 0000000..691fc98 --- /dev/null +++ b/modules/10_cpp14/test/auto_return_test.cpp @@ -0,0 +1,12 @@ +#include "auto_return.hpp" +#include + +TEST(AutoReturn, Identity) { + EXPECT_EQ(cpp14::identity(42), 42); +} + +TEST(AutoReturn, CopyOrReference) { + std::vector data = {1, 2, 3}; + const auto& ref = cpp14::copy_or_reference(data); + EXPECT_EQ(ref, data); +} diff --git a/modules/10_cpp14/test/digit_separators_test.cpp b/modules/10_cpp14/test/digit_separators_test.cpp new file mode 100644 index 0000000..9e782f1 --- /dev/null +++ b/modules/10_cpp14/test/digit_separators_test.cpp @@ -0,0 +1,6 @@ +#include "digit_separators.hpp" +#include + +TEST(DigitSeparators, Million) { EXPECT_EQ(cpp14::million(), 1'000'000); } +TEST(DigitSeparators, Mask) { EXPECT_EQ(cpp14::mask(), 0xFF00'00FFU); } +TEST(DigitSeparators, BitsSet) { EXPECT_EQ(cpp14::bits_set(), 16); } diff --git a/modules/10_cpp14/test/generic_lambdas_test.cpp b/modules/10_cpp14/test/generic_lambdas_test.cpp new file mode 100644 index 0000000..c3c0265 --- /dev/null +++ b/modules/10_cpp14/test/generic_lambdas_test.cpp @@ -0,0 +1,10 @@ +#include "generic_lambdas.hpp" +#include + +TEST(GenericLambdas, ApplyTwice) { + EXPECT_EQ(cpp14::apply_twice({1, 2, 3}), (std::vector{2, 4, 6})); +} + +TEST(GenericLambdas, ConcatAny) { + EXPECT_EQ(cpp14::concat_any({"a"}, {"b", "c"}), (std::vector{"a", "b", "c"})); +} diff --git a/modules/10_cpp14/test/make_unique_test.cpp b/modules/10_cpp14/test/make_unique_test.cpp new file mode 100644 index 0000000..d17e5bb --- /dev/null +++ b/modules/10_cpp14/test/make_unique_test.cpp @@ -0,0 +1,16 @@ +#include "make_unique.hpp" +#include + +TEST(MakeUnique, MakeTask) { + auto task = cpp14::make_task("build", 10); + ASSERT_NE(task, nullptr); + EXPECT_EQ(task->name, "build"); + EXPECT_EQ(task->priority, 10); +} + +TEST(MakeUnique, TotalPriority) { + std::vector> tasks; + tasks.push_back(cpp14::make_task("a", 1)); + tasks.push_back(cpp14::make_task("b", 2)); + EXPECT_EQ(cpp14::total_priority(tasks), 3); +} diff --git a/modules/11_cpp17/CMakeLists.txt b/modules/11_cpp17/CMakeLists.txt new file mode 100644 index 0000000..90cabd1 --- /dev/null +++ b/modules/11_cpp17/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME structured_bindings MODULE 11_cpp17 STANDARD 17 + SOURCES src/structured_bindings.cpp test/structured_bindings_test.cpp) +add_course_exercise(NAME optional MODULE 11_cpp17 STANDARD 17 + SOURCES src/optional.cpp test/optional_test.cpp) +add_course_exercise(NAME variant MODULE 11_cpp17 STANDARD 17 + SOURCES src/variant.cpp test/variant_test.cpp) +add_course_exercise(NAME if_constexpr MODULE 11_cpp17 STANDARD 17 + SOURCES src/if_constexpr.cpp test/if_constexpr_test.cpp) diff --git a/modules/11_cpp17/README.md b/modules/11_cpp17/README.md new file mode 100644 index 0000000..fae6bf3 --- /dev/null +++ b/modules/11_cpp17/README.md @@ -0,0 +1,17 @@ +# Module 11: C++17 Features + +## Learning Goals + +- Destructure tuples and pairs with structured bindings +- Model optional values with `std::optional` +- Represent alternatives with `std::variant` +- Use `if constexpr` and fold expressions + +## Exercises + +| Exercise | Feature | +|----------|---------| +| `structured_bindings` | Structured bindings | +| `optional` | std::optional | +| `variant` | std::variant + std::visit | +| `if_constexpr` | Compile-time branching | diff --git a/modules/11_cpp17/include/if_constexpr.hpp b/modules/11_cpp17/include/if_constexpr.hpp new file mode 100644 index 0000000..5c594be --- /dev/null +++ b/modules/11_cpp17/include/if_constexpr.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +namespace cpp17 { + +template +[[nodiscard]] constexpr auto type_name() -> const char* { + if constexpr (std::is_integral_v) { + return "integral"; + } else if constexpr (std::is_floating_point_v) { + return "floating"; + } else { + return "other"; + } +} + +template +[[nodiscard]] auto element_count(const Container& container) -> std::size_t { + if constexpr (std::is_member_function_pointer_v) { + return container.size(); + } else { + std::size_t count = 0; + for ([[maybe_unused]] const auto& item : container) { + ++count; + } + return count; + } +} + +[[nodiscard]] auto stringify_ints(const std::vector& data) -> std::string; + +} // namespace cpp17 diff --git a/modules/11_cpp17/include/optional.hpp b/modules/11_cpp17/include/optional.hpp new file mode 100644 index 0000000..19492ff --- /dev/null +++ b/modules/11_cpp17/include/optional.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace cpp17 { + +[[nodiscard]] auto safe_divide(int lhs, int rhs) -> std::optional; +[[nodiscard]] auto find_user(const std::vector& users, const std::string& name) + -> std::optional; +[[nodiscard]] auto first_positive(const std::vector& data) -> std::optional; + +} // namespace cpp17 diff --git a/modules/11_cpp17/include/structured_bindings.hpp b/modules/11_cpp17/include/structured_bindings.hpp new file mode 100644 index 0000000..bf1d5dd --- /dev/null +++ b/modules/11_cpp17/include/structured_bindings.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +namespace cpp17 { + +[[nodiscard]] auto minmax_pair(const std::vector& data) -> std::pair; +[[nodiscard]] auto split_key_value(const std::string& text) -> std::pair; +[[nodiscard]] auto first_pair(const std::map& scores) + -> std::tuple; + +} // namespace cpp17 diff --git a/modules/11_cpp17/include/variant.hpp b/modules/11_cpp17/include/variant.hpp new file mode 100644 index 0000000..254b844 --- /dev/null +++ b/modules/11_cpp17/include/variant.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace cpp17 { + +using Value = std::variant; + +[[nodiscard]] auto variant_to_string(const Value& value) -> std::string; +[[nodiscard]] auto sum_numeric_variants(const std::vector& values) -> double; +[[nodiscard]] auto is_string(const Value& value) -> bool; + +} // namespace cpp17 diff --git a/modules/11_cpp17/src/if_constexpr.cpp b/modules/11_cpp17/src/if_constexpr.cpp new file mode 100644 index 0000000..71acf71 --- /dev/null +++ b/modules/11_cpp17/src/if_constexpr.cpp @@ -0,0 +1,12 @@ +#include "if_constexpr.hpp" + +#include + +namespace cpp17 { + +auto stringify_ints(const std::vector& data) -> std::string { + (void)data; + return {}; +} + +} // namespace cpp17 diff --git a/modules/11_cpp17/src/optional.cpp b/modules/11_cpp17/src/optional.cpp new file mode 100644 index 0000000..d52defb --- /dev/null +++ b/modules/11_cpp17/src/optional.cpp @@ -0,0 +1,21 @@ +#include "optional.hpp" + +namespace cpp17 { + +auto safe_divide(int lhs, int rhs) -> std::optional { + (void)lhs; (void)rhs; + return std::nullopt; +} + +auto find_user(const std::vector& users, const std::string& name) + -> std::optional { + (void)users; (void)name; + return std::nullopt; +} + +auto first_positive(const std::vector& data) -> std::optional { + (void)data; + return std::nullopt; +} + +} // namespace cpp17 diff --git a/modules/11_cpp17/src/structured_bindings.cpp b/modules/11_cpp17/src/structured_bindings.cpp new file mode 100644 index 0000000..15f3ac7 --- /dev/null +++ b/modules/11_cpp17/src/structured_bindings.cpp @@ -0,0 +1,20 @@ +#include "structured_bindings.hpp" + +namespace cpp17 { + +auto minmax_pair(const std::vector& data) -> std::pair { + (void)data; + return {0, 0}; +} + +auto split_key_value(const std::string& text) -> std::pair { + (void)text; + return {"", ""}; +} + +auto first_pair(const std::map& scores) -> std::tuple { + (void)scores; + return {"", 0, false}; +} + +} // namespace cpp17 diff --git a/modules/11_cpp17/src/variant.cpp b/modules/11_cpp17/src/variant.cpp new file mode 100644 index 0000000..a926a71 --- /dev/null +++ b/modules/11_cpp17/src/variant.cpp @@ -0,0 +1,20 @@ +#include "variant.hpp" + +namespace cpp17 { + +auto variant_to_string(const Value& value) -> std::string { + (void)value; + return {}; +} + +auto sum_numeric_variants(const std::vector& values) -> double { + (void)values; + return 0.0; +} + +auto is_string(const Value& value) -> bool { + (void)value; + return false; +} + +} // namespace cpp17 diff --git a/modules/11_cpp17/test/if_constexpr_test.cpp b/modules/11_cpp17/test/if_constexpr_test.cpp new file mode 100644 index 0000000..4df7915 --- /dev/null +++ b/modules/11_cpp17/test/if_constexpr_test.cpp @@ -0,0 +1,15 @@ +#include "if_constexpr.hpp" +#include + +TEST(IfConstexpr, TypeName) { + EXPECT_STREQ(cpp17::type_name(), "integral"); + EXPECT_STREQ(cpp17::type_name(), "floating"); +} + +TEST(IfConstexpr, ElementCount) { + EXPECT_EQ(cpp17::element_count(std::vector{1, 2, 3}), 3U); +} + +TEST(IfConstexpr, StringifyInts) { + EXPECT_EQ(cpp17::stringify_ints({1, 2, 3}), "1,2,3"); +} diff --git a/modules/11_cpp17/test/optional_test.cpp b/modules/11_cpp17/test/optional_test.cpp new file mode 100644 index 0000000..cc83b87 --- /dev/null +++ b/modules/11_cpp17/test/optional_test.cpp @@ -0,0 +1,16 @@ +#include "optional.hpp" +#include + +TEST(Optional, SafeDivide) { + EXPECT_DOUBLE_EQ(*cpp17::safe_divide(10, 2), 5.0); + EXPECT_FALSE(cpp17::safe_divide(1, 0).has_value()); +} + +TEST(Optional, FindUser) { + EXPECT_EQ(*cpp17::find_user({"Ada", "Grace"}, "Grace"), 1U); +} + +TEST(Optional, FirstPositive) { + EXPECT_EQ(*cpp17::first_positive({-1, 0, 3, 4}), 3); + EXPECT_FALSE(cpp17::first_positive({-1, -2}).has_value()); +} diff --git a/modules/11_cpp17/test/structured_bindings_test.cpp b/modules/11_cpp17/test/structured_bindings_test.cpp new file mode 100644 index 0000000..ff12b72 --- /dev/null +++ b/modules/11_cpp17/test/structured_bindings_test.cpp @@ -0,0 +1,22 @@ +#include "structured_bindings.hpp" +#include + +TEST(StructuredBindings, MinMaxPair) { + const auto [min, max] = cpp17::minmax_pair({3, 1, 9, 2}); + EXPECT_EQ(min, 1); + EXPECT_EQ(max, 9); +} + +TEST(StructuredBindings, SplitKeyValue) { + const auto [key, value] = cpp17::split_key_value("name=Ada"); + EXPECT_EQ(key, "name"); + EXPECT_EQ(value, "Ada"); +} + +TEST(StructuredBindings, FirstPair) { + const std::map scores{{"Ada", 100}, {"Grace", 95}}; + const auto [name, score, found] = cpp17::first_pair(scores); + EXPECT_TRUE(found); + EXPECT_EQ(name, "Ada"); + EXPECT_EQ(score, 100); +} diff --git a/modules/11_cpp17/test/variant_test.cpp b/modules/11_cpp17/test/variant_test.cpp new file mode 100644 index 0000000..abc802d --- /dev/null +++ b/modules/11_cpp17/test/variant_test.cpp @@ -0,0 +1,17 @@ +#include "variant.hpp" +#include + +TEST(Variant, VariantToString) { + EXPECT_EQ(cpp17::variant_to_string(42), "int:42"); + EXPECT_EQ(cpp17::variant_to_string(std::string{"hi"}), "string:hi"); +} + +TEST(Variant, SumNumericVariants) { + const std::vector values{1, 2.5, std::string{"x"}, 3.5}; + EXPECT_DOUBLE_EQ(cpp17::sum_numeric_variants(values), 7.0); +} + +TEST(Variant, IsString) { + EXPECT_TRUE(cpp17::is_string(std::string{"ok"})); + EXPECT_FALSE(cpp17::is_string(1)); +} diff --git a/modules/12_cpp20/CMakeLists.txt b/modules/12_cpp20/CMakeLists.txt new file mode 100644 index 0000000..8e09365 --- /dev/null +++ b/modules/12_cpp20/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME concepts MODULE 12_cpp20 STANDARD 20 + SOURCES src/concepts.cpp test/concepts_test.cpp) +add_course_exercise(NAME ranges MODULE 12_cpp20 STANDARD 20 + SOURCES src/ranges.cpp test/ranges_test.cpp) +add_course_exercise(NAME spaceship_operator MODULE 12_cpp20 STANDARD 20 + SOURCES src/spaceship_operator.cpp test/spaceship_operator_test.cpp) +add_course_exercise(NAME span MODULE 12_cpp20 STANDARD 20 + SOURCES src/span.cpp test/span_test.cpp) diff --git a/modules/12_cpp20/README.md b/modules/12_cpp20/README.md new file mode 100644 index 0000000..f767e99 --- /dev/null +++ b/modules/12_cpp20/README.md @@ -0,0 +1,17 @@ +# Module 12: C++20 Major Features + +## Learning Goals + +- Constrain templates with concepts +- Process sequences with the Ranges library +- Compare values with the spaceship operator `<=>` +- Pass non-owning views with `std::span` + +## Exercises + +| Exercise | Feature | +|----------|---------| +| `concepts` | Concepts and constrained templates | +| `ranges` | Views and range algorithms | +| `spaceship_operator` | `<=>` and comparison categories | +| `span` | std::span non-owning spans | diff --git a/modules/12_cpp20/include/concepts.hpp b/modules/12_cpp20/include/concepts.hpp new file mode 100644 index 0000000..0e834ee --- /dev/null +++ b/modules/12_cpp20/include/concepts.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace cpp20 { + +template +[[nodiscard]] auto double_value(T value) -> T { + (void)value; + return T{}; +} + +template +[[nodiscard]] auto clamp01(T value) -> T { + (void)value; + return T{}; +} + +template +concept StringLike = requires(T value) { + { value.size() } -> std::convertible_to; + { value.data() } -> std::convertible_to; +}; + +template +[[nodiscard]] auto uppercase_copy(const T& text) -> std::string { + (void)text; + return {}; +} + +[[nodiscard]] auto sum_integral(const std::vector& data) -> int; + +} // namespace cpp20 diff --git a/modules/12_cpp20/include/ranges.hpp b/modules/12_cpp20/include/ranges.hpp new file mode 100644 index 0000000..302830c --- /dev/null +++ b/modules/12_cpp20/include/ranges.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +namespace cpp20 { + +[[nodiscard]] auto even_numbers(const std::vector& input) -> std::vector; +[[nodiscard]] auto take_first_three(const std::vector& input) -> std::vector; +[[nodiscard]] auto join_strings(const std::vector& parts) -> std::string; + +} // namespace cpp20 diff --git a/modules/12_cpp20/include/spaceship_operator.hpp b/modules/12_cpp20/include/spaceship_operator.hpp new file mode 100644 index 0000000..a5828ae --- /dev/null +++ b/modules/12_cpp20/include/spaceship_operator.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace cpp20 { + +struct Version { + int major; + int minor; + int patch; + + [[nodiscard]] auto operator<=>(const Version& other) const = default; +}; + +[[nodiscard]] auto sort_versions(std::vector versions) -> std::vector; +[[nodiscard]] auto is_sorted_versions(const std::vector& versions) -> bool; + +} // namespace cpp20 diff --git a/modules/12_cpp20/include/span.hpp b/modules/12_cpp20/include/span.hpp new file mode 100644 index 0000000..a3f143e --- /dev/null +++ b/modules/12_cpp20/include/span.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace cpp20 { + +[[nodiscard]] auto span_sum(std::span data) -> int; +[[nodiscard]] auto first_and_last(std::span data) -> std::pair; +[[nodiscard]] auto make_subspan(std::span data, std::size_t offset, std::size_t count) + -> std::vector; + +} // namespace cpp20 diff --git a/modules/12_cpp20/src/concepts.cpp b/modules/12_cpp20/src/concepts.cpp new file mode 100644 index 0000000..6fd74f0 --- /dev/null +++ b/modules/12_cpp20/src/concepts.cpp @@ -0,0 +1,10 @@ +#include "concepts.hpp" + +namespace cpp20 { + +auto sum_integral(const std::vector& data) -> int { + (void)data; + return 0; +} + +} // namespace cpp20 diff --git a/modules/12_cpp20/src/ranges.cpp b/modules/12_cpp20/src/ranges.cpp new file mode 100644 index 0000000..55289da --- /dev/null +++ b/modules/12_cpp20/src/ranges.cpp @@ -0,0 +1,20 @@ +#include "ranges.hpp" + +namespace cpp20 { + +auto even_numbers(const std::vector& input) -> std::vector { + (void)input; + return {}; +} + +auto take_first_three(const std::vector& input) -> std::vector { + (void)input; + return {}; +} + +auto join_strings(const std::vector& parts) -> std::string { + (void)parts; + return {}; +} + +} // namespace cpp20 diff --git a/modules/12_cpp20/src/spaceship_operator.cpp b/modules/12_cpp20/src/spaceship_operator.cpp new file mode 100644 index 0000000..2ad0d1e --- /dev/null +++ b/modules/12_cpp20/src/spaceship_operator.cpp @@ -0,0 +1,15 @@ +#include "spaceship_operator.hpp" + +namespace cpp20 { + +auto sort_versions(std::vector versions) -> std::vector { + (void)versions; + return {}; +} + +auto is_sorted_versions(const std::vector& versions) -> bool { + (void)versions; + return false; +} + +} // namespace cpp20 diff --git a/modules/12_cpp20/src/span.cpp b/modules/12_cpp20/src/span.cpp new file mode 100644 index 0000000..eae3ad1 --- /dev/null +++ b/modules/12_cpp20/src/span.cpp @@ -0,0 +1,21 @@ +#include "span.hpp" + +namespace cpp20 { + +auto span_sum(std::span data) -> int { + (void)data; + return 0; +} + +auto first_and_last(std::span data) -> std::pair { + (void)data; + return {0, 0}; +} + +auto make_subspan(std::span data, std::size_t offset, std::size_t count) + -> std::vector { + (void)data; (void)offset; (void)count; + return {}; +} + +} // namespace cpp20 diff --git a/modules/12_cpp20/test/concepts_test.cpp b/modules/12_cpp20/test/concepts_test.cpp new file mode 100644 index 0000000..50d56ea --- /dev/null +++ b/modules/12_cpp20/test/concepts_test.cpp @@ -0,0 +1,19 @@ +#include "concepts.hpp" +#include + +TEST(Concepts, DoubleValue) { + EXPECT_EQ(cpp20::double_value(21), 42); +} + +TEST(Concepts, Clamp01) { + EXPECT_DOUBLE_EQ(cpp20::clamp01(1.5), 1.0); + EXPECT_DOUBLE_EQ(cpp20::clamp01(-0.5), 0.0); +} + +TEST(Concepts, UppercaseCopy) { + EXPECT_EQ(cpp20::uppercase_copy(std::string{"hello"}), "HELLO"); +} + +TEST(Concepts, SumIntegral) { + EXPECT_EQ(cpp20::sum_integral({1, 2, 3}), 6); +} diff --git a/modules/12_cpp20/test/ranges_test.cpp b/modules/12_cpp20/test/ranges_test.cpp new file mode 100644 index 0000000..1e8457d --- /dev/null +++ b/modules/12_cpp20/test/ranges_test.cpp @@ -0,0 +1,14 @@ +#include "ranges.hpp" +#include + +TEST(Ranges, EvenNumbers) { + EXPECT_EQ(cpp20::even_numbers({1, 2, 3, 4, 5}), (std::vector{2, 4})); +} + +TEST(Ranges, TakeFirstThree) { + EXPECT_EQ(cpp20::take_first_three({9, 8, 7, 6, 5}), (std::vector{9, 8, 7})); +} + +TEST(Ranges, JoinStrings) { + EXPECT_EQ(cpp20::join_strings({"C", "++", "20"}), "C++20"); +} diff --git a/modules/12_cpp20/test/spaceship_operator_test.cpp b/modules/12_cpp20/test/spaceship_operator_test.cpp new file mode 100644 index 0000000..ea0b0f4 --- /dev/null +++ b/modules/12_cpp20/test/spaceship_operator_test.cpp @@ -0,0 +1,14 @@ +#include "spaceship_operator.hpp" +#include + +TEST(SpaceshipOperator, SortVersions) { + const auto sorted = cpp20::sort_versions({{1, 10, 0}, {1, 2, 0}, {2, 0, 0}}); + ASSERT_EQ(sorted.size(), 3U); + EXPECT_EQ(sorted[0].major, 1); + EXPECT_EQ(sorted[0].minor, 2); + EXPECT_EQ(sorted[2].major, 2); +} + +TEST(SpaceshipOperator, IsSorted) { + EXPECT_TRUE(cpp20::is_sorted_versions({{1, 0, 0}, {1, 1, 0}, {2, 0, 0}})); +} diff --git a/modules/12_cpp20/test/span_test.cpp b/modules/12_cpp20/test/span_test.cpp new file mode 100644 index 0000000..5313fbf --- /dev/null +++ b/modules/12_cpp20/test/span_test.cpp @@ -0,0 +1,19 @@ +#include "span.hpp" +#include + +TEST(Span, SpanSum) { + const int data[] = {1, 2, 3, 4}; + EXPECT_EQ(cpp20::span_sum(data), 10); +} + +TEST(Span, FirstAndLast) { + const std::vector data = {10, 20, 30}; + const auto [first, last] = cpp20::first_and_last(std::span{data}); + EXPECT_EQ(first, 10); + EXPECT_EQ(last, 30); +} + +TEST(Span, MakeSubspan) { + const std::vector data = {1, 2, 3, 4, 5}; + EXPECT_EQ(cpp20::make_subspan(data, 1, 3), (std::vector{2, 3, 4})); +} diff --git a/modules/13_cpp23/CMakeLists.txt b/modules/13_cpp23/CMakeLists.txt new file mode 100644 index 0000000..58ed470 --- /dev/null +++ b/modules/13_cpp23/CMakeLists.txt @@ -0,0 +1,8 @@ +add_course_exercise(NAME expected MODULE 13_cpp23 STANDARD 23 + SOURCES src/expected.cpp test/expected_test.cpp) +add_course_exercise(NAME print_and_format MODULE 13_cpp23 STANDARD 23 + SOURCES src/print_and_format.cpp test/print_and_format_test.cpp) +add_course_exercise(NAME mdspan MODULE 13_cpp23 STANDARD 23 + SOURCES src/mdspan.cpp test/mdspan_test.cpp) +add_course_exercise(NAME ranges_zip MODULE 13_cpp23 STANDARD 23 + SOURCES src/ranges_zip.cpp test/ranges_zip_test.cpp) diff --git a/modules/13_cpp23/README.md b/modules/13_cpp23/README.md new file mode 100644 index 0000000..94d2cc2 --- /dev/null +++ b/modules/13_cpp23/README.md @@ -0,0 +1,17 @@ +# Module 13: C++23 Library Additions + +## Learning Goals + +- Handle expected success/failure with `std::expected` +- Format and print text with `` and `` +- Work with multidimensional data using `std::mdspan` +- Combine ranges with `views::zip` and `views::chunk` + +## Exercises + +| Exercise | Feature | +|----------|---------| +| `expected` | std::expected error handling | +| `print_and_format` | std::format and std::print | +| `mdspan` | Multidimensional spans | +| `ranges_zip` | zip, chunk, adjacent views | diff --git a/modules/13_cpp23/include/expected.hpp b/modules/13_cpp23/include/expected.hpp new file mode 100644 index 0000000..bdea7fe --- /dev/null +++ b/modules/13_cpp23/include/expected.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace cpp23 { + +enum class ParseError { EmptyInput, InvalidNumber }; + +[[nodiscard]] auto parse_int(const std::string& text) -> std::expected; +[[nodiscard]] auto safe_sqrt(int value) -> std::expected; +[[nodiscard]] auto chain_parse_and_double(const std::string& text) -> std::expected; + +} // namespace cpp23 diff --git a/modules/13_cpp23/include/mdspan.hpp b/modules/13_cpp23/include/mdspan.hpp new file mode 100644 index 0000000..53c3090 --- /dev/null +++ b/modules/13_cpp23/include/mdspan.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace cpp23 { + +[[nodiscard]] auto make_row_major_matrix(int rows, int cols, int fill) -> std::vector; +[[nodiscard]] auto matrix_element(std::mdspan> matrix, + int row, int col) -> int; +[[nodiscard]] auto row_sums(std::mdspan> matrix) + -> std::vector; + +} // namespace cpp23 diff --git a/modules/13_cpp23/include/print_and_format.hpp b/modules/13_cpp23/include/print_and_format.hpp new file mode 100644 index 0000000..874365a --- /dev/null +++ b/modules/13_cpp23/include/print_and_format.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace cpp23 { + +[[nodiscard]] auto format_greeting(const std::string& name, int score) -> std::string; +[[nodiscard]] auto format_table_row(const std::vector& columns) -> std::string; +[[nodiscard]] auto format_hex(std::uint32_t value) -> std::string; + +} // namespace cpp23 diff --git a/modules/13_cpp23/include/ranges_zip.hpp b/modules/13_cpp23/include/ranges_zip.hpp new file mode 100644 index 0000000..7857cf4 --- /dev/null +++ b/modules/13_cpp23/include/ranges_zip.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace cpp23 { + +[[nodiscard]] auto zip_sum(const std::vector& a, const std::vector& b) -> std::vector; +[[nodiscard]] auto chunk_strings(const std::vector& words, std::size_t chunk_size) + -> std::vector>; +[[nodiscard]] auto adjacent_differences(const std::vector& data) -> std::vector; + +} // namespace cpp23 diff --git a/modules/13_cpp23/src/expected.cpp b/modules/13_cpp23/src/expected.cpp new file mode 100644 index 0000000..5e54f85 --- /dev/null +++ b/modules/13_cpp23/src/expected.cpp @@ -0,0 +1,20 @@ +#include "expected.hpp" + +namespace cpp23 { + +auto parse_int(const std::string& text) -> std::expected { + (void)text; + return std::unexpected(ParseError::EmptyInput); +} + +auto safe_sqrt(int value) -> std::expected { + (void)value; + return std::unexpected(std::string{"negative"}); +} + +auto chain_parse_and_double(const std::string& text) -> std::expected { + (void)text; + return std::unexpected(ParseError::InvalidNumber); +} + +} // namespace cpp23 diff --git a/modules/13_cpp23/src/mdspan.cpp b/modules/13_cpp23/src/mdspan.cpp new file mode 100644 index 0000000..a236620 --- /dev/null +++ b/modules/13_cpp23/src/mdspan.cpp @@ -0,0 +1,24 @@ +#include "mdspan.hpp" + +namespace cpp23 { + +auto make_row_major_matrix(int rows, int cols, int fill) -> std::vector { + (void)rows; (void)cols; (void)fill; + return {}; +} + +auto matrix_element( + std::mdspan> matrix, + int row, int col) -> int { + (void)matrix; (void)row; (void)col; + return 0; +} + +auto row_sums( + std::mdspan> matrix) + -> std::vector { + (void)matrix; + return {}; +} + +} // namespace cpp23 diff --git a/modules/13_cpp23/src/print_and_format.cpp b/modules/13_cpp23/src/print_and_format.cpp new file mode 100644 index 0000000..0b915c4 --- /dev/null +++ b/modules/13_cpp23/src/print_and_format.cpp @@ -0,0 +1,20 @@ +#include "print_and_format.hpp" + +namespace cpp23 { + +auto format_greeting(const std::string& name, int score) -> std::string { + (void)name; (void)score; + return {}; +} + +auto format_table_row(const std::vector& columns) -> std::string { + (void)columns; + return {}; +} + +auto format_hex(std::uint32_t value) -> std::string { + (void)value; + return {}; +} + +} // namespace cpp23 diff --git a/modules/13_cpp23/src/ranges_zip.cpp b/modules/13_cpp23/src/ranges_zip.cpp new file mode 100644 index 0000000..45b59e3 --- /dev/null +++ b/modules/13_cpp23/src/ranges_zip.cpp @@ -0,0 +1,21 @@ +#include "ranges_zip.hpp" + +namespace cpp23 { + +auto zip_sum(const std::vector& a, const std::vector& b) -> std::vector { + (void)a; (void)b; + return {}; +} + +auto chunk_strings(const std::vector& words, std::size_t chunk_size) + -> std::vector> { + (void)words; (void)chunk_size; + return {}; +} + +auto adjacent_differences(const std::vector& data) -> std::vector { + (void)data; + return {}; +} + +} // namespace cpp23 diff --git a/modules/13_cpp23/test/expected_test.cpp b/modules/13_cpp23/test/expected_test.cpp new file mode 100644 index 0000000..956dbe6 --- /dev/null +++ b/modules/13_cpp23/test/expected_test.cpp @@ -0,0 +1,16 @@ +#include "expected.hpp" +#include + +TEST(Expected, ParseInt) { + EXPECT_EQ(*cpp23::parse_int("42"), 42); + EXPECT_EQ(cpp23::parse_int(""), std::unexpected(cpp23::ParseError::EmptyInput)); +} + +TEST(Expected, SafeSqrt) { + EXPECT_DOUBLE_EQ(*cpp23::safe_sqrt(9), 3.0); + EXPECT_FALSE(cpp23::safe_sqrt(-1).has_value()); +} + +TEST(Expected, ChainParseAndDouble) { + EXPECT_EQ(*cpp23::chain_parse_and_double("21"), 42); +} diff --git a/modules/13_cpp23/test/mdspan_test.cpp b/modules/13_cpp23/test/mdspan_test.cpp new file mode 100644 index 0000000..50ef8c5 --- /dev/null +++ b/modules/13_cpp23/test/mdspan_test.cpp @@ -0,0 +1,16 @@ +#include "mdspan.hpp" +#include + +TEST(Mdspan, MatrixElement) { + auto storage = cpp23::make_row_major_matrix(2, 3, 1); + const auto matrix = std::mdspan>( + storage.data(), 2, 3); + EXPECT_EQ(cpp23::matrix_element(matrix, 1, 2), 1); +} + +TEST(Mdspan, RowSums) { + std::vector storage = {1, 2, 3, 4, 5, 6}; + const auto matrix = std::mdspan>( + storage.data(), 2, 3); + EXPECT_EQ(cpp23::row_sums(matrix), (std::vector{6, 15})); +} diff --git a/modules/13_cpp23/test/print_and_format_test.cpp b/modules/13_cpp23/test/print_and_format_test.cpp new file mode 100644 index 0000000..f764cbc --- /dev/null +++ b/modules/13_cpp23/test/print_and_format_test.cpp @@ -0,0 +1,14 @@ +#include "print_and_format.hpp" +#include + +TEST(PrintAndFormat, FormatGreeting) { + EXPECT_EQ(cpp23::format_greeting("Ada", 100), "Ada scored 100 points"); +} + +TEST(PrintAndFormat, FormatTableRow) { + EXPECT_EQ(cpp23::format_table_row({"A", "B", "C"}), "A | B | C"); +} + +TEST(PrintAndFormat, FormatHex) { + EXPECT_EQ(cpp23::format_hex(255), "0xFF"); +} diff --git a/modules/13_cpp23/test/ranges_zip_test.cpp b/modules/13_cpp23/test/ranges_zip_test.cpp new file mode 100644 index 0000000..beaf322 --- /dev/null +++ b/modules/13_cpp23/test/ranges_zip_test.cpp @@ -0,0 +1,15 @@ +#include "ranges_zip.hpp" +#include + +TEST(RangesZip, ZipSum) { + EXPECT_EQ(cpp23::zip_sum({1, 2, 3}, {4, 5, 6}), (std::vector{5, 7, 9})); +} + +TEST(RangesZip, ChunkStrings) { + EXPECT_EQ(cpp23::chunk_strings({"a", "b", "c", "d"}, 2), + (std::vector>{{"a", "b"}, {"c", "d"}})); +} + +TEST(RangesZip, AdjacentDifferences) { + EXPECT_EQ(cpp23::adjacent_differences({1, 4, 9, 16}), (std::vector{3, 5, 7})); +} diff --git a/modules/14_cpp26/CMakeLists.txt b/modules/14_cpp26/CMakeLists.txt new file mode 100644 index 0000000..dd2f935 --- /dev/null +++ b/modules/14_cpp26/CMakeLists.txt @@ -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) diff --git a/modules/14_cpp26/README.md b/modules/14_cpp26/README.md new file mode 100644 index 0000000..1be9f9c --- /dev/null +++ b/modules/14_cpp26/README.md @@ -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 | diff --git a/modules/14_cpp26/include/constexpr_frontier.hpp b/modules/14_cpp26/include/constexpr_frontier.hpp new file mode 100644 index 0000000..7b33ebb --- /dev/null +++ b/modules/14_cpp26/include/constexpr_frontier.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace cpp26 { + +[[nodiscard]] auto compile_time_factorial(int n) -> long long; +[[nodiscard]] auto compile_time_sum(std::array values) -> int; +[[nodiscard]] auto is_sorted(std::array values) -> bool; + +} // namespace cpp26 diff --git a/modules/14_cpp26/include/custom_formatter.hpp b/modules/14_cpp26/include/custom_formatter.hpp new file mode 100644 index 0000000..b6ec99f --- /dev/null +++ b/modules/14_cpp26/include/custom_formatter.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +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 : std::formatter { + auto format(const cpp26::Point& point, std::format_context& ctx) const { + return std::formatter::format( + std::format("({}, {})", point.x, point.y), ctx); + } +}; diff --git a/modules/14_cpp26/include/monadic_expected.hpp b/modules/14_cpp26/include/monadic_expected.hpp new file mode 100644 index 0000000..fb7e5ef --- /dev/null +++ b/modules/14_cpp26/include/monadic_expected.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace cpp26 { + +enum class ErrorCode { NotFound, Invalid }; + +[[nodiscard]] auto parse_id(const std::string& text) -> std::expected; +[[nodiscard]] auto load_user(int id) -> std::expected; +[[nodiscard]] auto load_user_name(const std::string& text) -> std::expected; + +} // namespace cpp26 diff --git a/modules/14_cpp26/include/pipeline_ranges.hpp b/modules/14_cpp26/include/pipeline_ranges.hpp new file mode 100644 index 0000000..fe29cbf --- /dev/null +++ b/modules/14_cpp26/include/pipeline_ranges.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace cpp26 { + +struct Record { + std::string category; + int value; +}; + +[[nodiscard]] auto top_values_by_category(const std::vector& records, int min_value) + -> std::vector; +[[nodiscard]] auto total_by_category(const std::vector& records) + -> std::vector>; + +} // namespace cpp26 diff --git a/modules/14_cpp26/src/constexpr_frontier.cpp b/modules/14_cpp26/src/constexpr_frontier.cpp new file mode 100644 index 0000000..d50c521 --- /dev/null +++ b/modules/14_cpp26/src/constexpr_frontier.cpp @@ -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 values) -> int { + (void)values; + return 0; +} + +auto is_sorted(std::array values) -> bool { + (void)values; + return false; +} + +} // namespace cpp26 diff --git a/modules/14_cpp26/src/custom_formatter.cpp b/modules/14_cpp26/src/custom_formatter.cpp new file mode 100644 index 0000000..54cbc4f --- /dev/null +++ b/modules/14_cpp26/src/custom_formatter.cpp @@ -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 diff --git a/modules/14_cpp26/src/monadic_expected.cpp b/modules/14_cpp26/src/monadic_expected.cpp new file mode 100644 index 0000000..5e56eae --- /dev/null +++ b/modules/14_cpp26/src/monadic_expected.cpp @@ -0,0 +1,20 @@ +#include "monadic_expected.hpp" + +namespace cpp26 { + +auto parse_id(const std::string& text) -> std::expected { + (void)text; + return std::unexpected(ErrorCode::Invalid); +} + +auto load_user(int id) -> std::expected { + (void)id; + return std::unexpected(ErrorCode::NotFound); +} + +auto load_user_name(const std::string& text) -> std::expected { + (void)text; + return std::unexpected(ErrorCode::Invalid); +} + +} // namespace cpp26 diff --git a/modules/14_cpp26/src/pipeline_ranges.cpp b/modules/14_cpp26/src/pipeline_ranges.cpp new file mode 100644 index 0000000..2fb3686 --- /dev/null +++ b/modules/14_cpp26/src/pipeline_ranges.cpp @@ -0,0 +1,17 @@ +#include "pipeline_ranges.hpp" + +namespace cpp26 { + +auto top_values_by_category(const std::vector& records, int min_value) + -> std::vector { + (void)records; (void)min_value; + return {}; +} + +auto total_by_category(const std::vector& records) + -> std::vector> { + (void)records; + return {}; +} + +} // namespace cpp26 diff --git a/modules/14_cpp26/test/constexpr_frontier_test.cpp b/modules/14_cpp26/test/constexpr_frontier_test.cpp new file mode 100644 index 0000000..4b5779e --- /dev/null +++ b/modules/14_cpp26/test/constexpr_frontier_test.cpp @@ -0,0 +1,15 @@ +#include "constexpr_frontier.hpp" +#include + +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})); +} diff --git a/modules/14_cpp26/test/custom_formatter_test.cpp b/modules/14_cpp26/test/custom_formatter_test.cpp new file mode 100644 index 0000000..af9bf31 --- /dev/null +++ b/modules/14_cpp26/test/custom_formatter_test.cpp @@ -0,0 +1,10 @@ +#include "custom_formatter.hpp" +#include + +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)"); +} diff --git a/modules/14_cpp26/test/monadic_expected_test.cpp b/modules/14_cpp26/test/monadic_expected_test.cpp new file mode 100644 index 0000000..47f7486 --- /dev/null +++ b/modules/14_cpp26/test/monadic_expected_test.cpp @@ -0,0 +1,8 @@ +#include "monadic_expected.hpp" +#include + +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)); +} diff --git a/modules/14_cpp26/test/pipeline_ranges_test.cpp b/modules/14_cpp26/test/pipeline_ranges_test.cpp new file mode 100644 index 0000000..4437df9 --- /dev/null +++ b/modules/14_cpp26/test/pipeline_ranges_test.cpp @@ -0,0 +1,19 @@ +#include "pipeline_ranges.hpp" +#include + +TEST(PipelineRanges, TopValuesByCategory) { + const std::vector 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 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); +} diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 0000000..a2e13d5 --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,14 @@ +add_subdirectory(01_fundamentals) +add_subdirectory(02_control_flow) +add_subdirectory(03_functions) +add_subdirectory(04_arrays_and_strings) +add_subdirectory(05_pointers_and_references) +add_subdirectory(06_oop_basics) +add_subdirectory(07_stl_containers) +add_subdirectory(08_stl_algorithms) +add_subdirectory(09_cpp11) +add_subdirectory(10_cpp14) +add_subdirectory(11_cpp17) +add_subdirectory(12_cpp20) +add_subdirectory(13_cpp23) +add_subdirectory(14_cpp26)