Initial Course
This commit is contained in:
+26
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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!
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fundamentals {
|
||||||
|
|
||||||
|
[[nodiscard]] auto join_words(const std::vector<std::string>& words, char separator) -> std::string;
|
||||||
|
[[nodiscard]] auto parse_integers(const std::string& csv) -> std::vector<int>;
|
||||||
|
[[nodiscard]] auto format_score(const std::string& player, int score) -> std::string;
|
||||||
|
|
||||||
|
} // namespace fundamentals
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#include "basic_io.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace fundamentals {
|
||||||
|
|
||||||
|
auto join_words(const std::vector<std::string>& 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<int> {
|
||||||
|
// 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 "<player> scored <score> points".
|
||||||
|
(void)player;
|
||||||
|
(void)score;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fundamentals
|
||||||
@@ -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, <name>!"
|
||||||
|
(void)name;
|
||||||
|
return "Hello, World!";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fundamentals
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "basic_io.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<int>{1, 2, 3}));
|
||||||
|
EXPECT_EQ(fundamentals::parse_integers("42"), (std::vector<int>{42}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BasicIo, FormatScore) {
|
||||||
|
EXPECT_EQ(fundamentals::format_score("Alice", 150), "Alice scored 150 points");
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include "hello_world.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#include "operators.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#include "variables_and_types.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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 |
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace control_flow {
|
||||||
|
|
||||||
|
[[nodiscard]] auto sum_positive(const std::vector<int>& data) -> int;
|
||||||
|
[[nodiscard]] auto first_multiple_of(const std::vector<int>& data, int divisor) -> int;
|
||||||
|
[[nodiscard]] auto collect_until_negative(const std::vector<int>& data) -> std::vector<int>;
|
||||||
|
|
||||||
|
} // namespace control_flow
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<int>& data, int target) -> int;
|
||||||
|
[[nodiscard]] auto first_index_of(const std::vector<int>& data, int target) -> int;
|
||||||
|
|
||||||
|
} // namespace control_flow
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include "break_continue.hpp"
|
||||||
|
|
||||||
|
namespace control_flow {
|
||||||
|
|
||||||
|
auto sum_positive(const std::vector<int>& data) -> int {
|
||||||
|
(void)data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto first_multiple_of(const std::vector<int>& data, int divisor) -> int {
|
||||||
|
(void)data; (void)divisor;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto collect_until_negative(const std::vector<int>& data) -> std::vector<int> {
|
||||||
|
(void)data;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace control_flow
|
||||||
@@ -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
|
||||||
@@ -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<int>& data, int target) -> int {
|
||||||
|
(void)data; (void)target;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto first_index_of(const std::vector<int>& data, int target) -> int {
|
||||||
|
// TODO: Return index or -1 if not found
|
||||||
|
(void)data; (void)target;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace control_flow
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include "break_continue.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<int>{1, 2, 3}));
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "conditionals.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "loops.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "switch_statements.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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 |
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace functions {
|
||||||
|
|
||||||
|
[[nodiscard]] auto square(int value) -> int;
|
||||||
|
void swap_integers(int& lhs, int& rhs);
|
||||||
|
[[nodiscard]] auto concatenate(const std::vector<std::string>& parts) -> std::string;
|
||||||
|
void append_suffix(std::string& text, const std::string& suffix);
|
||||||
|
|
||||||
|
} // namespace functions
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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<std::string>& parts) -> std::string { (void)parts; return {}; }
|
||||||
|
void append_suffix(std::string& text, const std::string& suffix) { (void)text; (void)suffix; }
|
||||||
|
|
||||||
|
} // namespace functions
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "default_parameters.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "function_basics.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include "overloading.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#include "recursion.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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 |
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace arrays {
|
||||||
|
|
||||||
|
using Matrix = std::vector<std::vector<int>>;
|
||||||
|
|
||||||
|
[[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<int>;
|
||||||
|
|
||||||
|
} // namespace arrays
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace arrays {
|
||||||
|
|
||||||
|
[[nodiscard]] auto vector_sum(const std::vector<int>& data) -> int;
|
||||||
|
void remove_value(std::vector<int>& data, int value);
|
||||||
|
[[nodiscard]] auto unique_sorted(std::vector<int> data) -> std::vector<int>;
|
||||||
|
[[nodiscard]] auto chunk(const std::vector<int>& data, std::size_t size) -> std::vector<std::vector<int>>;
|
||||||
|
|
||||||
|
} // namespace arrays
|
||||||
@@ -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
|
||||||
@@ -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<int> { (void)matrix; return {}; }
|
||||||
|
|
||||||
|
} // namespace arrays
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include "std_vector.hpp"
|
||||||
|
|
||||||
|
namespace arrays {
|
||||||
|
|
||||||
|
auto vector_sum(const std::vector<int>& data) -> int { (void)data; return 0; }
|
||||||
|
void remove_value(std::vector<int>& data, int value) { (void)data; (void)value; }
|
||||||
|
auto unique_sorted(std::vector<int> data) -> std::vector<int> { (void)data; return {}; }
|
||||||
|
auto chunk(const std::vector<int>& data, std::size_t size) -> std::vector<std::vector<int>> { (void)data; (void)size; return {}; }
|
||||||
|
|
||||||
|
} // namespace arrays
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "c_arrays.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include "multidimensional.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<int>{6, 15}));
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "std_string.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "std_vector.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(StdVector, Sum) {
|
||||||
|
EXPECT_EQ(arrays::vector_sum({1, 2, 3}), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdVector, RemoveValue) {
|
||||||
|
std::vector<int> data = {1, 2, 2, 3};
|
||||||
|
arrays::remove_value(data, 2);
|
||||||
|
EXPECT_EQ(data, (std::vector<int>{1, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdVector, UniqueSorted) {
|
||||||
|
EXPECT_EQ(arrays::unique_sorted({3, 1, 2, 2, 3}), (std::vector<int>{1, 2, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdVector, Chunk) {
|
||||||
|
EXPECT_EQ(arrays::chunk({1, 2, 3, 4, 5}, 2),
|
||||||
|
(std::vector<std::vector<int>>{{1, 2}, {3, 4}, {5}}));
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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 |
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "dynamic_memory.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "pointer_arithmetic.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "pointer_basics.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "references.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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 |
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<char> buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace oop
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "inheritance.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace oop {
|
||||||
|
|
||||||
|
[[nodiscard]] auto total_area(const std::vector<std::unique_ptr<Shape>>& shapes) -> double;
|
||||||
|
[[nodiscard]] auto shape_names(const std::vector<std::unique_ptr<Shape>>& shapes)
|
||||||
|
-> std::vector<std::string>;
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "polymorphism.hpp"
|
||||||
|
|
||||||
|
namespace oop {
|
||||||
|
|
||||||
|
auto total_area(const std::vector<std::unique_ptr<Shape>>& 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<std::unique_ptr<Shape>>& shapes)
|
||||||
|
-> std::vector<std::string> {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
for (const auto& shape : shapes) {
|
||||||
|
// TODO: Collect shape->name()
|
||||||
|
(void)shape;
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Counter::copies() -> int { return copies_; }
|
||||||
|
|
||||||
|
} // namespace oop
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "classes_and_objects.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "constructors.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include "inheritance.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "polymorphism.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Polymorphism, TotalArea) {
|
||||||
|
std::vector<std::unique_ptr<oop::Shape>> shapes;
|
||||||
|
shapes.push_back(std::make_unique<oop::Rectangle>(2.0, 3.0));
|
||||||
|
shapes.push_back(std::make_unique<oop::Circle>(1.0));
|
||||||
|
EXPECT_NEAR(oop::total_area(shapes), 6.0 + 3.141592653589793, 1e-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Polymorphism, ShapeNames) {
|
||||||
|
std::vector<std::unique_ptr<oop::Shape>> shapes;
|
||||||
|
shapes.push_back(std::make_unique<oop::Circle>(1.0));
|
||||||
|
shapes.push_back(std::make_unique<oop::Rectangle>(1.0, 2.0));
|
||||||
|
EXPECT_EQ(oop::shape_names(shapes), (std::vector<std::string>{"Circle", "Rectangle"}));
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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 |
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
[[nodiscard]] auto word_frequencies(const std::vector<std::string>& words)
|
||||||
|
-> std::map<std::string, int>;
|
||||||
|
[[nodiscard]] auto unique_sorted(const std::vector<int>& data) -> std::set<int>;
|
||||||
|
[[nodiscard]] auto invert_map(const std::map<int, std::string>& input)
|
||||||
|
-> std::map<std::string, int>;
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
[[nodiscard]] auto is_balanced_parentheses(const std::string& text) -> bool;
|
||||||
|
[[nodiscard]] auto simulate_queue(const std::vector<int>& arrivals) -> std::vector<int>;
|
||||||
|
[[nodiscard]] auto top_k_largest(const std::vector<int>& data, int k) -> std::vector<int>;
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
[[nodiscard]] auto merge_sorted_vectors(const std::vector<int>& a, const std::vector<int>& b)
|
||||||
|
-> std::vector<int>;
|
||||||
|
[[nodiscard]] auto list_to_vector(const std::list<int>& data) -> std::vector<int>;
|
||||||
|
[[nodiscard]] auto rotate_deque(std::deque<int> data, int steps) -> std::deque<int>;
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
[[nodiscard]] auto first_unique(const std::vector<std::string>& words) -> std::string;
|
||||||
|
[[nodiscard]] auto group_anagrams(const std::vector<std::string>& words)
|
||||||
|
-> std::unordered_map<std::string, std::vector<std::string>>;
|
||||||
|
[[nodiscard]] auto has_duplicate(const std::vector<int>& data) -> bool;
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "associative_containers.hpp"
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
auto word_frequencies(const std::vector<std::string>& words)
|
||||||
|
-> std::map<std::string, int> {
|
||||||
|
(void)words;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto unique_sorted(const std::vector<int>& data) -> std::set<int> {
|
||||||
|
(void)data;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto invert_map(const std::map<int, std::string>& input) -> std::map<std::string, int> {
|
||||||
|
(void)input;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -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<int>& arrivals) -> std::vector<int> {
|
||||||
|
(void)arrivals;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto top_k_largest(const std::vector<int>& data, int k) -> std::vector<int> {
|
||||||
|
(void)data; (void)k;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "sequential_containers.hpp"
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
auto merge_sorted_vectors(const std::vector<int>& a, const std::vector<int>& b)
|
||||||
|
-> std::vector<int> {
|
||||||
|
(void)a; (void)b;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto list_to_vector(const std::list<int>& data) -> std::vector<int> {
|
||||||
|
(void)data;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rotate_deque(std::deque<int> data, int steps) -> std::deque<int> {
|
||||||
|
(void)data; (void)steps;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "unordered_containers.hpp"
|
||||||
|
|
||||||
|
namespace stl_containers {
|
||||||
|
|
||||||
|
auto first_unique(const std::vector<std::string>& words) -> std::string {
|
||||||
|
(void)words;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto group_anagrams(const std::vector<std::string>& words)
|
||||||
|
-> std::unordered_map<std::string, std::vector<std::string>> {
|
||||||
|
(void)words;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto has_duplicate(const std::vector<int>& data) -> bool {
|
||||||
|
(void)data;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stl_containers
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "associative_containers.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<int>{1, 2, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AssociativeContainers, InvertMap) {
|
||||||
|
const std::map<int, std::string> input{{1, "one"}, {2, "two"}};
|
||||||
|
const auto inverted = stl_containers::invert_map(input);
|
||||||
|
EXPECT_EQ(inverted.at("one"), 1);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user