Compare commits
72
Commits
initial-course
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dcb4c85c5 | ||
|
|
64b29cfebe | ||
|
|
45bcd09bc1 | ||
|
|
986be65d61 | ||
|
|
e907849c57 | ||
|
|
dea138ca04 | ||
|
|
1e538bdd4b | ||
|
|
602e5d757b | ||
|
|
519e90e66f | ||
|
|
758b9fdaa8 | ||
|
|
e25e965489 | ||
|
|
57693c5de2 | ||
|
|
8fa5630858 | ||
|
|
29df33a4e9 | ||
|
|
b4a46bece7 | ||
|
|
75221d0124 | ||
|
|
9d2ba83869 | ||
|
|
40387c9218 | ||
|
|
9928855c1e | ||
|
|
68fc104f31 | ||
|
|
d05a9f44fa | ||
|
|
a72d03277e | ||
|
|
0a0c430ebe | ||
|
|
d26d32a245 | ||
|
|
2ec26ecd04 | ||
|
|
8807a9228c | ||
|
|
3131313253 | ||
|
|
8319d391be | ||
|
|
5867900588 | ||
|
|
5fa04b30c7 | ||
|
|
7a8e430982 | ||
|
|
5f4bbc0918 | ||
|
|
4314f9126a | ||
|
|
00a5178b01 | ||
|
|
362ef757b3 | ||
|
|
dd981b5968 | ||
|
|
7a44e8fbc2 | ||
|
|
a33ef36d8e | ||
|
|
61d91878b9 | ||
|
|
564cc1b765 | ||
|
|
8b82257ed6 | ||
|
|
9676acc893 | ||
|
|
d9e08afbcb | ||
|
|
7ddd9f1bc3 | ||
|
|
ec1c1f1ce7 | ||
|
|
8688803952 | ||
|
|
f2495e1fb5 | ||
|
|
1aacfbbd1a | ||
|
|
925d766bcf | ||
|
|
97e72b545f | ||
|
|
e6bdb3bdd8 | ||
|
|
11af4914ad | ||
|
|
bee8344fdf | ||
|
|
3c4777e840 | ||
|
|
d1fbb1edbd | ||
|
|
626b01cf8a | ||
|
|
88cc7728a6 | ||
|
|
ba6d3ea5d4 | ||
|
|
ca42c2c9d4 | ||
|
|
5c28159a9f | ||
|
|
28a315195e | ||
|
|
41111dba30 | ||
|
|
206742e9b3 | ||
|
|
583f8d73db | ||
|
|
ea0e601850 | ||
|
|
3cce26e72d | ||
|
|
bd85800a30 | ||
|
|
d9c7987269 | ||
|
|
2d25dd503a | ||
|
|
35afea97c0 | ||
|
|
6af86468d0 | ||
|
|
8de2e83c0b |
@@ -1,5 +1,6 @@
|
||||
# Build directories
|
||||
build/
|
||||
build-gcc26/
|
||||
cmake-build-*/
|
||||
out/
|
||||
|
||||
|
||||
@@ -12,14 +12,33 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"GCC 16+ is required (found ${CMAKE_CXX_COMPILER_VERSION}). "
|
||||
"Use the 'default' CMake preset or cmake/toolchains/gcc-16.cmake."
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"This project requires GCC 16+. Found compiler: ${CMAKE_CXX_COMPILER_ID}. "
|
||||
"Select the GCC 16 toolchain / default CMake preset in CLion."
|
||||
)
|
||||
endif()
|
||||
|
||||
option(COURSE_BUILD_TESTS "Build Google Test exercise targets" ON)
|
||||
option(COURSE_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
|
||||
option(COURSE_ENABLE_CONTRACTS "Enable C++26 contracts on GCC 16+" ON)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include(CourseOptions)
|
||||
include(CourseExercise)
|
||||
|
||||
add_subdirectory(support)
|
||||
|
||||
if(COURSE_BUILD_TESTS)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
@@ -34,3 +53,6 @@ if(COURSE_BUILD_TESTS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(modules)
|
||||
|
||||
include(Cppcheck)
|
||||
course_add_cppcheck_targets()
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": 6,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 20,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "GCC 16 + C++26",
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/cmake-build-debug-gcc-16-cpp26",
|
||||
"toolchainFile": "${sourceDir}/cmake/toolchains/gcc-16.cmake",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"COURSE_ENABLE_CONTRACTS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"displayName": "GCC 16 + C++26 (Release)",
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/cmake-build-release-gcc-16-cpp26",
|
||||
"toolchainFile": "${sourceDir}/cmake/toolchains/gcc-16.cmake",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"COURSE_ENABLE_CONTRACTS": "ON"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default"
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"configurePreset": "release"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,32 +5,52 @@ A hands-on C++ course with **56 Google Test exercises** organized into **14 modu
|
||||
## Requirements
|
||||
|
||||
- **CMake** 3.20+
|
||||
- **C++ compiler** with C++23 support for later modules
|
||||
- Apple Clang 15+, GCC 13+, or MSVC 19.34+ recommended
|
||||
- **GCC 16+** with C++26 and contracts support
|
||||
- **CLion** (recommended) or any CMake-aware IDE
|
||||
- Internet access on first configure (Google Test is fetched automatically)
|
||||
|
||||
Install GCC 16 on macOS (Homebrew):
|
||||
|
||||
```bash
|
||||
brew install gcc@16
|
||||
```
|
||||
|
||||
## 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.
|
||||
1. Open this folder in CLion.
|
||||
2. **Settings → Build, Execution, Deployment → CMake**
|
||||
3. Enable profile **`default`** (display name **GCC 16 + C++26** from `CMakePresets.json`).
|
||||
4. Disable old profiles that use Apple Clang (e.g. custom profiles without the preset toolchain).
|
||||
5. Click **Reload CMake Project**.
|
||||
|
||||
If configure fails with *"This project requires GCC 16+"*, CLion is still using Apple Clang. Enable the **`default`** preset or set the toolchain C++ compiler to `g++-16`.
|
||||
|
||||
If configure fails with *"generator does not match"*, delete the build folder (`cmake-build-debug-gcc-16-cpp26`) and reload CMake.
|
||||
|
||||
6. Pick an exercise target (e.g. `02_control_flow_loops`) and run tests.
|
||||
|
||||
## Quick Start (Terminal)
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake --build build
|
||||
ctest --test-dir build --output-on-failure
|
||||
cmake --preset default
|
||||
cmake --build cmake-build-debug-gcc-16-cpp26
|
||||
ctest --test-dir cmake-build-debug-gcc-16-cpp26 --output-on-failure
|
||||
```
|
||||
|
||||
Run a single exercise:
|
||||
|
||||
```bash
|
||||
cmake --build build --target 01_fundamentals_hello_world
|
||||
./build/modules/01_fundamentals/01_fundamentals_hello_world
|
||||
cmake --build cmake-build-debug-gcc-16-cpp26 --target 02_control_flow_loops
|
||||
./cmake-build-debug-gcc-16-cpp26/modules/02_control_flow/02_control_flow_loops
|
||||
```
|
||||
|
||||
Manual configure (without presets):
|
||||
|
||||
```bash
|
||||
cmake -S . -B cmake-build-debug-gcc-16-cpp26 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/gcc-16.cmake \
|
||||
-DCOURSE_ENABLE_CONTRACTS=ON
|
||||
cmake --build cmake-build-debug-gcc-16-cpp26
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
@@ -38,30 +58,22 @@ cmake --build build --target 01_fundamentals_hello_world
|
||||
```
|
||||
cppc/
|
||||
├── CMakeLists.txt # Root project + Google Test
|
||||
├── CMakePresets.json # GCC 16 + C++26 preset
|
||||
├── cmake/
|
||||
│ ├── CourseExercise.cmake # add_course_exercise() helper
|
||||
│ └── CourseOptions.cmake # Warnings and standards
|
||||
│ ├── CourseOptions.cmake # Warnings, standards, contracts
|
||||
│ └── toolchains/gcc-16.cmake # GCC 16 toolchain file
|
||||
├── support/ # Contract violation handler
|
||||
└── 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
|
||||
├── 01_fundamentals/
|
||||
├── 02_control_flow/
|
||||
└── ... # 14 modules total
|
||||
```
|
||||
|
||||
Each module contains:
|
||||
|
||||
- `README.md` — learning goals and exercise list
|
||||
- `include/` — function and class declarations (your contract)
|
||||
- `include/` — function and class declarations
|
||||
- `src/` — **your implementation** (start here)
|
||||
- `test/` — Google Test files (do not edit unless extending)
|
||||
|
||||
@@ -75,22 +87,22 @@ Each module contains:
|
||||
|
||||
## 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+ |
|
||||
| Module | Topic | Exercises | Build standard |
|
||||
|--------|-------|-----------|----------------|
|
||||
| 01 | Fundamentals | 4 | C++26 |
|
||||
| 02 | Control flow | 4 | C++26 |
|
||||
| 03 | Functions | 4 | C++26 |
|
||||
| 04 | Arrays & strings | 4 | C++26 |
|
||||
| 05 | Pointers & references | 4 | C++26 |
|
||||
| 06 | OOP basics | 4 | C++26 |
|
||||
| 07 | STL containers | 4 | C++26 |
|
||||
| 08 | STL algorithms | 4 | C++26 |
|
||||
| 09 | C++11 features | 4 | C++26 |
|
||||
| 10 | C++14 features | 4 | C++26 |
|
||||
| 11 | C++17 features | 4 | C++26 |
|
||||
| 12 | C++20 features | 4 | C++26 |
|
||||
| 13 | C++23 features | 4 | C++26 |
|
||||
| 14 | C++26 frontier | 4 | C++26 |
|
||||
|
||||
## CMake Options
|
||||
|
||||
@@ -98,22 +110,81 @@ Each module contains:
|
||||
|--------|---------|-------------|
|
||||
| `COURSE_BUILD_TESTS` | ON | Build Google Test exercise targets |
|
||||
| `COURSE_WARNINGS_AS_ERRORS` | OFF | Treat warnings as errors |
|
||||
| `COURSE_ENABLE_CONTRACTS` | ON | Enable `-fcontracts` on GCC 16+ |
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCOURSE_WARNINGS_AS_ERRORS=ON
|
||||
cmake --preset default -DCOURSE_WARNINGS_AS_ERRORS=ON
|
||||
```
|
||||
|
||||
## C++26 Contracts
|
||||
|
||||
On GCC 16+, the build enables:
|
||||
|
||||
- `-fcontracts`
|
||||
- `-fcontract-evaluation-semantic=enforce`
|
||||
- A shared `handle_contract_violation` handler in `support/contract_handler.cpp`
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#include <contracts>
|
||||
|
||||
[[nodiscard]] auto factorial(const int n) -> long long pre(n >= 0);
|
||||
```
|
||||
|
||||
```cpp
|
||||
auto factorial(const int n) -> long long
|
||||
pre(n >= 0)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
- Use the **GCC 16** toolchain, not Apple Clang.
|
||||
- Disable **Clangd** and **Clang-Tidy** inspections — they use the Clang frontend and do not support GCC C++26 contracts.
|
||||
- **Cppcheck plugin:** disable inline analysis for this project (see [Cppcheck](#cppcheck) below).
|
||||
- Use **Run | Run…** and filter by module prefix (e.g. `02_control_flow`).
|
||||
- Enable **Google Test** integration to see individual tests in the tree view.
|
||||
|
||||
## Cppcheck
|
||||
|
||||
Cppcheck **does not understand GCC C++26 contracts** (`pre(...)`) or GCC 16 libstdc++ when the CLion plugin analyzes headers in isolation. That produces false IDE errors such as:
|
||||
|
||||
- `Include file: <contracts> not found`
|
||||
- `failed to evaluate #elif condition` in libstdc++ headers
|
||||
- parse errors on `pre(...)`
|
||||
|
||||
### Recommended: disable the plugin, use the CMake target
|
||||
|
||||
1. **Settings → Editor → Inspections → C/C++ → Cppcheck** → **disable**
|
||||
2. Configure and build once:
|
||||
```bash
|
||||
cmake --preset default
|
||||
cmake --build build
|
||||
```
|
||||
3. Run Cppcheck from CLion or terminal:
|
||||
```bash
|
||||
cmake --build build --target cppcheck_modules
|
||||
```
|
||||
|
||||
This uses `compile_commands.json` from your **GCC 16** build and suppresses known false positives (`cppcheck/suppressions.txt`).
|
||||
|
||||
### If you keep the plugin anyway
|
||||
|
||||
**Settings → Cppcheck configuration → Options:**
|
||||
|
||||
```
|
||||
--language=c++ --std=c++26 --max-configs=1 --suppress=missingIncludeSystem --suppress=syntaxError
|
||||
```
|
||||
|
||||
Expect remaining false positives on contract syntax. The plugin is not a good fit for this codebase.
|
||||
|
||||
## 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.
|
||||
All exercises build with **C++26**. Module READMEs describe the language feature each exercise teaches (C++11, C++17, etc.), but the toolchain baseline is C++26 throughout.
|
||||
|
||||
Happy learning!
|
||||
|
||||
@@ -21,7 +21,7 @@ function(add_course_exercise)
|
||||
endif()
|
||||
|
||||
if(NOT EXERCISE_STANDARD)
|
||||
set(EXERCISE_STANDARD 23)
|
||||
set(EXERCISE_STANDARD 26)
|
||||
endif()
|
||||
|
||||
set(target_name "${EXERCISE_MODULE}_${EXERCISE_NAME}")
|
||||
@@ -33,6 +33,11 @@ function(add_course_exercise)
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
)
|
||||
course_set_cxx_standard(${target_name} ${EXERCISE_STANDARD})
|
||||
|
||||
if(EXERCISE_STANDARD GREATER_EQUAL 26)
|
||||
course_enable_contracts(${target_name})
|
||||
endif()
|
||||
|
||||
course_apply_warnings(${target_name})
|
||||
|
||||
if(COURSE_BUILD_TESTS)
|
||||
|
||||
@@ -21,5 +21,37 @@ function(course_apply_warnings target_name)
|
||||
endfunction()
|
||||
|
||||
function(course_set_cxx_standard target_name standard)
|
||||
target_compile_features(${target_name} PRIVATE cxx_std_${standard})
|
||||
# Use explicit -std flags so older CLion/CMake builds still get C++26.
|
||||
if(standard GREATER_EQUAL 26)
|
||||
target_compile_options(${target_name} PRIVATE -std=c++26)
|
||||
elseif(standard GREATER_EQUAL 23)
|
||||
target_compile_options(${target_name} PRIVATE -std=c++23)
|
||||
elseif(standard GREATER_EQUAL 20)
|
||||
target_compile_options(${target_name} PRIVATE -std=c++20)
|
||||
elseif(standard GREATER_EQUAL 17)
|
||||
target_compile_options(${target_name} PRIVATE -std=c++17)
|
||||
elseif(standard GREATER_EQUAL 14)
|
||||
target_compile_options(${target_name} PRIVATE -std=c++14)
|
||||
else()
|
||||
target_compile_options(${target_name} PRIVATE -std=c++11)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(course_enable_contracts target_name)
|
||||
if(NOT COURSE_ENABLE_CONTRACTS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16))
|
||||
return()
|
||||
endif()
|
||||
|
||||
target_compile_options(
|
||||
${target_name}
|
||||
PRIVATE
|
||||
-fcontracts
|
||||
-fcontract-evaluation-semantic=enforce
|
||||
)
|
||||
target_link_options(${target_name} PRIVATE -fcontracts)
|
||||
target_link_libraries(${target_name} PRIVATE course_contract_support)
|
||||
endfunction()
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
function(course_add_cppcheck_targets)
|
||||
find_program(CPPCHECK_EXECUTABLE cppcheck)
|
||||
if(NOT CPPCHECK_EXECUTABLE)
|
||||
message(STATUS "cppcheck not found; cppcheck targets will not be available")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_compile_commands "${CMAKE_BINARY_DIR}/compile_commands.json")
|
||||
set(_suppressions "${CMAKE_SOURCE_DIR}/cppcheck/suppressions.txt")
|
||||
|
||||
add_custom_target(
|
||||
cppcheck_modules
|
||||
COMMAND
|
||||
"${CPPCHECK_EXECUTABLE}"
|
||||
--project=${_compile_commands}
|
||||
--enable=warning,style,performance
|
||||
--std=c++26
|
||||
--inline-suppr
|
||||
--suppressions-list=${_suppressions}
|
||||
-i${CMAKE_SOURCE_DIR}/support
|
||||
-i${CMAKE_BINARY_DIR}/_deps
|
||||
--quiet
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
COMMENT "Running cppcheck on course modules (build once before running this target)"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
cppcheck_project
|
||||
COMMAND
|
||||
"${CPPCHECK_EXECUTABLE}"
|
||||
--project=${_compile_commands}
|
||||
--enable=warning,style,performance
|
||||
--std=c++26
|
||||
--inline-suppr
|
||||
--suppressions-list=${_suppressions}
|
||||
--quiet
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
COMMENT "Running cppcheck on entire project (build once before running this target)"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
@@ -0,0 +1,22 @@
|
||||
# GCC 16 toolchain for C++26 and contracts.
|
||||
find_program(
|
||||
COURSE_GCC16_C
|
||||
NAMES gcc-16
|
||||
HINTS /opt/homebrew/bin /usr/local/bin /opt/local/bin
|
||||
)
|
||||
find_program(
|
||||
COURSE_GCC16_CXX
|
||||
NAMES g++-16
|
||||
HINTS /opt/homebrew/bin /usr/local/bin /opt/local/bin
|
||||
)
|
||||
|
||||
if(NOT COURSE_GCC16_CXX)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"g++-16 not found. Install GCC 16, e.g. 'brew install gcc@16', "
|
||||
"then ensure g++-16 is on your PATH."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_COMPILER "${COURSE_GCC16_C}" CACHE FILEPATH "C compiler" FORCE)
|
||||
set(CMAKE_CXX_COMPILER "${COURSE_GCC16_CXX}" CACHE FILEPATH "C++ compiler" FORCE)
|
||||
@@ -0,0 +1,19 @@
|
||||
# Suppress Cppcheck noise for this GCC 16 / C++26 course project.
|
||||
#
|
||||
# Prefer: cmake --build cmake-build-debug-gcc-16-cpp26 --target cppcheck_modules
|
||||
# Disable the CLion Cppcheck plugin — it analyzes headers in isolation.
|
||||
|
||||
# GCC / libstdc++ / contracts false positives (especially in CLion plugin mode)
|
||||
missingIncludeSystem
|
||||
syntaxError
|
||||
|
||||
# Third-party (Google Test / Google Mock) — analyzed by cppcheck_project
|
||||
*:_deps/*
|
||||
|
||||
# Course exercise stubs — intentional teaching patterns (TODO implementations)
|
||||
passedByValue:*modules/*
|
||||
useStlAlgorithm:*modules/*
|
||||
functionStatic:*modules/*
|
||||
uninitMemberVarNoCtor:*modules/*
|
||||
constVariable:*modules/*
|
||||
bitwiseOnBoolean:*modules/*
|
||||
@@ -1,7 +1,7 @@
|
||||
add_course_exercise(
|
||||
NAME hello_world
|
||||
MODULE 01_fundamentals
|
||||
STANDARD 17
|
||||
STANDARD 26
|
||||
SOURCES
|
||||
src/hello_world.cpp
|
||||
test/hello_world_test.cpp
|
||||
@@ -10,7 +10,7 @@ add_course_exercise(
|
||||
add_course_exercise(
|
||||
NAME variables_and_types
|
||||
MODULE 01_fundamentals
|
||||
STANDARD 17
|
||||
STANDARD 26
|
||||
SOURCES
|
||||
src/variables_and_types.cpp
|
||||
test/variables_and_types_test.cpp
|
||||
@@ -19,7 +19,7 @@ add_course_exercise(
|
||||
add_course_exercise(
|
||||
NAME operators
|
||||
MODULE 01_fundamentals
|
||||
STANDARD 17
|
||||
STANDARD 26
|
||||
SOURCES
|
||||
src/operators.cpp
|
||||
test/operators_test.cpp
|
||||
@@ -28,7 +28,7 @@ add_course_exercise(
|
||||
add_course_exercise(
|
||||
NAME basic_io
|
||||
MODULE 01_fundamentals
|
||||
STANDARD 17
|
||||
STANDARD 26
|
||||
SOURCES
|
||||
src/basic_io.cpp
|
||||
test/basic_io_test.cpp
|
||||
|
||||
@@ -5,24 +5,36 @@
|
||||
|
||||
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 join_words(const std::vector<std::string>& words, const char separator) -> std::string {
|
||||
std::ostringstream oss;
|
||||
size_t i = 0;
|
||||
for (const auto& word : words) {
|
||||
i++;
|
||||
oss << word;
|
||||
if (i < words.size()) {
|
||||
oss << separator;
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
auto parse_integers(const std::string& csv) -> std::vector<int> {
|
||||
// TODO: Parse comma-separated integers (e.g. "1,2,3").
|
||||
(void)csv;
|
||||
return {};
|
||||
std::vector<int> numbers;
|
||||
std::istringstream iss(csv);
|
||||
int n;
|
||||
char c;
|
||||
do {
|
||||
iss >> n;
|
||||
numbers.push_back(n);
|
||||
iss >> c;
|
||||
} while (!iss.eof());
|
||||
return numbers;
|
||||
}
|
||||
|
||||
auto format_score(const std::string& player, int score) -> std::string {
|
||||
// TODO: Return "<player> scored <score> points".
|
||||
(void)player;
|
||||
(void)score;
|
||||
return {};
|
||||
auto format_score(const std::string& player, const int score) -> std::string {
|
||||
std::ostringstream oss;
|
||||
oss << player << " scored " << score << " points";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // namespace fundamentals
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
#include "hello_world.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace fundamentals {
|
||||
|
||||
auto greet(const char* name) -> const char* {
|
||||
// TODO: Return a static string in the format "Hello, <name>!"
|
||||
(void)name;
|
||||
return "Hello, World!";
|
||||
const auto n = std::string(name);
|
||||
const auto c = "Hello, " + n + "!";
|
||||
// LESSON: Never return pointers to local objects -> dangling -> undefined behavior
|
||||
// NO! --> return c.c_str();
|
||||
// The destructor will be called when c goes out of scope, destroying the object and freeing its memory
|
||||
// This is shit C++ and should use std::string instead as a return value
|
||||
const auto p = new char[c.length() + 1]; // Give space for the null terminated pointer
|
||||
strcpy(p, c.c_str());
|
||||
return p;
|
||||
// TL;DR -> Never use C-style strings if possible
|
||||
}
|
||||
|
||||
} // namespace fundamentals
|
||||
|
||||
@@ -2,39 +2,31 @@
|
||||
|
||||
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 absolute_difference(const int lhs, const int rhs) -> int {
|
||||
// NOTE: std::abs not allowed
|
||||
return lhs < rhs ? rhs - lhs : lhs - rhs;
|
||||
}
|
||||
|
||||
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 is_in_range(const int value, const int min, const int max) -> bool {
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
auto count_set_bits(std::uint8_t value) -> int {
|
||||
// TODO: Count how many bits are set to 1.
|
||||
(void)value;
|
||||
return 0;
|
||||
auto count_set_bits(const std::uint8_t value) -> int {
|
||||
auto count = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (((value >> i) & 1) == 1) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
auto logical_and(bool lhs, bool rhs) -> bool {
|
||||
// TODO: Implement logical AND without using &&.
|
||||
(void)lhs;
|
||||
(void)rhs;
|
||||
return false;
|
||||
auto logical_and(const bool lhs, const bool rhs) -> bool {
|
||||
// NOTE: && can't be used
|
||||
return (lhs & rhs) == 1;
|
||||
}
|
||||
|
||||
auto logical_or(bool lhs, bool rhs) -> bool {
|
||||
// TODO: Implement logical OR without using ||.
|
||||
(void)lhs;
|
||||
(void)rhs;
|
||||
return false;
|
||||
auto logical_or(const bool lhs, const bool rhs) -> bool {
|
||||
// NOTE: || can't be used
|
||||
return (lhs | rhs) == 1;
|
||||
}
|
||||
|
||||
} // namespace fundamentals
|
||||
|
||||
@@ -1,43 +1,38 @@
|
||||
#include "variables_and_types.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace fundamentals {
|
||||
|
||||
auto add_integers(int lhs, int rhs) -> int {
|
||||
// TODO: Return the sum.
|
||||
(void)lhs;
|
||||
(void)rhs;
|
||||
return 0;
|
||||
auto add_integers(const int lhs, const int rhs) -> int {
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
auto multiply_doubles(double lhs, double rhs) -> double {
|
||||
// TODO: Return the product.
|
||||
(void)lhs;
|
||||
(void)rhs;
|
||||
return 0.0;
|
||||
auto multiply_doubles(const double lhs, const double rhs) -> double {
|
||||
return lhs * rhs;
|
||||
}
|
||||
|
||||
auto is_even(std::int64_t value) -> bool {
|
||||
// TODO: Return true when value is divisible by 2.
|
||||
(void)value;
|
||||
return false;
|
||||
auto is_even(const std::int64_t value) -> bool {
|
||||
return value % 2 == 0;
|
||||
}
|
||||
|
||||
auto describe_type(int value) -> std::string {
|
||||
// TODO: Return "int:" followed by the decimal representation.
|
||||
(void)value;
|
||||
return "int:0";
|
||||
auto describe_type(const int value) -> std::string {
|
||||
auto oss = std::ostringstream{};
|
||||
oss << "int:" << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
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(const double value) -> std::string {
|
||||
auto oss = std::ostringstream{};
|
||||
oss << "double:" << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
auto describe_type(bool value) -> std::string {
|
||||
// TODO: Return "bool:true" or "bool:false".
|
||||
(void)value;
|
||||
return "bool:false";
|
||||
auto describe_type(const bool value) -> std::string {
|
||||
auto oss = std::ostringstream{};
|
||||
const std::string representation = value ? "true" : "false";
|
||||
oss << "bool:" << representation;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // namespace fundamentals
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME conditionals MODULE 02_control_flow STANDARD 17
|
||||
add_course_exercise(NAME conditionals MODULE 02_control_flow STANDARD 26
|
||||
SOURCES src/conditionals.cpp test/conditionals_test.cpp)
|
||||
add_course_exercise(NAME switch_statements MODULE 02_control_flow STANDARD 17
|
||||
add_course_exercise(NAME switch_statements MODULE 02_control_flow STANDARD 26
|
||||
SOURCES src/switch_statements.cpp test/switch_statements_test.cpp)
|
||||
add_course_exercise(NAME loops MODULE 02_control_flow STANDARD 17
|
||||
add_course_exercise(NAME loops MODULE 02_control_flow STANDARD 26
|
||||
SOURCES src/loops.cpp test/loops_test.cpp)
|
||||
add_course_exercise(NAME break_continue MODULE 02_control_flow STANDARD 17
|
||||
add_course_exercise(NAME break_continue MODULE 02_control_flow STANDARD 26
|
||||
SOURCES src/break_continue.cpp test/break_continue_test.cpp)
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
namespace control_flow {
|
||||
|
||||
[[nodiscard]] auto sum_range(int from, int to) -> int;
|
||||
[[nodiscard]] auto factorial(int n) -> long long;
|
||||
// ReSharper disable once CppConstParameterInDeclaration
|
||||
[[nodiscard]] auto factorial(const int n) -> long long pre(n >= 0);
|
||||
[[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;
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ 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;
|
||||
// ReSharper disable once CppConstParameterInDeclaration
|
||||
[[nodiscard]] auto apply_operation(const Operation op, const int lhs, const int rhs) -> int
|
||||
pre(op != Operation::Unknown);
|
||||
[[nodiscard]] auto day_name(int day) -> std::string;
|
||||
|
||||
} // namespace control_flow
|
||||
|
||||
@@ -3,18 +3,27 @@
|
||||
namespace control_flow {
|
||||
|
||||
auto sum_positive(const std::vector<int>& data) -> int {
|
||||
(void)data;
|
||||
return 0;
|
||||
auto sum = 0;
|
||||
for (const auto i : data) {
|
||||
if (i > 0) sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
auto first_multiple_of(const std::vector<int>& data, int divisor) -> int {
|
||||
(void)data; (void)divisor;
|
||||
auto first_multiple_of(const std::vector<int>& data, const int divisor) -> int {
|
||||
for (const auto i : data) {
|
||||
if (i > divisor && i % divisor == 0) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto collect_until_negative(const std::vector<int>& data) -> std::vector<int> {
|
||||
(void)data;
|
||||
return {};
|
||||
std::vector<int> result;
|
||||
for (const auto i : data) {
|
||||
if (i < 0) break;
|
||||
result.push_back(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace control_flow
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
namespace control_flow {
|
||||
|
||||
auto classify_score(int score) -> Grade {
|
||||
// TODO: A>=90, B>=80, C>=70, D>=60, else F
|
||||
(void)score;
|
||||
auto classify_score(const int score) -> Grade {
|
||||
if (score >= 90) return Grade::A;
|
||||
if (score >= 80) return Grade::B;
|
||||
if (score >= 70) return Grade::C;
|
||||
if (score >= 60) return Grade::D;
|
||||
return Grade::F;
|
||||
}
|
||||
|
||||
auto max_of_three(int a, int b, int c) -> int {
|
||||
(void)a; (void)b; (void)c;
|
||||
return 0;
|
||||
auto max_of_three(const int a, const int b, const int c) -> int {
|
||||
if (a >= b) {
|
||||
if (a >= c) return a;
|
||||
return c;
|
||||
}
|
||||
if (b >= c) return b;
|
||||
return c;
|
||||
}
|
||||
|
||||
auto sign_of(int value) -> int {
|
||||
// TODO: Return -1, 0, or 1
|
||||
(void)value;
|
||||
return 0;
|
||||
auto sign_of(const int value) -> int {
|
||||
return value == 0 ? 0 : value > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
} // namespace control_flow
|
||||
|
||||
@@ -1,26 +1,41 @@
|
||||
#include "loops.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
namespace control_flow {
|
||||
|
||||
auto sum_range(int from, int to) -> int {
|
||||
(void)from; (void)to;
|
||||
return 0;
|
||||
auto sum_range(const int from, const int to) -> int {
|
||||
return std::ranges::fold_left(
|
||||
std::views::iota(from, to + 1), // End-exclusive
|
||||
0,
|
||||
[](const int acc, const int x) { return acc + x; }
|
||||
);
|
||||
}
|
||||
|
||||
auto factorial(int n) -> long long {
|
||||
(void)n;
|
||||
return 1;
|
||||
auto factorial(const int n) -> long long
|
||||
pre(n >= 0)
|
||||
{
|
||||
if (n == 0 || n == 1) return 1;
|
||||
return std::ranges::fold_left(
|
||||
std::views::iota(2, n + 1), // End-exclusive
|
||||
1LL,
|
||||
[](const long long acc, const int x) { return acc * x; }
|
||||
);
|
||||
}
|
||||
|
||||
auto count_occurrences(const std::vector<int>& data, int target) -> int {
|
||||
(void)data; (void)target;
|
||||
return 0;
|
||||
auto count_occurrences(const std::vector<int>& data, const int target) -> int {
|
||||
int count = 0;
|
||||
std::ranges::for_each(data, [target, &count](const int x) {
|
||||
if (x == target) ++count;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
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;
|
||||
auto first_index_of(const std::vector<int>& data, const int target) -> int {
|
||||
const auto it = std::ranges::find(data, target);
|
||||
if (it == data.end()) return -1;
|
||||
return static_cast<int>(std::distance(data.begin(), it));
|
||||
}
|
||||
|
||||
} // namespace control_flow
|
||||
|
||||
@@ -2,20 +2,40 @@
|
||||
|
||||
namespace control_flow {
|
||||
|
||||
auto parse_operation(char op) -> Operation {
|
||||
(void)op;
|
||||
return Operation::Unknown;
|
||||
// enum class Operation { Add, Subtract, Multiply, Divide, Unknown };
|
||||
auto parse_operation(const char op) -> Operation {
|
||||
switch (op) {
|
||||
case '+': return Operation::Add;
|
||||
case '-': return Operation::Subtract;
|
||||
case '*': return Operation::Multiply;
|
||||
case '/': return Operation::Divide;
|
||||
default: return Operation::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
auto apply_operation(Operation op, int lhs, int rhs) -> int {
|
||||
(void)op; (void)lhs; (void)rhs;
|
||||
return 0;
|
||||
auto apply_operation(const Operation op, const int lhs, const int rhs) -> int
|
||||
pre(op != Operation::Unknown)
|
||||
{
|
||||
switch (op) {
|
||||
case Operation::Add: return lhs + rhs;
|
||||
case Operation::Subtract: return lhs - rhs;
|
||||
case Operation::Multiply: return lhs * rhs;
|
||||
case Operation::Divide: return lhs / rhs;
|
||||
default: return -1; // Unreachable due to contract
|
||||
}
|
||||
}
|
||||
|
||||
auto day_name(int day) -> std::string {
|
||||
// TODO: 1=Monday ... 7=Sunday, else "Invalid"
|
||||
(void)day;
|
||||
return "Invalid";
|
||||
auto day_name(const int day) -> std::string {
|
||||
switch (day) {
|
||||
case 1: return "Monday";
|
||||
case 2: return "Tuesday";
|
||||
case 3: return "Wednesday";
|
||||
case 4: return "Thursday";
|
||||
case 5: return "Friday";
|
||||
case 6: return "Saturday";
|
||||
case 7: return "Sunday";
|
||||
default: return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace control_flow
|
||||
|
||||
@@ -9,6 +9,12 @@ TEST(Loops, SumRange) {
|
||||
TEST(Loops, Factorial) {
|
||||
EXPECT_EQ(control_flow::factorial(0), 1);
|
||||
EXPECT_EQ(control_flow::factorial(5), 120);
|
||||
// Breach of contract
|
||||
EXPECT_DEATH(
|
||||
{
|
||||
(void)control_flow::factorial(-1);
|
||||
},
|
||||
"contract violation");
|
||||
}
|
||||
|
||||
TEST(Loops, CountOccurrences) {
|
||||
|
||||
@@ -9,6 +9,12 @@ TEST(SwitchStatements, ParseOperation) {
|
||||
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);
|
||||
// Breach of contract
|
||||
EXPECT_DEATH(
|
||||
{
|
||||
(void)control_flow::apply_operation(control_flow::Operation::Unknown, 2, 3);
|
||||
},
|
||||
"contract violation");
|
||||
}
|
||||
|
||||
TEST(SwitchStatements, DayName) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME function_basics MODULE 03_functions STANDARD 17
|
||||
add_course_exercise(NAME function_basics MODULE 03_functions STANDARD 26
|
||||
SOURCES src/function_basics.cpp test/function_basics_test.cpp)
|
||||
add_course_exercise(NAME overloading MODULE 03_functions STANDARD 17
|
||||
add_course_exercise(NAME overloading MODULE 03_functions STANDARD 26
|
||||
SOURCES src/overloading.cpp test/overloading_test.cpp)
|
||||
add_course_exercise(NAME default_parameters MODULE 03_functions STANDARD 17
|
||||
add_course_exercise(NAME default_parameters MODULE 03_functions STANDARD 26
|
||||
SOURCES src/default_parameters.cpp test/default_parameters_test.cpp)
|
||||
add_course_exercise(NAME recursion MODULE 03_functions STANDARD 17
|
||||
add_course_exercise(NAME recursion MODULE 03_functions STANDARD 26
|
||||
SOURCES src/recursion.cpp test/recursion_test.cpp)
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
#include "default_parameters.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
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; }
|
||||
auto repeat(const char ch, const int count) -> std::string {
|
||||
std::ostringstream oss;
|
||||
for (int i = 0; i < count; i++) {
|
||||
oss << ch;
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
auto power(const int base, const int exponent) -> long long {
|
||||
return std::pow(base, exponent);
|
||||
}
|
||||
|
||||
auto clamp(const int value, const int min, const int max) -> int {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace functions
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
#include "function_basics.hpp"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
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; }
|
||||
auto square(const int value) -> int {
|
||||
return value * value;
|
||||
}
|
||||
|
||||
void swap_integers(int& lhs, int& rhs) {
|
||||
const int tmp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = tmp;
|
||||
}
|
||||
|
||||
auto concatenate(const std::vector<std::string>& parts) -> std::string {
|
||||
return std::ranges::to<std::string>(std::views::join(parts));
|
||||
}
|
||||
|
||||
void append_suffix(std::string& text, const std::string& suffix) {
|
||||
// The string is passed by reference, so it can have text appended
|
||||
text += suffix;
|
||||
}
|
||||
|
||||
} // namespace functions
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
|
||||
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; }
|
||||
// Overloading based in parameter type
|
||||
|
||||
auto max_value(const int a, const int b) -> int { return a > b ? a : b; }
|
||||
auto max_value(const double a, const double b) -> double { return a > b ? a : b; }
|
||||
auto max_value(const std::string& a, const std::string& b) -> std::string { return a > b ? a : b; }
|
||||
|
||||
// Overloading based in parameter count
|
||||
|
||||
auto area(const int side) -> int { return side * side; }
|
||||
auto area(const int width, const int height) -> int { return width * height; }
|
||||
|
||||
} // namespace functions
|
||||
|
||||
@@ -2,8 +2,34 @@
|
||||
|
||||
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; }
|
||||
auto fibonacci(const int n) -> long long {
|
||||
if (n == 0) return 0;
|
||||
if (n == 1) return 1;
|
||||
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||
}
|
||||
|
||||
auto sum_digits(const int n) -> int {
|
||||
if (n < 10) return n;
|
||||
return n % 10 + sum_digits(n / 10);
|
||||
}
|
||||
|
||||
// Advanced recursion with recursive lambda helper
|
||||
auto is_palindrome(const int n) -> bool {
|
||||
auto f = [](const auto& self, const int x, const int d) -> bool {
|
||||
if (x < 0) return false;
|
||||
if (x < 10) return true;
|
||||
if (x / d != x % 10) return false;
|
||||
if (d < 10) return true;
|
||||
const int m = (x / d) / 10;
|
||||
return self(self, m, d / 100);
|
||||
};
|
||||
if (n < 0) return false;
|
||||
if (n < 10) return true;
|
||||
int d = 1;
|
||||
while (n / d >= 10) {
|
||||
d *= 10;
|
||||
}
|
||||
return f(f, n, d);
|
||||
}
|
||||
|
||||
} // namespace functions
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME c_arrays MODULE 04_arrays_and_strings STANDARD 17
|
||||
add_course_exercise(NAME c_arrays MODULE 04_arrays_and_strings STANDARD 26
|
||||
SOURCES src/c_arrays.cpp test/c_arrays_test.cpp)
|
||||
add_course_exercise(NAME std_string MODULE 04_arrays_and_strings STANDARD 17
|
||||
add_course_exercise(NAME std_string MODULE 04_arrays_and_strings STANDARD 26
|
||||
SOURCES src/std_string.cpp test/std_string_test.cpp)
|
||||
add_course_exercise(NAME std_vector MODULE 04_arrays_and_strings STANDARD 17
|
||||
add_course_exercise(NAME std_vector MODULE 04_arrays_and_strings STANDARD 26
|
||||
SOURCES src/std_vector.cpp test/std_vector_test.cpp)
|
||||
add_course_exercise(NAME multidimensional MODULE 04_arrays_and_strings STANDARD 17
|
||||
add_course_exercise(NAME multidimensional MODULE 04_arrays_and_strings STANDARD 26
|
||||
SOURCES src/multidimensional.cpp test/multidimensional_test.cpp)
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
namespace arrays {
|
||||
|
||||
[[nodiscard]] auto to_uppercase(std::string text) -> std::string;
|
||||
[[nodiscard]] auto to_uppercase(const 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 replace_all(const std::string &text, char from, char to) -> std::string;
|
||||
[[nodiscard]] auto starts_with(const std::string& text, const std::string& prefix) -> bool;
|
||||
|
||||
} // namespace arrays
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
#include "c_arrays.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
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; }
|
||||
auto array_sum(const int* data, const std::size_t size) -> int {
|
||||
auto sum = 0;
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
sum += data[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
auto array_max(const int* data, const std::size_t size) -> int {
|
||||
auto max = INT_MIN;
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
if (data[i] > max) {
|
||||
max = data[i];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void reverse_array(int* data, const std::size_t size) {
|
||||
for (std::size_t i = 0; i < size / 2; ++i) {
|
||||
const auto temp = data[i];
|
||||
data[i] = data[size - i - 1];
|
||||
data[size - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace arrays
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
#include "multidimensional.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
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 {}; }
|
||||
auto create_matrix(const int rows, const int cols, const int fill) -> Matrix {
|
||||
return Matrix(rows, std::vector(cols, fill));
|
||||
}
|
||||
|
||||
auto transpose(const Matrix& matrix) -> Matrix {
|
||||
auto new_mat = Matrix(matrix[0].size(), std::vector(matrix.size(), 0));
|
||||
for (size_t row = 0; row < matrix.size(); ++row) {
|
||||
for (size_t col = 0; col < matrix[row].size(); ++col) {
|
||||
new_mat[col][row] = matrix[row][col];
|
||||
}
|
||||
}
|
||||
return new_mat;
|
||||
}
|
||||
|
||||
auto row_sums(const Matrix& matrix) -> std::vector<int> {
|
||||
auto sums = std::vector(matrix.size(), 0);
|
||||
auto idx = 0;
|
||||
for (auto row: matrix) {
|
||||
sums[idx] = std::ranges::fold_left(row, 0, std::plus{});
|
||||
idx++;
|
||||
}
|
||||
return sums;
|
||||
}
|
||||
|
||||
} // namespace arrays
|
||||
|
||||
@@ -1,10 +1,46 @@
|
||||
#include "std_string.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
|
||||
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; }
|
||||
auto to_uppercase(const std::string &text) -> std::string {
|
||||
// Returns copy of the string without mutating the original string
|
||||
auto s = std::string{text};
|
||||
auto c_to_upper = [](const unsigned char c) { return std::toupper(c); };
|
||||
std::ranges::transform(s, s.begin(), c_to_upper);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto trim(const std::string& text) -> std::string {
|
||||
// Returns a trimmed copy of the string
|
||||
auto s = std::string{text};
|
||||
auto is_not_space = [](const unsigned char c) { return !std::isspace(c); };
|
||||
const auto first = std::ranges::find_if(s, is_not_space);
|
||||
const auto last = std::ranges::find_if(s.rbegin(), s.rend(), is_not_space).base();
|
||||
s.erase(last, s.end());
|
||||
s.erase(s.begin(), first);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto replace_all(const std::string &text, const char from, const char to) -> std::string {
|
||||
// This one also copies the string
|
||||
auto s = std::string{text};
|
||||
std::ranges::replace(s, from, to);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto starts_with(const std::string& text, const std::string& prefix) -> bool {
|
||||
// Since C++20 you can use std::string::starts_with, but anyways.
|
||||
if (text.size() < prefix.size()) return false;
|
||||
// uz -> unsigned size_t
|
||||
for (auto idx = 0uz; idx < prefix.size(); ++idx) {
|
||||
if (text[idx] != prefix[idx]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace arrays
|
||||
|
||||
@@ -7,6 +7,7 @@ TEST(StdString, ToUppercase) {
|
||||
|
||||
TEST(StdString, Trim) {
|
||||
EXPECT_EQ(arrays::trim(" hi "), "hi");
|
||||
EXPECT_EQ(arrays::trim(" a b c "), "a b c");
|
||||
}
|
||||
|
||||
TEST(StdString, ReplaceAll) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME pointer_basics MODULE 05_pointers_and_references STANDARD 17
|
||||
add_course_exercise(NAME pointer_basics MODULE 05_pointers_and_references STANDARD 26
|
||||
SOURCES src/pointer_basics.cpp test/pointer_basics_test.cpp)
|
||||
add_course_exercise(NAME references MODULE 05_pointers_and_references STANDARD 17
|
||||
add_course_exercise(NAME references MODULE 05_pointers_and_references STANDARD 26
|
||||
SOURCES src/references.cpp test/references_test.cpp)
|
||||
add_course_exercise(NAME pointer_arithmetic MODULE 05_pointers_and_references STANDARD 17
|
||||
add_course_exercise(NAME pointer_arithmetic MODULE 05_pointers_and_references STANDARD 26
|
||||
SOURCES src/pointer_arithmetic.cpp test/pointer_arithmetic_test.cpp)
|
||||
add_course_exercise(NAME dynamic_memory MODULE 05_pointers_and_references STANDARD 17
|
||||
add_course_exercise(NAME dynamic_memory MODULE 05_pointers_and_references STANDARD 26
|
||||
SOURCES src/dynamic_memory.cpp test/dynamic_memory_test.cpp)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace pointers {
|
||||
|
||||
[[nodiscard]] auto allocate_and_fill(std::size_t count, int value) -> int*;
|
||||
void deallocate(int* ptr);
|
||||
void deallocate(const int* ptr);
|
||||
[[nodiscard]] auto clone_array(const int* source, std::size_t count) -> int*;
|
||||
|
||||
} // namespace pointers
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace pointers {
|
||||
|
||||
[[nodiscard]] auto get_value(int* ptr) -> int;
|
||||
[[nodiscard]] auto get_value(const 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);
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
#include "dynamic_memory.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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; }
|
||||
auto allocate_and_fill(const std::size_t count, const int value) -> int* {
|
||||
const auto na = new int[count];
|
||||
std::fill_n(na, count, value);
|
||||
return na;
|
||||
}
|
||||
|
||||
void deallocate(const int* ptr) {
|
||||
delete[] ptr;
|
||||
}
|
||||
|
||||
auto clone_array(const int* source, const std::size_t count) -> int* {
|
||||
const auto na = new int[count];
|
||||
std::copy_n(source, count, na);
|
||||
return na;
|
||||
}
|
||||
|
||||
} // namespace pointers
|
||||
|
||||
@@ -2,8 +2,24 @@
|
||||
|
||||
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; }
|
||||
auto pointer_distance(const int* begin, const int* end) -> std::size_t {
|
||||
return end - begin;
|
||||
}
|
||||
|
||||
auto find_pointer(const int* begin, const int* end, int target) -> const int* {
|
||||
while (*begin != target && ++begin != end) {
|
||||
if (*begin == target) return begin;
|
||||
begin++;
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
void reverse_in_place(int* begin, int* end) {
|
||||
while (begin != end && begin != --end) {
|
||||
const auto aux = *begin;
|
||||
*begin++ = *end;
|
||||
*end = aux;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pointers
|
||||
|
||||
@@ -2,9 +2,22 @@
|
||||
|
||||
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; }
|
||||
auto get_value(const int* ptr) -> int {
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void set_value(int* ptr, int value) {
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
auto is_null(const int* ptr) -> bool {
|
||||
return ptr == nullptr;
|
||||
}
|
||||
|
||||
void swap_via_pointers(int* a, int* b) {
|
||||
const auto aux = *a;
|
||||
*a = *b;
|
||||
*b = aux;
|
||||
}
|
||||
|
||||
} // namespace pointers
|
||||
|
||||
@@ -2,9 +2,19 @@
|
||||
|
||||
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; }
|
||||
auto double_value(const int& value) -> int {
|
||||
return value * 2;
|
||||
}
|
||||
void increment(int& value) {
|
||||
value++;
|
||||
}
|
||||
|
||||
auto max_ref(const int& a, const int& b) -> const int& {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
auto sum_three(const int& a, const int& b, const int& c) -> int {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
} // namespace pointers
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME classes_and_objects MODULE 06_oop_basics STANDARD 17
|
||||
add_course_exercise(NAME classes_and_objects MODULE 06_oop_basics STANDARD 26
|
||||
SOURCES src/classes_and_objects.cpp test/classes_and_objects_test.cpp)
|
||||
add_course_exercise(NAME constructors MODULE 06_oop_basics STANDARD 17
|
||||
add_course_exercise(NAME constructors MODULE 06_oop_basics STANDARD 26
|
||||
SOURCES src/constructors.cpp test/constructors_test.cpp)
|
||||
add_course_exercise(NAME inheritance MODULE 06_oop_basics STANDARD 17
|
||||
add_course_exercise(NAME inheritance MODULE 06_oop_basics STANDARD 26
|
||||
SOURCES src/inheritance.cpp test/inheritance_test.cpp)
|
||||
add_course_exercise(NAME polymorphism MODULE 06_oop_basics STANDARD 17
|
||||
add_course_exercise(NAME polymorphism MODULE 06_oop_basics STANDARD 26
|
||||
SOURCES src/polymorphism.cpp src/inheritance.cpp test/polymorphism_test.cpp)
|
||||
|
||||
@@ -11,8 +11,8 @@ public:
|
||||
[[nodiscard]] auto owner() const -> const std::string&;
|
||||
[[nodiscard]] auto balance() const -> double;
|
||||
|
||||
void deposit(double amount);
|
||||
auto withdraw(double amount) -> bool;
|
||||
void deposit(double amount) pre(amount > 0);
|
||||
auto withdraw(double amount) -> bool pre(amount > 0);
|
||||
|
||||
private:
|
||||
std::string owner_;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace oop {
|
||||
class StringBuilder {
|
||||
public:
|
||||
StringBuilder() = default;
|
||||
explicit StringBuilder(std::string initial);
|
||||
explicit StringBuilder(const std::string &initial);
|
||||
StringBuilder(const StringBuilder& other);
|
||||
auto operator=(const StringBuilder& other) -> StringBuilder&;
|
||||
~StringBuilder() = default;
|
||||
|
||||
@@ -2,20 +2,23 @@
|
||||
|
||||
namespace oop {
|
||||
|
||||
BankAccount::BankAccount(std::string owner, double balance)
|
||||
BankAccount::BankAccount(std::string owner, const 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;
|
||||
void BankAccount::deposit(const double amount)
|
||||
{
|
||||
this->balance_ += amount;
|
||||
}
|
||||
|
||||
auto BankAccount::withdraw(double amount) -> bool {
|
||||
// TODO: Return false if insufficient funds, else subtract and return true
|
||||
(void)amount;
|
||||
auto BankAccount::withdraw(const double amount) -> bool
|
||||
{
|
||||
if (this->balance_ >= amount) {
|
||||
this->balance_ -= amount;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace oop {
|
||||
|
||||
StringBuilder::StringBuilder(std::string initial) {
|
||||
// TODO: Initialize buffer_ from initial
|
||||
(void)initial;
|
||||
}
|
||||
|
||||
StringBuilder::StringBuilder(const StringBuilder& other) : buffer_(other.buffer_) {}
|
||||
StringBuilder::StringBuilder(const std::string &initial)
|
||||
: buffer_(initial.begin(), initial.end()) {}
|
||||
|
||||
StringBuilder::StringBuilder(const StringBuilder& other)
|
||||
: buffer_(other.buffer_) {}
|
||||
|
||||
auto StringBuilder::operator=(const StringBuilder& other) -> StringBuilder& {
|
||||
if (this != &other) {
|
||||
@@ -17,8 +17,7 @@ auto StringBuilder::operator=(const StringBuilder& other) -> StringBuilder& {
|
||||
}
|
||||
|
||||
void StringBuilder::append(const std::string& text) {
|
||||
// TODO: Append all characters from text
|
||||
(void)text;
|
||||
std::ranges::copy(text, std::back_inserter(this->buffer_));
|
||||
}
|
||||
|
||||
auto StringBuilder::str() const -> std::string {
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace oop {
|
||||
|
||||
Circle::Circle(double radius) : radius_(radius) {}
|
||||
Circle::Circle(const 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) {}
|
||||
Rectangle::Rectangle(const double width, const double height) : width_(width), height_(height) {}
|
||||
auto Rectangle::name() const -> std::string { return "Rectangle"; }
|
||||
auto Rectangle::area() const -> double { return width_ * height_; }
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ 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;
|
||||
total += shape->area();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -15,8 +14,7 @@ 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;
|
||||
names.push_back(shape->name());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ TEST(ClassesAndObjects, DepositAndWithdraw) {
|
||||
EXPECT_TRUE(account.withdraw(30.0));
|
||||
EXPECT_DOUBLE_EQ(account.balance(), 120.0);
|
||||
EXPECT_FALSE(account.withdraw(1000.0));
|
||||
EXPECT_DEATH(account.withdraw(-10.0), "amount > 0");
|
||||
EXPECT_DEATH(account.deposit(-10.0), "amount > 0");
|
||||
}
|
||||
|
||||
TEST(ClassesAndObjects, OwnerName) {
|
||||
|
||||
@@ -8,7 +8,7 @@ TEST(Constructors, InitialAndAppend) {
|
||||
}
|
||||
|
||||
TEST(Constructors, CopySemantics) {
|
||||
oop::StringBuilder original("C++");
|
||||
const oop::StringBuilder original("C++");
|
||||
oop::StringBuilder copy = original;
|
||||
copy.append("11");
|
||||
EXPECT_EQ(original.str(), "C++");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME sequential_containers MODULE 07_stl_containers STANDARD 17
|
||||
add_course_exercise(NAME sequential_containers MODULE 07_stl_containers STANDARD 26
|
||||
SOURCES src/sequential_containers.cpp test/sequential_containers_test.cpp)
|
||||
add_course_exercise(NAME associative_containers MODULE 07_stl_containers STANDARD 17
|
||||
add_course_exercise(NAME associative_containers MODULE 07_stl_containers STANDARD 26
|
||||
SOURCES src/associative_containers.cpp test/associative_containers_test.cpp)
|
||||
add_course_exercise(NAME unordered_containers MODULE 07_stl_containers STANDARD 17
|
||||
add_course_exercise(NAME unordered_containers MODULE 07_stl_containers STANDARD 26
|
||||
SOURCES src/unordered_containers.cpp test/unordered_containers_test.cpp)
|
||||
add_course_exercise(NAME container_adapters MODULE 07_stl_containers STANDARD 17
|
||||
add_course_exercise(NAME container_adapters MODULE 07_stl_containers STANDARD 26
|
||||
SOURCES src/container_adapters.cpp test/container_adapters_test.cpp)
|
||||
|
||||
@@ -9,6 +9,6 @@ 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>;
|
||||
[[nodiscard]] auto rotate_deque(const std::deque<int> &data, int steps) -> std::deque<int>;
|
||||
|
||||
} // namespace stl_containers
|
||||
|
||||
@@ -4,18 +4,25 @@ namespace stl_containers {
|
||||
|
||||
auto word_frequencies(const std::vector<std::string>& words)
|
||||
-> std::map<std::string, int> {
|
||||
(void)words;
|
||||
return {};
|
||||
auto m = std::map<std::string, int>();
|
||||
for (const auto& word : words) {
|
||||
++m[word];
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
auto unique_sorted(const std::vector<int>& data) -> std::set<int> {
|
||||
(void)data;
|
||||
return {};
|
||||
auto s = std::set<int>();
|
||||
for (const auto& value : data) {
|
||||
s.insert(value);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
auto invert_map(const std::map<int, std::string>& input) -> std::map<std::string, int> {
|
||||
(void)input;
|
||||
return {};
|
||||
auto m = std::map<std::string, int>();
|
||||
for (const auto&[k, v] : input) m[v] = k;
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace stl_containers
|
||||
|
||||
@@ -1,20 +1,52 @@
|
||||
#include "container_adapters.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
namespace stl_containers {
|
||||
|
||||
auto is_balanced_parentheses(const std::string& text) -> bool {
|
||||
(void)text;
|
||||
std::stack<char> s;
|
||||
for (const auto c: text) {
|
||||
switch (c) {
|
||||
case '(':
|
||||
s.push(')');
|
||||
break;
|
||||
case '[':
|
||||
s.push(']');
|
||||
break;
|
||||
case '{':
|
||||
s.push('}');
|
||||
break;
|
||||
case ')':
|
||||
case ']':
|
||||
case '}': {
|
||||
if (const auto n = s.top(); c != n) return false;
|
||||
s.pop();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (s.empty()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto simulate_queue(const std::vector<int>& arrivals) -> std::vector<int> {
|
||||
(void)arrivals;
|
||||
return {};
|
||||
auto v = std::vector<int>{};
|
||||
const auto push = [&v](const int val){v.push_back(val);};
|
||||
std::ranges::for_each(arrivals, push);
|
||||
return v;
|
||||
}
|
||||
|
||||
auto top_k_largest(const std::vector<int>& data, int k) -> std::vector<int> {
|
||||
(void)data; (void)k;
|
||||
return {};
|
||||
auto top_k_largest(const std::vector<int>& data, const int k) -> std::vector<int> {;
|
||||
// Remove duplicates
|
||||
std::set<int> s{data.begin(), data.end()};
|
||||
auto v = std::vector<int>{s.begin(), s.end()};
|
||||
std::ranges::sort(v, std::ranges::greater{});
|
||||
v.resize(k);
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace stl_containers
|
||||
|
||||
@@ -4,18 +4,38 @@ 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 {};
|
||||
std::vector<int> v;
|
||||
auto aIt = a.begin();
|
||||
auto bIt = b.begin();
|
||||
while (aIt != a.end() && bIt != b.end()) {
|
||||
if (*aIt >= *bIt) {
|
||||
v.push_back(*bIt++);
|
||||
} else {
|
||||
v.push_back(*aIt++);
|
||||
}
|
||||
}
|
||||
while (aIt != a.end()) {
|
||||
v.push_back(*aIt++);
|
||||
}
|
||||
while (bIt != b.end()) {
|
||||
v.push_back(*bIt++);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
auto list_to_vector(const std::list<int>& data) -> std::vector<int> {
|
||||
(void)data;
|
||||
return {};
|
||||
return std::vector(data.begin(), data.end());
|
||||
}
|
||||
|
||||
auto rotate_deque(std::deque<int> data, int steps) -> std::deque<int> {
|
||||
(void)data; (void)steps;
|
||||
return {};
|
||||
auto rotate_deque(const std::deque<int> &data, const int steps) -> std::deque<int> {
|
||||
auto result = std::deque{data};
|
||||
for (auto i = 0; i < steps; ++i) {
|
||||
// Take from the front, add to the end
|
||||
auto aux = result.front();
|
||||
result.push_back(aux);
|
||||
result.pop_front();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace stl_containers
|
||||
|
||||
@@ -1,21 +1,37 @@
|
||||
#include "unordered_containers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
namespace stl_containers {
|
||||
|
||||
auto first_unique(const std::vector<std::string>& words) -> std::string {
|
||||
(void)words;
|
||||
return {};
|
||||
auto s = std::set<std::string>();
|
||||
for (const auto& word : words) {
|
||||
if (!s.contains(word)) {
|
||||
s.insert(word);
|
||||
} else {
|
||||
s.erase(word);
|
||||
}
|
||||
}
|
||||
return s.empty() ? "" : *s.begin();
|
||||
}
|
||||
|
||||
auto group_anagrams(const std::vector<std::string>& words)
|
||||
-> std::unordered_map<std::string, std::vector<std::string>> {
|
||||
(void)words;
|
||||
return {};
|
||||
auto groups = std::unordered_map<std::string, std::vector<std::string>>();
|
||||
for (const auto& word : words) {
|
||||
auto key = word;
|
||||
std::ranges::sort(key);
|
||||
groups[key].push_back(word);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
auto has_duplicate(const std::vector<int>& data) -> bool {
|
||||
(void)data;
|
||||
return false;
|
||||
auto s = std::set<int>{};
|
||||
for (auto i : data) s.insert(i);
|
||||
return s.size() != data.size();
|
||||
}
|
||||
|
||||
} // namespace stl_containers
|
||||
|
||||
@@ -12,4 +12,5 @@ TEST(ContainerAdapters, SimulateQueue) {
|
||||
|
||||
TEST(ContainerAdapters, TopKLargest) {
|
||||
EXPECT_EQ(stl_containers::top_k_largest({3, 1, 4, 1, 5}, 3), (std::vector<int>{5, 4, 3}));
|
||||
EXPECT_EQ(stl_containers::top_k_largest({3, 1, 4, 1, 5, 5, 5}, 3), (std::vector<int>{5, 4, 3}));
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ TEST(SequentialContainers, ListToVector) {
|
||||
|
||||
TEST(SequentialContainers, RotateDeque) {
|
||||
EXPECT_EQ(stl_containers::rotate_deque({1, 2, 3, 4}, 1), (std::deque<int>{2, 3, 4, 1}));
|
||||
EXPECT_EQ(stl_containers::rotate_deque({1, 2, 3, 4}, 2), (std::deque<int>{3, 4, 1, 2}));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "unordered_containers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(UnorderedContainers, FirstUnique) {
|
||||
@@ -9,3 +12,42 @@ TEST(UnorderedContainers, HasDuplicate) {
|
||||
EXPECT_TRUE(stl_containers::has_duplicate({1, 2, 2}));
|
||||
EXPECT_FALSE(stl_containers::has_duplicate({1, 2, 3}));
|
||||
}
|
||||
|
||||
TEST(UnorderedContainers, GroupAnagramsBasic) {
|
||||
auto result = stl_containers::group_anagrams({"eat", "tea", "tan", "ate", "nat", "bat"});
|
||||
// Sort each group for deterministic comparison
|
||||
for (auto &group: result | std::views::values) {
|
||||
std::ranges::sort(group);
|
||||
}
|
||||
ASSERT_EQ(result.size(), 3);
|
||||
EXPECT_EQ(result["aet"], (std::vector<std::string>{"ate", "eat", "tea"}));
|
||||
EXPECT_EQ(result["ant"], (std::vector<std::string>{"nat", "tan"}));
|
||||
EXPECT_EQ(result["abt"], (std::vector<std::string>{"bat"}));
|
||||
}
|
||||
|
||||
TEST(UnorderedContainers, GroupAnagramsEmpty) {
|
||||
auto result = stl_containers::group_anagrams({});
|
||||
EXPECT_TRUE(result.empty());
|
||||
}
|
||||
|
||||
TEST(UnorderedContainers, GroupAnagramsSingleWord) {
|
||||
auto result = stl_containers::group_anagrams({"hello"});
|
||||
ASSERT_EQ(result.size(), 1);
|
||||
EXPECT_EQ(result["ehllo"], (std::vector<std::string>{"hello"}));
|
||||
}
|
||||
|
||||
TEST(UnorderedContainers, GroupAnagramsNoAnagrams) {
|
||||
auto result = stl_containers::group_anagrams({"abc", "def", "ghi"});
|
||||
ASSERT_EQ(result.size(), 3);
|
||||
EXPECT_EQ(result["abc"].size(), 1);
|
||||
EXPECT_EQ(result["def"].size(), 1);
|
||||
EXPECT_EQ(result["ghi"].size(), 1);
|
||||
}
|
||||
|
||||
TEST(UnorderedContainers, GroupAnagramsAllSame) {
|
||||
auto result = stl_containers::group_anagrams({"abc", "bca", "cab"});
|
||||
ASSERT_EQ(result.size(), 1);
|
||||
auto& group = result["abc"];
|
||||
std::ranges::sort(group);
|
||||
EXPECT_EQ(group, (std::vector<std::string>{"abc", "bca", "cab"}));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME sort_and_find MODULE 08_stl_algorithms STANDARD 17
|
||||
add_course_exercise(NAME sort_and_find MODULE 08_stl_algorithms STANDARD 26
|
||||
SOURCES src/sort_and_find.cpp test/sort_and_find_test.cpp)
|
||||
add_course_exercise(NAME transform_reduce MODULE 08_stl_algorithms STANDARD 17
|
||||
add_course_exercise(NAME transform_reduce MODULE 08_stl_algorithms STANDARD 26
|
||||
SOURCES src/transform_reduce.cpp test/transform_reduce_test.cpp)
|
||||
add_course_exercise(NAME iterators MODULE 08_stl_algorithms STANDARD 17
|
||||
add_course_exercise(NAME iterators MODULE 08_stl_algorithms STANDARD 26
|
||||
SOURCES src/iterators.cpp test/iterators_test.cpp)
|
||||
add_course_exercise(NAME algorithm_lambdas MODULE 08_stl_algorithms STANDARD 17
|
||||
add_course_exercise(NAME algorithm_lambdas MODULE 08_stl_algorithms STANDARD 26
|
||||
SOURCES src/algorithm_lambdas.cpp test/algorithm_lambdas_test.cpp)
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
#include "algorithm_lambdas.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace stl_algorithms {
|
||||
|
||||
auto filter_length(const std::vector<std::string>& words, std::size_t min_len)
|
||||
-> std::vector<std::string> {
|
||||
(void)words; (void)min_len;
|
||||
return {};
|
||||
const auto is_valid = [min_len](const auto& word) { return word.size() >= min_len; };
|
||||
auto r = std::vector<std::string>();
|
||||
std::ranges::copy_if(words,std::back_inserter(r), is_valid);
|
||||
return r;
|
||||
}
|
||||
|
||||
auto map_to_lengths(const std::vector<std::string>& words) -> std::vector<int> {
|
||||
(void)words;
|
||||
return {};
|
||||
auto r = std::vector<int>();
|
||||
for (const auto& word : words) {
|
||||
r.push_back(static_cast<int>(word.size()));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
auto first_match(const std::vector<int>& data, int threshold) -> int {
|
||||
(void)data; (void)threshold;
|
||||
auto first_match(const std::vector<int>& data, const int threshold) -> int {
|
||||
for (const auto& value : data) {
|
||||
if (value >= threshold) return value;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
#include "iterators.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
namespace stl_algorithms {
|
||||
|
||||
auto reverse_copy(const std::vector<int>& data) -> std::vector<int> { (void)data; return {}; }
|
||||
auto countGreaterThan(const std::vector<int>& data, int threshold) -> int { (void)data; (void)threshold; return 0; }
|
||||
auto everyEven(const std::vector<int>& data) -> bool { (void)data; return false; }
|
||||
auto reverse_copy(const std::vector<int>& data) -> std::vector<int> {
|
||||
std::vector<int> result;
|
||||
for (const auto& element : data | std::views::reverse) {
|
||||
result.push_back(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto countGreaterThan(const std::vector<int>& data, const int threshold) -> int {
|
||||
auto count = 0;
|
||||
for (const auto& element : data) {
|
||||
if (element > threshold) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
auto everyEven(const std::vector<int>& data) -> bool {
|
||||
return std::ranges::all_of(data, [](const int x) { return x % 2 == 0; });
|
||||
}
|
||||
|
||||
} // namespace stl_algorithms
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
#include "sort_and_find.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace stl_algorithms {
|
||||
|
||||
auto sorted_copy(std::vector<int> data) -> std::vector<int> { (void)data; return {}; }
|
||||
auto contains(const std::vector<int>& data, int value) -> bool { (void)data; (void)value; return false; }
|
||||
auto lower_bound_index(const std::vector<int>& sorted, int value) -> int { (void)sorted; (void)value; return -1; }
|
||||
auto sorted_copy(std::vector<int> data) -> std::vector<int> {
|
||||
std::ranges::sort(data);
|
||||
return data;
|
||||
}
|
||||
auto contains(const std::vector<int>& data, const int value) -> bool {
|
||||
return std::ranges::find(data, value) != data.end();
|
||||
}
|
||||
|
||||
auto lower_bound_index(const std::vector<int>& sorted, const int value) -> int {
|
||||
return static_cast<int>(std::ranges::lower_bound(sorted, value) - sorted.begin());
|
||||
}
|
||||
|
||||
} // namespace stl_algorithms
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
#include "transform_reduce.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace stl_algorithms {
|
||||
|
||||
auto square_all(const std::vector<int>& data) -> std::vector<int> { (void)data; return {}; }
|
||||
auto sum_all(const std::vector<int>& data) -> int { (void)data; return 0; }
|
||||
auto product_positive(const std::vector<int>& data) -> long long { (void)data; return 0; }
|
||||
auto square_all(const std::vector<int>& data) -> std::vector<int> {
|
||||
auto result = std::vector<int>{};
|
||||
result.reserve(data.size());
|
||||
std::ranges::transform(data, std::back_inserter(result), [](const int value) { return value * value; });
|
||||
return result;
|
||||
}
|
||||
auto sum_all(const std::vector<int>& data) -> int {
|
||||
return std::ranges::fold_left(data, 0, std::plus<int>{});
|
||||
}
|
||||
auto product_positive(const std::vector<int>& data) -> long long {
|
||||
return std::ranges::fold_left(data, 1, [](const int accumulator, const int value) { return value > 0 ? value * accumulator : accumulator; });
|
||||
}
|
||||
|
||||
} // namespace stl_algorithms
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME auto_and_range_for MODULE 09_cpp11 STANDARD 11
|
||||
add_course_exercise(NAME auto_and_range_for MODULE 09_cpp11 STANDARD 26
|
||||
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
|
||||
add_course_exercise(NAME smart_pointers MODULE 09_cpp11 STANDARD 26
|
||||
SOURCES src/smart_pointers.cpp test/smart_pointers_test.cpp)
|
||||
add_course_exercise(NAME move_semantics MODULE 09_cpp11 STANDARD 11
|
||||
add_course_exercise(NAME move_semantics MODULE 09_cpp11 STANDARD 26
|
||||
SOURCES src/move_semantics.cpp test/move_semantics_test.cpp)
|
||||
add_course_exercise(NAME constexpr_nullptr MODULE 09_cpp11 STANDARD 11
|
||||
add_course_exercise(NAME constexpr_nullptr MODULE 09_cpp11 STANDARD 26
|
||||
SOURCES src/constexpr_nullptr.cpp test/constexpr_nullptr_test.cpp)
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace cpp11 {
|
||||
enum class Color { Red, Green, Blue };
|
||||
|
||||
[[nodiscard]] auto to_index(Color color) -> int;
|
||||
[[nodiscard]] auto square(int value) -> int;
|
||||
[[nodiscard]] constexpr auto square(const int value) -> int { return value * value; }
|
||||
[[nodiscard]] auto find_null(int* ptr) -> int*;
|
||||
|
||||
} // namespace cpp11
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
#include "auto_and_range_for.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace cpp11 {
|
||||
|
||||
auto double_values(const std::vector<int>& input) -> std::vector<int> {
|
||||
(void)input;
|
||||
return {};
|
||||
auto result = std::vector<int>{};
|
||||
result.reserve(input.size());
|
||||
std::ranges::for_each(input, [&result](const int value) { result.push_back(value * 2); });
|
||||
return result;
|
||||
}
|
||||
|
||||
auto join_with_auto(const std::vector<std::string>& parts) -> std::string {
|
||||
(void)parts;
|
||||
return {};
|
||||
auto result = std::string{};
|
||||
std::ranges::for_each(parts, [&result](const std::string& part) { result += part; });
|
||||
return result;
|
||||
}
|
||||
|
||||
auto count_if_positive(const std::vector<int>& input) -> int {
|
||||
(void)input;
|
||||
return 0;
|
||||
auto count = 0;
|
||||
std::ranges::for_each(input, [&count](const int value) { if (value > 0) ++count; });
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace cpp11
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace cpp11 {
|
||||
|
||||
auto to_index(Color color) -> int {
|
||||
auto to_index(const Color color) -> int {
|
||||
switch (color) {
|
||||
case Color::Red: return 0;
|
||||
case Color::Green: return 1;
|
||||
@@ -11,14 +11,8 @@ auto to_index(Color color) -> int {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto square(int value) -> int {
|
||||
(void)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto find_null(int* ptr) -> int* {
|
||||
(void)ptr;
|
||||
return nullptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
} // namespace cpp11
|
||||
|
||||
@@ -17,8 +17,7 @@ auto MovableBuffer::size() const -> std::size_t { return data_.size(); }
|
||||
auto MovableBuffer::data() const -> const std::vector<int>& { return data_; }
|
||||
|
||||
auto consume(MovableBuffer buffer) -> std::size_t {
|
||||
(void)buffer;
|
||||
return 0;
|
||||
return buffer.size();
|
||||
}
|
||||
|
||||
} // namespace cpp11
|
||||
|
||||
@@ -3,18 +3,19 @@
|
||||
namespace cpp11 {
|
||||
|
||||
auto make_counter(int start) -> std::unique_ptr<int> {
|
||||
(void)start;
|
||||
return nullptr;
|
||||
return std::make_unique<int>(start);
|
||||
}
|
||||
|
||||
auto shared_total(const std::vector<std::shared_ptr<int>>& values) -> int {
|
||||
(void)values;
|
||||
return 0;
|
||||
int total = 0;
|
||||
for (const auto& value : values) {
|
||||
total += *value;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
auto clone_unique(const std::unique_ptr<int>& value) -> std::unique_ptr<int> {
|
||||
(void)value;
|
||||
return nullptr;
|
||||
return std::make_unique<int>(*value);
|
||||
}
|
||||
|
||||
} // namespace cpp11
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME generic_lambdas MODULE 10_cpp14 STANDARD 14
|
||||
add_course_exercise(NAME generic_lambdas MODULE 10_cpp14 STANDARD 26
|
||||
SOURCES src/generic_lambdas.cpp test/generic_lambdas_test.cpp)
|
||||
add_course_exercise(NAME auto_return MODULE 10_cpp14 STANDARD 14
|
||||
add_course_exercise(NAME auto_return MODULE 10_cpp14 STANDARD 26
|
||||
SOURCES src/auto_return.cpp test/auto_return_test.cpp)
|
||||
add_course_exercise(NAME make_unique MODULE 10_cpp14 STANDARD 14
|
||||
add_course_exercise(NAME make_unique MODULE 10_cpp14 STANDARD 26
|
||||
SOURCES src/make_unique.cpp test/make_unique_test.cpp)
|
||||
add_course_exercise(NAME digit_separators MODULE 10_cpp14 STANDARD 14
|
||||
add_course_exercise(NAME digit_separators MODULE 10_cpp14 STANDARD 26
|
||||
SOURCES src/digit_separators.cpp test/digit_separators_test.cpp)
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
namespace cpp14 {
|
||||
|
||||
[[nodiscard]] inline auto copy_or_reference(const std::vector<int>& input) -> decltype(auto) {
|
||||
(void)input;
|
||||
static std::vector<int> empty;
|
||||
return empty;
|
||||
return input;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto identity(int value) -> decltype(auto) { return value; }
|
||||
|
||||
@@ -3,18 +3,20 @@
|
||||
namespace cpp14 {
|
||||
|
||||
auto million() -> int {
|
||||
// TODO: Return 1'000'000 using digit separators
|
||||
return 0;
|
||||
return 1'000'000;
|
||||
}
|
||||
|
||||
auto mask() -> std::uint32_t {
|
||||
// TODO: Return 0xFF00'00FF
|
||||
return 0;
|
||||
return 0xFF00'00FF;
|
||||
}
|
||||
|
||||
auto bits_set() -> int {
|
||||
// TODO: Return population count of mask()
|
||||
return 0;
|
||||
const auto m = mask();
|
||||
auto count = 0;
|
||||
for (auto i = 0; i < 32; i++) {
|
||||
if (m & (1 << i)) ++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace cpp14
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
#include "generic_lambdas.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace cpp14 {
|
||||
|
||||
auto apply_twice(const std::vector<int>& values) -> std::vector<int> {
|
||||
(void)values;
|
||||
return {};
|
||||
auto r = std::vector<int>(values.size());
|
||||
std::ranges::transform(values, r.begin(), [](auto x) { return x * 2; });
|
||||
return r;
|
||||
}
|
||||
|
||||
auto concat_any(const std::vector<std::string>& a, const std::vector<std::string>& b)
|
||||
-> std::vector<std::string> {
|
||||
(void)a; (void)b;
|
||||
return {};
|
||||
auto r = std::vector<std::string>(a.size() + b.size());
|
||||
std::ranges::transform(a, r.begin(), [](auto x) { return x; });
|
||||
std::ranges::transform(b, r.begin() + a.size(), [](auto x) { return x; });
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace cpp14
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
#include "make_unique.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace cpp14 {
|
||||
|
||||
auto make_task(std::string name, int priority) -> std::unique_ptr<Task> {
|
||||
(void)name; (void)priority;
|
||||
return nullptr;
|
||||
return std::make_unique<Task>(name, priority);
|
||||
}
|
||||
|
||||
auto clone_task(const Task& task) -> std::unique_ptr<Task> {
|
||||
(void)task;
|
||||
return nullptr;
|
||||
return std::make_unique<Task>(task);
|
||||
}
|
||||
|
||||
auto total_priority(const std::vector<std::unique_ptr<Task>>& tasks) -> int {
|
||||
(void)tasks;
|
||||
return 0;
|
||||
return std::ranges::fold_left(tasks, 0, [](int acc, const auto& x) { return acc + x->priority; });
|
||||
}
|
||||
|
||||
} // namespace cpp14
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME structured_bindings MODULE 11_cpp17 STANDARD 17
|
||||
add_course_exercise(NAME structured_bindings MODULE 11_cpp17 STANDARD 26
|
||||
SOURCES src/structured_bindings.cpp test/structured_bindings_test.cpp)
|
||||
add_course_exercise(NAME optional MODULE 11_cpp17 STANDARD 17
|
||||
add_course_exercise(NAME optional MODULE 11_cpp17 STANDARD 26
|
||||
SOURCES src/optional.cpp test/optional_test.cpp)
|
||||
add_course_exercise(NAME variant MODULE 11_cpp17 STANDARD 17
|
||||
add_course_exercise(NAME variant MODULE 11_cpp17 STANDARD 26
|
||||
SOURCES src/variant.cpp test/variant_test.cpp)
|
||||
add_course_exercise(NAME if_constexpr MODULE 11_cpp17 STANDARD 17
|
||||
add_course_exercise(NAME if_constexpr MODULE 11_cpp17 STANDARD 26
|
||||
SOURCES src/if_constexpr.cpp test/if_constexpr_test.cpp)
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
namespace cpp17 {
|
||||
|
||||
auto stringify_ints(const std::vector<int>& data) -> std::string {
|
||||
(void)data;
|
||||
return {};
|
||||
auto oss = std::stringstream();
|
||||
for (auto i = 0uz; i < data.size(); ++i) {
|
||||
if (i > 0) oss << ",";
|
||||
oss << data[i];
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // namespace cpp17
|
||||
|
||||
@@ -2,19 +2,28 @@
|
||||
|
||||
namespace cpp17 {
|
||||
|
||||
auto safe_divide(int lhs, int rhs) -> std::optional<double> {
|
||||
(void)lhs; (void)rhs;
|
||||
auto safe_divide(const int lhs, const int rhs) -> std::optional<double> {
|
||||
if (rhs == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::optional(lhs / static_cast<double>(rhs));
|
||||
}
|
||||
|
||||
auto find_user(const std::vector<std::string>& users, const std::string& name)
|
||||
-> std::optional<std::size_t> {
|
||||
(void)users; (void)name;
|
||||
const auto idx = std::ranges::find(users, name);
|
||||
if (idx == users.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::optional<std::size_t>(std::distance(users.begin(), idx));
|
||||
}
|
||||
|
||||
auto first_positive(const std::vector<int>& data) -> std::optional<int> {
|
||||
(void)data;
|
||||
for (const auto& elem : data) {
|
||||
if (elem > 0) {
|
||||
return std::optional(elem);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
#include "structured_bindings.hpp"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
namespace cpp17 {
|
||||
|
||||
auto minmax_pair(const std::vector<int>& data) -> std::pair<int, int> {
|
||||
(void)data;
|
||||
return {0, 0};
|
||||
auto min = std::numeric_limits<int>::max();
|
||||
auto max = std::numeric_limits<int>::min();
|
||||
for (const auto i : data) {
|
||||
if (i < min) min = i;
|
||||
if (i > max) max = i;
|
||||
}
|
||||
return {min, max};
|
||||
}
|
||||
|
||||
auto split_key_value(const std::string& text) -> std::pair<std::string, std::string> {
|
||||
(void)text;
|
||||
return {"", ""};
|
||||
auto r = std::views::split(text, '=');
|
||||
auto it = r.begin();
|
||||
auto key = *it | std::ranges::to<std::string>();
|
||||
++it;
|
||||
auto value = *it | std::ranges::to<std::string>();
|
||||
return {key, value};
|
||||
}
|
||||
|
||||
auto first_pair(const std::map<std::string, int>& scores) -> std::tuple<std::string, int, bool> {
|
||||
(void)scores;
|
||||
return {"", 0, false};
|
||||
if (scores.empty()) return {"", 0, false};
|
||||
const auto& [name, score] = *scores.begin();
|
||||
return {name, score, true};
|
||||
}
|
||||
|
||||
} // namespace cpp17
|
||||
|
||||
@@ -3,18 +3,32 @@
|
||||
namespace cpp17 {
|
||||
|
||||
auto variant_to_string(const Value& value) -> std::string {
|
||||
(void)value;
|
||||
if (std::holds_alternative<int>(value)) {
|
||||
return "int:" + std::to_string(std::get<int>(value));
|
||||
}
|
||||
if (std::holds_alternative<double>(value)) {
|
||||
return "double:" + std::to_string(std::get<double>(value));
|
||||
}
|
||||
if (std::holds_alternative<std::string>(value)) {
|
||||
return "string:" +std::get<std::string>(value);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto sum_numeric_variants(const std::vector<Value>& values) -> double {
|
||||
(void)values;
|
||||
return 0.0;
|
||||
auto sum = 0.0;
|
||||
for (const auto& value : values) {
|
||||
if (std::holds_alternative<int>(value)) {
|
||||
sum += static_cast<double>(std::get<int>(value));
|
||||
} else if (std::holds_alternative<double>(value)) {
|
||||
sum += std::get<double>(value);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
auto is_string(const Value& value) -> bool {
|
||||
(void)value;
|
||||
return false;
|
||||
return std::holds_alternative<std::string>(value);
|
||||
}
|
||||
|
||||
} // namespace cpp17
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME concepts MODULE 12_cpp20 STANDARD 20
|
||||
add_course_exercise(NAME concepts MODULE 12_cpp20 STANDARD 26
|
||||
SOURCES src/concepts.cpp test/concepts_test.cpp)
|
||||
add_course_exercise(NAME ranges MODULE 12_cpp20 STANDARD 20
|
||||
add_course_exercise(NAME ranges MODULE 12_cpp20 STANDARD 26
|
||||
SOURCES src/ranges.cpp test/ranges_test.cpp)
|
||||
add_course_exercise(NAME spaceship_operator MODULE 12_cpp20 STANDARD 20
|
||||
add_course_exercise(NAME spaceship_operator MODULE 12_cpp20 STANDARD 26
|
||||
SOURCES src/spaceship_operator.cpp test/spaceship_operator_test.cpp)
|
||||
add_course_exercise(NAME span MODULE 12_cpp20 STANDARD 20
|
||||
add_course_exercise(NAME span MODULE 12_cpp20 STANDARD 26
|
||||
SOURCES src/span.cpp test/span_test.cpp)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <concepts>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -10,14 +11,13 @@ namespace cpp20 {
|
||||
|
||||
template <std::integral T>
|
||||
[[nodiscard]] auto double_value(T value) -> T {
|
||||
(void)value;
|
||||
return T{};
|
||||
return value * 2;
|
||||
}
|
||||
|
||||
template <std::floating_point T>
|
||||
[[nodiscard]] auto clamp01(T value) -> T {
|
||||
(void)value;
|
||||
return T{};
|
||||
// Converts the 0 and 1 to the right type, which fulfills the std::floating_point concept
|
||||
return std::clamp(value, T{0}, T{1});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -28,10 +28,17 @@ concept StringLike = requires(T value) {
|
||||
|
||||
template <StringLike T>
|
||||
[[nodiscard]] auto uppercase_copy(const T& text) -> std::string {
|
||||
(void)text;
|
||||
return {};
|
||||
auto oss = std::ostringstream{};
|
||||
for (auto idx = 0uz; idx < text.size(); idx++) {
|
||||
oss << static_cast<char>(std::toupper(text.data()[idx]));
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto sum_integral(const std::vector<int>& data) -> int;
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
[[nodiscard]] auto sum_integral(const std::vector<T>& data) -> T {
|
||||
return std::ranges::fold_left(data, T{0}, std::plus{});
|
||||
}
|
||||
|
||||
} // namespace cpp20
|
||||
|
||||
@@ -2,9 +2,5 @@
|
||||
|
||||
namespace cpp20 {
|
||||
|
||||
auto sum_integral(const std::vector<int>& data) -> int {
|
||||
(void)data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace cpp20
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
namespace cpp20 {
|
||||
|
||||
auto even_numbers(const std::vector<int>& input) -> std::vector<int> {
|
||||
(void)input;
|
||||
return {};
|
||||
return std::ranges::filter_view(input, [](auto x) { return x % 2 == 0; })
|
||||
| std::ranges::to<std::vector<int>>();
|
||||
}
|
||||
|
||||
auto take_first_three(const std::vector<int>& input) -> std::vector<int> {
|
||||
(void)input;
|
||||
return {};
|
||||
return std::ranges::take_view(input, 3)
|
||||
| std::ranges::to<std::vector<int>>();
|
||||
}
|
||||
|
||||
auto join_strings(const std::vector<std::string>& parts) -> std::string {
|
||||
(void)parts;
|
||||
return {};
|
||||
return std::ranges::join_view(parts)
|
||||
| std::ranges::to<std::string>();
|
||||
}
|
||||
|
||||
} // namespace cpp20
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#include "spaceship_operator.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
namespace cpp20 {
|
||||
|
||||
auto sort_versions(std::vector<Version> versions) -> std::vector<Version> {
|
||||
(void)versions;
|
||||
return {};
|
||||
std::ranges::sort(versions);
|
||||
return versions;
|
||||
}
|
||||
|
||||
auto is_sorted_versions(const std::vector<Version>& versions) -> bool {
|
||||
(void)versions;
|
||||
return false;
|
||||
return std::ranges::is_sorted(versions);
|
||||
}
|
||||
|
||||
} // namespace cpp20
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
#include "span.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
namespace cpp20 {
|
||||
|
||||
auto span_sum(std::span<const int> data) -> int {
|
||||
(void)data;
|
||||
return 0;
|
||||
return std::ranges::fold_left(data, 0, std::plus<>{});
|
||||
}
|
||||
|
||||
auto first_and_last(std::span<const int> data) -> std::pair<int, int> {
|
||||
(void)data;
|
||||
return {0, 0};
|
||||
return {*data.begin(), *(--data.end())};
|
||||
}
|
||||
|
||||
auto make_subspan(std::span<const int> data, std::size_t offset, std::size_t count)
|
||||
auto make_subspan(std::span<const int> data, const std::size_t offset, std::size_t count)
|
||||
-> std::vector<int> {
|
||||
(void)data; (void)offset; (void)count;
|
||||
return {};
|
||||
return std::views::drop(data, offset)
|
||||
| std::views::take(count)
|
||||
| std::ranges::to<std::vector<int>>();
|
||||
}
|
||||
|
||||
} // namespace cpp20
|
||||
|
||||
@@ -15,5 +15,5 @@ TEST(Concepts, UppercaseCopy) {
|
||||
}
|
||||
|
||||
TEST(Concepts, SumIntegral) {
|
||||
EXPECT_EQ(cpp20::sum_integral({1, 2, 3}), 6);
|
||||
EXPECT_EQ(cpp20::sum_integral<int>({1, 2, 3}), 6);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME expected MODULE 13_cpp23 STANDARD 23
|
||||
add_course_exercise(NAME expected MODULE 13_cpp23 STANDARD 26
|
||||
SOURCES src/expected.cpp test/expected_test.cpp)
|
||||
add_course_exercise(NAME print_and_format MODULE 13_cpp23 STANDARD 23
|
||||
add_course_exercise(NAME print_and_format MODULE 13_cpp23 STANDARD 26
|
||||
SOURCES src/print_and_format.cpp test/print_and_format_test.cpp)
|
||||
add_course_exercise(NAME mdspan MODULE 13_cpp23 STANDARD 23
|
||||
add_course_exercise(NAME mdspan MODULE 13_cpp23 STANDARD 26
|
||||
SOURCES src/mdspan.cpp test/mdspan_test.cpp)
|
||||
add_course_exercise(NAME ranges_zip MODULE 13_cpp23 STANDARD 23
|
||||
add_course_exercise(NAME ranges_zip MODULE 13_cpp23 STANDARD 26
|
||||
SOURCES src/ranges_zip.cpp test/ranges_zip_test.cpp)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
#include "expected.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace cpp23 {
|
||||
|
||||
auto parse_int(const std::string& text) -> std::expected<int, ParseError> {
|
||||
(void)text;
|
||||
if (text.empty()) {
|
||||
return std::unexpected(ParseError::EmptyInput);
|
||||
}
|
||||
try {
|
||||
const auto i = std::stoi(text);
|
||||
return i;
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
return std::unexpected(ParseError::InvalidNumber);
|
||||
}
|
||||
catch (const std::out_of_range&) {
|
||||
return std::unexpected(ParseError::InvalidNumber);
|
||||
}
|
||||
}
|
||||
|
||||
auto safe_sqrt(int value) -> std::expected<double, std::string> {
|
||||
(void)value;
|
||||
auto safe_sqrt(const int value) -> std::expected<double, std::string> {
|
||||
if (value < 0) {
|
||||
return std::unexpected(std::string{"negative"});
|
||||
}
|
||||
return std::sqrt(value);
|
||||
}
|
||||
|
||||
auto chain_parse_and_double(const std::string& text) -> std::expected<int, ParseError> {
|
||||
(void)text;
|
||||
return std::unexpected(ParseError::InvalidNumber);
|
||||
return parse_int(text).transform([](const auto i) { return i * 2; });
|
||||
}
|
||||
|
||||
} // namespace cpp23
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
|
||||
namespace cpp23 {
|
||||
|
||||
auto make_row_major_matrix(int rows, int cols, int fill) -> std::vector<int> {
|
||||
(void)rows; (void)cols; (void)fill;
|
||||
return {};
|
||||
auto make_row_major_matrix(const int rows, const int cols, const int fill) -> std::vector<int> {
|
||||
return std::vector(static_cast<size_t>(rows * cols), fill);
|
||||
}
|
||||
|
||||
auto matrix_element(
|
||||
std::mdspan<const int, std::extents<int, std::dynamic_extent, std::dynamic_extent>> matrix,
|
||||
int row, int col) -> int {
|
||||
(void)matrix; (void)row; (void)col;
|
||||
return 0;
|
||||
auto matrix_element(const std::mdspan<const int, std::extents<int, std::dynamic_extent, std::dynamic_extent>> matrix, const int row, const int col) -> int {
|
||||
return matrix[row, col];
|
||||
}
|
||||
|
||||
auto row_sums(
|
||||
std::mdspan<const int, std::extents<int, std::dynamic_extent, std::dynamic_extent>> matrix)
|
||||
-> std::vector<int> {
|
||||
(void)matrix;
|
||||
return {};
|
||||
auto row_sums(const std::mdspan<const int, std::extents<int, std::dynamic_extent, std::dynamic_extent>> matrix) -> std::vector<int> {
|
||||
auto sums = std::vector<int>(matrix.extent(0));
|
||||
for (auto i = 0; i < matrix.extent(0); ++i) {
|
||||
for (auto j = 0; j < matrix.extent(1); ++j) {
|
||||
sums[i] += matrix[i, j];
|
||||
}
|
||||
}
|
||||
return sums;
|
||||
}
|
||||
|
||||
} // namespace cpp23
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
#include "print_and_format.hpp"
|
||||
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
|
||||
namespace cpp23 {
|
||||
|
||||
auto format_greeting(const std::string& name, int score) -> std::string {
|
||||
(void)name; (void)score;
|
||||
return {};
|
||||
auto format_greeting(const std::string& name, const int score) -> std::string {
|
||||
return std::format("{} scored {} points", name, score)
|
||||
| std::ranges::to<std::string>();
|
||||
}
|
||||
|
||||
auto format_table_row(const std::vector<std::string>& columns) -> std::string {
|
||||
(void)columns;
|
||||
return {};
|
||||
return columns
|
||||
| std::views::join_with(std::string_view{" | "})
|
||||
| std::ranges::to<std::string>();
|
||||
}
|
||||
|
||||
auto format_hex(std::uint32_t value) -> std::string {
|
||||
(void)value;
|
||||
return {};
|
||||
auto format_hex(const std::uint32_t value) -> std::string {
|
||||
return std::format("0x{:X}", value)
|
||||
| std::ranges::to<std::string>();
|
||||
}
|
||||
|
||||
} // namespace cpp23
|
||||
|
||||
@@ -3,19 +3,23 @@
|
||||
namespace cpp23 {
|
||||
|
||||
auto zip_sum(const std::vector<int>& a, const std::vector<int>& b) -> std::vector<int> {
|
||||
(void)a; (void)b;
|
||||
return {};
|
||||
return std::views::zip_transform(std::plus{}, a, b)
|
||||
| std::ranges::to<std::vector>();
|
||||
}
|
||||
|
||||
auto chunk_strings(const std::vector<std::string>& words, std::size_t chunk_size)
|
||||
-> std::vector<std::vector<std::string>> {
|
||||
(void)words; (void)chunk_size;
|
||||
return {};
|
||||
auto chunk_strings(const std::vector<std::string>& words, const std::size_t chunk_size) -> std::vector<std::vector<std::string>> {
|
||||
return words
|
||||
| std::views::chunk(chunk_size)
|
||||
| std::views::transform([](const auto& chunk) {
|
||||
return chunk | std::ranges::to<std::vector<std::string>>();
|
||||
})
|
||||
| std::ranges::to<std::vector<std::vector<std::string>>>();
|
||||
}
|
||||
|
||||
auto adjacent_differences(const std::vector<int>& data) -> std::vector<int> {
|
||||
(void)data;
|
||||
return {};
|
||||
return data
|
||||
| std::views::adjacent_transform<2>([](const int x, const int y) { return y - x; })
|
||||
| std::ranges::to<std::vector<int>>();
|
||||
}
|
||||
|
||||
} // namespace cpp23
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_course_exercise(NAME constexpr_frontier MODULE 14_cpp26 STANDARD 23
|
||||
add_course_exercise(NAME constexpr_frontier MODULE 14_cpp26 STANDARD 26
|
||||
SOURCES src/constexpr_frontier.cpp test/constexpr_frontier_test.cpp)
|
||||
add_course_exercise(NAME monadic_expected MODULE 14_cpp26 STANDARD 23
|
||||
add_course_exercise(NAME monadic_expected MODULE 14_cpp26 STANDARD 26
|
||||
SOURCES src/monadic_expected.cpp test/monadic_expected_test.cpp)
|
||||
add_course_exercise(NAME pipeline_ranges MODULE 14_cpp26 STANDARD 23
|
||||
add_course_exercise(NAME pipeline_ranges MODULE 14_cpp26 STANDARD 26
|
||||
SOURCES src/pipeline_ranges.cpp test/pipeline_ranges_test.cpp)
|
||||
add_course_exercise(NAME custom_formatter MODULE 14_cpp26 STANDARD 23
|
||||
add_course_exercise(NAME custom_formatter MODULE 14_cpp26 STANDARD 26
|
||||
SOURCES src/custom_formatter.cpp test/custom_formatter_test.cpp)
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
namespace cpp26 {
|
||||
|
||||
[[nodiscard]] auto compile_time_factorial(int n) -> long long;
|
||||
[[nodiscard]] auto compile_time_sum(std::array<int, 5> values) -> int;
|
||||
[[nodiscard]] auto is_sorted(std::array<int, 5> values) -> bool;
|
||||
[[nodiscard]] constexpr auto compile_time_factorial(int n) -> long long {
|
||||
auto result = 1LL;
|
||||
for (auto i = 2; i <= n; ++i) {
|
||||
result *= i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
[[nodiscard]] constexpr auto compile_time_sum(std::array<int, 5> values) -> int {
|
||||
return std::ranges::fold_left(values, 0, std::plus{});
|
||||
}
|
||||
[[nodiscard]] constexpr auto is_sorted(const std::array<int, 5> &values) -> bool {
|
||||
return std::ranges::is_sorted(values); // also a constexpr function
|
||||
|
||||
}
|
||||
|
||||
} // namespace cpp26
|
||||
@@ -1,20 +1,6 @@
|
||||
#include "constexpr_frontier.hpp"
|
||||
|
||||
namespace cpp26 {
|
||||
|
||||
auto compile_time_factorial(int n) -> long long {
|
||||
(void)n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto compile_time_sum(std::array<int, 5> values) -> int {
|
||||
(void)values;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto is_sorted(std::array<int, 5> values) -> bool {
|
||||
(void)values;
|
||||
return false;
|
||||
}
|
||||
// constexpr functions should be defined in the header file
|
||||
|
||||
} // namespace cpp26
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
namespace cpp26 {
|
||||
|
||||
auto format_point(const Point& point) -> std::string {
|
||||
(void)point;
|
||||
return {};
|
||||
return std::format("{}", point);
|
||||
}
|
||||
|
||||
auto format_points(const Point& a, const Point& b) -> std::string {
|
||||
(void)a; (void)b;
|
||||
return {};
|
||||
return std::format("{} -> {}", a, b);
|
||||
}
|
||||
|
||||
} // namespace cpp26
|
||||
|
||||
@@ -3,18 +3,23 @@
|
||||
namespace cpp26 {
|
||||
|
||||
auto parse_id(const std::string& text) -> std::expected<int, ErrorCode> {
|
||||
(void)text;
|
||||
int id = 0;
|
||||
if (auto [ptr, ec] = std::from_chars(text.data(), text.data() + text.size(), id);
|
||||
ec != std::errc{} || ptr != text.data() + text.size()) {
|
||||
return std::unexpected(ErrorCode::Invalid);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
auto load_user(int id) -> std::expected<std::string, ErrorCode> {
|
||||
(void)id;
|
||||
auto load_user(const int id) -> std::expected<std::string, ErrorCode> {
|
||||
if (id == 999) {
|
||||
return std::unexpected(ErrorCode::NotFound);
|
||||
}
|
||||
return "User-" + std::to_string(id);
|
||||
}
|
||||
|
||||
auto load_user_name(const std::string& text) -> std::expected<std::string, ErrorCode> {
|
||||
(void)text;
|
||||
return std::unexpected(ErrorCode::Invalid);
|
||||
return parse_id(text).and_then(load_user);
|
||||
}
|
||||
|
||||
} // namespace cpp26
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
#include "pipeline_ranges.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
|
||||
namespace cpp26 {
|
||||
|
||||
auto top_values_by_category(const std::vector<Record>& records, int min_value)
|
||||
-> std::vector<Record> {
|
||||
(void)records; (void)min_value;
|
||||
return {};
|
||||
auto top_values_by_category(const std::vector<Record>& records, const int min_value) -> std::vector<Record> {
|
||||
return records
|
||||
| std::views::filter([min_value](const auto& record) { return record.value > min_value; })
|
||||
| std::ranges::to<std::vector<Record>>();
|
||||
}
|
||||
|
||||
auto total_by_category(const std::vector<Record>& records)
|
||||
-> std::vector<std::pair<std::string, int>> {
|
||||
(void)records;
|
||||
return {};
|
||||
auto total_by_category(const std::vector<Record>& records) -> std::vector<std::pair<std::string, int>> {
|
||||
std::map<std::string, int> grouped;
|
||||
for (const auto&[category, value] : records) {
|
||||
grouped[category] += value;
|
||||
}
|
||||
return grouped
|
||||
| std::ranges::to<std::vector<std::pair<std::string, int>>>();
|
||||
}
|
||||
|
||||
} // namespace cpp26
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
add_library(course_contract_support STATIC contract_handler.cpp)
|
||||
target_compile_options(course_contract_support PUBLIC -std=c++26)
|
||||
set_target_properties(course_contract_support PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND COURSE_ENABLE_CONTRACTS)
|
||||
target_compile_options(course_contract_support PRIVATE -fcontracts)
|
||||
target_link_options(course_contract_support PRIVATE -fcontracts)
|
||||
endif()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user