From ebbd3a8e77a6bf4da571265a9af5b8178f32b626 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 9 Mar 2026 22:07:38 +0700 Subject: [PATCH 01/10] Update doc comment --- src/core/string/BasicString.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/string/BasicString.cpp b/src/core/string/BasicString.cpp index cb05b5f..7a968a3 100644 --- a/src/core/string/BasicString.cpp +++ b/src/core/string/BasicString.cpp @@ -1,4 +1,4 @@ -#include +#include // C-String #include namespace { From d4a317b5e7e28e3b1ee9adac1e1f3d7ddc5505ef Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 9 Mar 2026 22:07:54 +0700 Subject: [PATCH 02/10] Add StdString example --- src/core/CMakeLists.txt | 1 + src/core/string/README.md | 291 +++++++++++++++++++++++++++++++++- src/core/string/StdString.cpp | 175 ++++++++++++++++++++ 3 files changed, 465 insertions(+), 2 deletions(-) create mode 100644 src/core/string/StdString.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1828f8a..4915958 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -42,6 +42,7 @@ set(CORE_SOURCES # String handling ${CMAKE_CURRENT_SOURCE_DIR}/string/StringFormatting.cpp ${CMAKE_CURRENT_SOURCE_DIR}/string/BasicString.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/string/StdString.cpp # Exception handling ${CMAKE_CURRENT_SOURCE_DIR}/exception/BasicHandle.cpp diff --git a/src/core/string/README.md b/src/core/string/README.md index fffa492..45dc79a 100644 --- a/src/core/string/README.md +++ b/src/core/string/README.md @@ -1,8 +1,295 @@ -## String Formatting +# 1. String Formatting | Method | Standard | Pros | Cons | |------------------|----------|--------------------------|------------------------------| | `std::format` | C++20 | Clean, safe, Python-like | Needs newer compiler | | `+` concatenation | C++98 | Simple | Hard to format numbers | | `stringstream` | C++98 | Flexible streaming | Slow, verbose | -| `snprintf` | C | Very fast, classic | Unsafe if misused | \ No newline at end of file +| `snprintf` | C | Very fast, classic | Unsafe if misused | +https://www.geeksforgeeks.org/cpp/strings-in-cpp/ +https://hackingcpp.com/cpp/std/string_basics + +--- + +# 2. Create Strings + +```cpp +std::string s = "hello"; +std::string s2("hello"); +std::string s3(5, 'a'); // "aaaaa" +``` +--- + +# 3. Basic Properties + +| Function | Description | +| ------------ | --------------------- | +| `s.size()` | number of characters | +| `s.length()` | same as size | +| `s.empty()` | check empty | +| `s.clear()` | remove all characters | + +--- + +# 4. Access Characters + +```cpp +s[0] // no bounds check +s.at(0) // bounds check +s.front() // first char +s.back() // last char +``` + +--- + +# 5. Modify String + +## append + +```cpp +s.append(" world"); +s += " world"; +``` + +## insert + +```cpp +s.insert(5, "XXX"); +``` + +## erase + +```cpp +s.erase(0,2); +``` + +## replace + +```cpp +s.replace(0,5,"hi"); +``` + +## remove `\n` + +```cpp + std::string s = "hello\nworld\n"; + auto new_end = std::remove(s.begin(), s.end(), '\n'); + s.erase(new_end) + // std::erase(s,'\n') + s.erase(std::remove(s.begin(), s.end(), '\n'), s.end()); +``` + + +--- + +# 6. Substring + +```cpp +std::string sub = s.substr(pos, len); +``` + +Example: + +```cpp +std::string s = "abcdef"; + +s.substr(2,3); // "cde" +``` + +Important: + +``` +substr() does NOT modify original string +``` + +--- + +# 7. Find / Search + +## find substring + +```cpp +size_t pos = s.find("abc"); + +if (pos != std::string::npos) + std::cout << "found"; +``` + +## find char + +```cpp +s.find('a'); +``` + +## find last + +```cpp +s.rfind("abc"); +``` + +--- + +# 8. Compare Strings + +```cpp +if (a == b) +if (a != b) +if (a < b) +``` + +Using compare: + +```cpp +a.compare(b) +``` + +Return: + +``` +0 -> equal +<0 -> smaller +>0 -> larger +``` + +### C++20 + +```cpp +s.starts_with("abc"); +s.ends_with("xyz"); +``` + +### Before C++20 + +#### starts_with + +```cpp +s.rfind("abc",0) == 0 +``` + +#### ends_with + +```cpp +s.size() >= 3 && +s.compare(s.size()-3,3,"xyz") == 0 +``` + +--- + +# 9. Convert String + +## string → int + +### C++11 + +```cpp +int n = std::stoi(s); +``` + +Other: + +``` +stol +stoll +stof +stod +``` + +Example: + +```cpp +double x = std::stod("3.14"); +``` + +--- + +## number → string + +```cpp +std::string s = std::to_string(123); +``` + +--- + +# 10. String Parsing + +## split using stringstream + +```cpp +#include + +std::string line = "a,b,c"; +std::stringstream ss(line); +std::string item; + +while(std::getline(ss,item,',')) { + std::cout << item << std::endl; +} +``` + +Result + +``` +a +b +c +``` + +--- + +# 11. Trim Whitespace (manual) + +Common pattern: + +```cpp +s.erase(0, s.find_first_not_of(" \t\n\r")); +s.erase(s.find_last_not_of(" \t\n\r") + 1); +``` + +Removes leading/trailing spaces. + +--- + +# 12. Convert Case + +```cpp +#include + +std::transform(s.begin(), s.end(), s.begin(), ::tolower); +std::transform(s.begin(), s.end(), s.begin(), ::toupper); +``` + +--- + +# 20. Functions Modify the String + +Modify: + +``` +append +insert +erase +replace +clear +push_back +pop_back +``` + +Do NOT modify: + +``` +substr +find +compare +size +empty +``` + +Rule: + +``` +method() const -> does NOT modify object +``` + +--- + +# 21. Common Bugs \ No newline at end of file diff --git a/src/core/string/StdString.cpp b/src/core/string/StdString.cpp new file mode 100644 index 0000000..9853701 --- /dev/null +++ b/src/core/string/StdString.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include "ExampleRegistry.h" + +namespace Create { +void run() { + // 1. Direct initialization with literal + std::string s1 = "string 1"; + + // 2. Constructor initialization + std::string s2("string 2"); + + // 3. Repeat character constructor + std::string s3(5, 's'); // "sssss" + + // 4. Concatenated string literals (compile-time) + std::string s4 = + "First " + "Second"; // -> "First Second" + + // 5. Raw string literal (no escaping needed) + std::string s5 = R"(C:\folder\file.txt)"; + + // Print results + std::cout << "s1: " << s1 << '\n'; + std::cout << "s2: " << s2 << '\n'; + std::cout << "s3: " << s3 << '\n'; + std::cout << "s4: " << s4 << '\n'; + std::cout << "s5: " << s5 << '\n'; +} +} // namespace Create + +namespace Modify { +void run() { + std::string ss = "xPhong"; + std::cout << "init : " << ss << '\n'; + + // append + ss.append("Nguyen"); + std::cout << "append : " << ss << '\n'; + + // insert at position + ss.insert(6, "Vanxx"); + std::cout << "insert : " << ss << '\n'; + + // erase (pos, length) + ss.erase(9, 2); + std::cout << "erase : " << ss << '\n'; + + // erase char using pos + auto new_end = std::remove(ss.begin(), ss.end(), 'O'); + ss.erase(new_end, ss.end()); + std::cout << "erase 'O') : " << ss << '\n'; + // #include + // std::erase(ss, '\n'); + + // replace (pos, length, new_string) + ss.replace(0, 1, "My name is "); + std::cout << "replace : " << ss << '\n'; + + // convert case + std::transform(ss.begin(), ss.end(), ss.begin(), ::tolower); + std::cout << "tolower : " << ss << '\n'; + std::transform(ss.begin(), ss.end(), ss.begin(), ::toupper); + std::cout << "toupper : " << ss << '\n'; + + // trim white + ss.erase(0, ss.find_first_not_of(" \t\n\r")); + std::cout << "trim first : " << ss << '\n'; + ss.erase(ss.find_last_not_of(" \t\n\r") + 1); + std::cout << "trim last : " << ss << '\n'; +} +} // namespace Modify + +namespace Sub { +void run() { + std::string ss = "PhongNguyen"; + std::cout << "init : " << ss << '\n'; + std::string firstName = ss.substr(0, 5); + std::cout << "sub : " << firstName << '\n'; +} +} // namespace Sub + +namespace Search { +void run() { + std::string ss = "PhongNguyen"; + std::cout << "init : " << ss << '\n'; + + // find char + size_t pos = ss.find('N'); + if (pos != std::string::npos) { + std::string secondName = ss.substr(pos); // from pos to end (default) + std::cout << "find 'N' : at " << pos << " -> " << secondName << '\n'; + } + + // find substring + pos = ss.find("ong"); + if (pos != std::string::npos) { + std::cout << "find \"ong\" : at " << pos << '\n'; + } + + // find last occurrence + pos = ss.rfind('n'); + if (pos != std::string::npos) { + std::cout << "rfind 'n' : at " << pos << '\n'; + } +} +} // namespace Search + +namespace Compare { +void run() { + std::string ss1 = "PhongNguyen"; + std::string ss2 = "PhongNguyen"; + + int result = ss1.compare(ss2); + + std::cout << "compare result : " << result << '\n'; + std::cout << "equal ? : " << std::boolalpha << (result == 0) << '\n'; +} + +} // namespace Compare + +namespace Convert { +void run() { + // string -> int + std::string sint = "3"; + int ivalue = std::stoi(sint); + std::cout << "stoi : " << ivalue << '\n'; + + // string -> double + std::string sdouble = "3.3"; + double dvalue = std::stod(sdouble); + std::cout << "stod : " << std::setprecision(4) << dvalue << '\n'; + + // number -> string + int value = 999; + std::string svalue = std::to_string(value); + std::cout << "to_string : " << svalue << '\n'; +} +} // namespace Convert + +namespace Parsing { +void run() { + std::string line = "a,b,c"; + std::cout << "init : " << line << '\n'; + char deli = ','; + std::stringstream ss(line); + std::string item; + + std::cout << "Parsing with delimiter ',': \n"; + while (std::getline(ss, item, deli)) { + std::cout << item << std::endl; + } +} +} // namespace Parsing + +class StdString : public IExample { + public: + std::string group() const override { return "core/string"; } + std::string name() const override { return "StdString"; } + std::string description() const override { return "StdString Example"; } + void execute() override { + Create::run(); + Modify::run(); + Sub::run(); + Search::run(); + Compare::run(); + Convert::run(); + Parsing::run(); + } +}; + +REGISTER_EXAMPLE(StdString, "core/string", "StdString"); From 92d3969cbf956d88d4f935b2114bbc0aacef7a41 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Tue, 10 Mar 2026 13:44:22 +0700 Subject: [PATCH 03/10] Update guide for debug --- README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82db833..4b9b922 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 1. Overview **TL;DR** ```bash -./r +./run.sh ``` **Project Structure** @@ -95,6 +95,87 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma * `cpp-lab:latest`: the image you built earlier. * `/bin/bash`: the command to execute inside the container (opens a Bash shell). +### 3.2 Configure C/C++ debugging +```json +// launch.json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug/cpp_lab_project", // path to the executable + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", // working directory + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "preLaunchTask": "CMake Debug Build", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + }, + ] +} + +// tasks.json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "CMake Debug Configure", + "type": "shell", + "command": "cmake", + "args": [ + "-S", + ".", + "-B", + "build/debug", + "-DCMAKE_BUILD_TYPE=Debug" + ] + }, + { + "label": "CMake Debug Build", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "build/debug" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always" + }, + "dependsOn": "CMake Debug Configure" + } + ] +} +``` +- 1. Open project using VSCode +- 2. Install `The C/C++ Extension Pack` +- 3. Install gdb +```bash +$ sudo apt update +$ sudo apt install gdb +``` +- 4. `F5`: Start Debug +- 5. `Ctrl + F5`: Run without debug +- 6. `Ctrl + Shift + B`: Build project (optional) + ## 5. Update Docker Image ```bash # Navigate to the project that contain your Dockerfile From 6baf6691e188905c20cd130bcf5d8ac528d0e462 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Tue, 10 Mar 2026 15:59:30 +0700 Subject: [PATCH 04/10] Add script to generate coverages using gcovr/lcov --- .gitignore | 4 ++- README.md | 27 ++++++++++++------- gen_coverage_gcovr.sh | 46 ++++++++++++++++++++++++++++++++ gen_coverage_lcov.sh | 61 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 10 deletions(-) create mode 100755 gen_coverage_gcovr.sh create mode 100755 gen_coverage_lcov.sh diff --git a/.gitignore b/.gitignore index f9b956b..6488f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ *private* *.vscode *Identifier -*Testing* \ No newline at end of file +*Testing* +coverage_gcovr +coverage_lcov diff --git a/README.md b/README.md index 4b9b922..048437e 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,13 @@ ./run.sh ``` -**Project Structure** +**Project Hierarchy** ``` includes/ → Header files (.h, .hpp) src/ → Source files (.cpp) tests/ → GoogleTest test cases ``` + ## 2. Dependencies Make sure the following tools are installed before building the project: - **g++ / gcc** @@ -36,38 +37,42 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma ### 3.1. Setup the Local Test Environment - **Ubuntu system** * Install `gcc`, `cmake`, `git`, and `pthread` (Skip this step if you already install) - ``` + ```bash $ sudo apt-get update $ sudo apt-get install g++ $ sudo apt-get install lcov $ sudo apt-get install cmake $ sudo apt-get install git + $ sudo apt install valgrind $ sudo apt-get install cppcheck + $ sudo apt-get install -y clang-tidy + $ sudo apt install python3-gcovr ``` * Build the application and the tests - ``` + ```bash $ cd build $ cmake .. $ cmake --build . ``` * Run the application and the test - ``` + ```bash $ ./cpp_lab_project $ ./cpp_lab_project_test ``` * Detect Memory Leak Using [valgrind](https://valgrind.org/) - ``` - $ sudo apt install valgrind + ```bash $ valgrind --leak-check=full -v ./cpp-lab ``` * (Optional) Run static analysis - cppcheck - ``` - $ sudo apt-get install cppcheck + ```bash $ cppcheck "folder" / "file" ``` * (Optional) Run static analysis - clang-tidy + ```bash + $ clang-tidy -p build -header-filter='^src/.*' $(find src -name "*.cpp") ``` - $ sudo apt-get install -y clang-tidy + * (Optional) Run coverage - clang-tidy + ```bash $ clang-tidy -p build -header-filter='^src/.*' $(find src -name "*.cpp") ``` - **Docker** @@ -176,6 +181,10 @@ $ sudo apt install gdb - 5. `Ctrl + F5`: Run without debug - 6. `Ctrl + Shift + B`: Build project (optional) + +### 3.3 Documentation with `doxygen` +TBD - Refer to this [Documentation with doxygen](https://www.labri.fr/perso/fleury/posts/programming/using-cmake-googletests-and-gcovr-in-a-c-project.html#:~:text=of%20the%C2%A0project.-,Documentation%20with%20doxygen,-Code%20embedded%20documentation) + ## 5. Update Docker Image ```bash # Navigate to the project that contain your Dockerfile diff --git a/gen_coverage_gcovr.sh b/gen_coverage_gcovr.sh new file mode 100755 index 0000000..6fda7ab --- /dev/null +++ b/gen_coverage_gcovr.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -e + +# ----------------------------------------------------------------------------- +# Check gcovr installation +# ----------------------------------------------------------------------------- +if ! command -v gcovr >/dev/null 2>&1; then + echo "Error: gcovr is not installed." + echo "Install with: pip install gcovr or sudo apt install gcovr or sudo apt install python3-gcovr" + exit 1 +fi + +# ----------------------------------------------------------------------------- +# Configure project (only if build folder does not exist) +# ----------------------------------------------------------------------------- +if [ ! -d build ]; then + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="--coverage -O0 -g" +fi + +# ----------------------------------------------------------------------------- +# Build project +# ----------------------------------------------------------------------------- +cmake --build build + +# ----------------------------------------------------------------------------- +# Run unit tests +# ----------------------------------------------------------------------------- +ctest --test-dir build --output-on-failure + +# ----------------------------------------------------------------------------- +# Generate coverage report +# ----------------------------------------------------------------------------- +mkdir -p coverage_gcovr + +gcovr -r . build \ + --branches \ + --html \ + --html-details \ + -o coverage_gcovr/index.html + +# ----------------------------------------------------------------------------- +# Open coverage report +# ----------------------------------------------------------------------------- +xdg-open coverage_gcovr/index.html \ No newline at end of file diff --git a/gen_coverage_lcov.sh b/gen_coverage_lcov.sh new file mode 100755 index 0000000..64732d3 --- /dev/null +++ b/gen_coverage_lcov.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -e + +# ----------------------------------------------------------------------------- +# Check lcov installation +# ----------------------------------------------------------------------------- +if ! command -v lcov >/dev/null 2>&1; then + echo "Error: lcov is not installed." + echo "Install with: sudo apt install lcov" + exit 1 +fi + +# ----------------------------------------------------------------------------- +# Configure project (only if build folder does not exist) +# ----------------------------------------------------------------------------- +if [ ! -d build ]; then + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="--coverage -O0 -g" +fi + +# ----------------------------------------------------------------------------- +# Build project +# ----------------------------------------------------------------------------- +cmake --build build + +# ----------------------------------------------------------------------------- +# Reset previous coverage +# ----------------------------------------------------------------------------- +lcov --directory build --zerocounters + +# ----------------------------------------------------------------------------- +# Run unit tests +# ----------------------------------------------------------------------------- +ctest --test-dir build --output-on-failure + +# ----------------------------------------------------------------------------- +# Capture coverage +# ----------------------------------------------------------------------------- +mkdir -p coverage_lcov + +lcov --capture \ + --directory build \ + --ignore-errors mismatch \ + --rc geninfo_unexecuted_blocks=1 \ + --output-file coverage_lcov/coverage.info + +# Remove system headers +lcov --remove coverage_lcov/coverage.info '/usr/*' \ + --output-file coverage_lcov/coverage.info + +# ----------------------------------------------------------------------------- +# Generate HTML report +# ----------------------------------------------------------------------------- +genhtml coverage_lcov/coverage.info \ + --output-directory coverage_lcov + +# ----------------------------------------------------------------------------- +# Open coverage report +# ----------------------------------------------------------------------------- +xdg-open coverage_lcov/index.html \ No newline at end of file From b59412e0f224c98c2f51e593a5ea67dab21970c8 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 11 Mar 2026 09:58:08 +0700 Subject: [PATCH 05/10] Add configs to debug on VS --- .vscode/launch.json | 30 ++++++++++++++++++++++++++++++ .vscode/tasks.json | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..19e8785 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug/cpp_lab_project", // path to the executable + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", // working directory + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "preLaunchTask": "CMake Debug Build", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + }, + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..cb9546c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,34 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "CMake Debug Configure", + "type": "shell", + "command": "cmake", + "args": [ + "-S", + ".", + "-B", + "build/debug", + "-DCMAKE_BUILD_TYPE=Debug" + ] + }, + { + "label": "CMake Debug Build", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "build/debug" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always" + }, + "dependsOn": "CMake Debug Configure" + } + ] +} \ No newline at end of file From ab3c541e67efb2878dd59f55d5e33fb74cdf1afc Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 11 Mar 2026 10:06:11 +0700 Subject: [PATCH 06/10] Update README for debug configurations --- .gitignore | 4 ++- .vscode/launch.json | 2 +- README.md | 87 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 6488f3b..6321306 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ *build *private* -*.vscode +.vscode/ *Identifier *Testing* coverage_gcovr coverage_lcov +!.vscode/launch.json +!.vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 19e8785..5c505cf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "name": "(gdb) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/debug/cpp_lab_project", // path to the executable + "program": "${workspaceFolder}/build/debug/bin/cpp_lab_project", // path to the executable "args": [], "stopAtEntry": true, "cwd": "${workspaceFolder}", // working directory diff --git a/README.md b/README.md index 048437e..6a3c429 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,39 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma * `cpp-lab:latest`: the image you built earlier. * `/bin/bash`: the command to execute inside the container (opens a Bash shell). -### 3.2 Configure C/C++ debugging +### 3.2 Config C/C++ Debugging (VS Code) + +#### 3.2.1. Launch Configuration + +To debug C/C++ projects in VS Code, you must create a *launch configuration* that specifies: + +- The application entry point (executable) +- How to attach to a running process +- Environment variables needed +- Saved debugging setup details + +All launch configs are stored in `.vscode/launch.json` within your workspace. +##### a. Create `launch.json` +- Go to `Run` → `Add Configuration...` or +- Use VS Code chat command: + ``` + /startDebugging + ``` + to auto-generate a debug configuration. + +##### b. Start Debugging + +You can start a debug session in several ways: +- **F5** +- **Ctrl + Shift + D**: Open Debug panel, select a config, press Start +- **Ctrl + Shift + P** → select `Debug: Select and Start Debugging` + +#### 3.2.2. Debug Configuration Example + +The most important field is the executable that will run: + ```json -// launch.json +// .vscode/launch.json { "version": "0.2.0", "configurations": [ @@ -110,31 +140,37 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma "name": "(gdb) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/debug/cpp_lab_project", // path to the executable + "program": "${workspaceFolder}/build/debug/bin/cpp_lab_project", "args": [], "stopAtEntry": true, - "cwd": "${workspaceFolder}", // working directory + "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "preLaunchTask": "CMake Debug Build", "setupCommands": [ { - "description": "Enable pretty-printing for gdb", + "description": "Enable pretty printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true }, { - "description": "Set Disassembly Flavor to Intel", + "description": "Set disassembly flavor to Intel", "text": "-gdb-set disassembly-flavor intel", "ignoreFailures": true } - ], - }, + ] + } ] } +``` + +#### 3.2.3. Build Tasks (CMake) -// tasks.json +An example `tasks.json` for CMake builds: + +```json +// .vscode/tasks.json { "version": "2.0.0", "tasks": [ @@ -170,17 +206,30 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma ] } ``` -- 1. Open project using VSCode -- 2. Install `The C/C++ Extension Pack` -- 3. Install gdb -```bash -$ sudo apt update -$ sudo apt install gdb -``` -- 4. `F5`: Start Debug -- 5. `Ctrl + F5`: Run without debug -- 6. `Ctrl + Shift + B`: Build project (optional) +#### 3.2.4. Run Steps + +1. **Open** your project in VS Code +2. **Install Extension:** + - *C/C++ Extension Pack* +3. **Install GDB:** + ```bash + sudo apt update + sudo apt install gdb + ``` +4. **Start Debugging:** + Press `F5` +5. **Run Without Debugging:** + Press `Ctrl + F5` +6. **Build Project (Optional):** + Press `Ctrl + Shift + B` +Notes: +| Shortcut | Action | +|-----------------|-----------------------| +| F5 | Start debugging | +| Ctrl + F5 | Run without debugging | +| Ctrl + Shift + D| Open Debug panel | +| Ctrl + Shift + B| Build project | ### 3.3 Documentation with `doxygen` TBD - Refer to this [Documentation with doxygen](https://www.labri.fr/perso/fleury/posts/programming/using-cmake-googletests-and-gcovr-in-a-c-project.html#:~:text=of%20the%C2%A0project.-,Documentation%20with%20doxygen,-Code%20embedded%20documentation) From 34ee0d56d6c8e303d4bcb4e58bd766b28ecf0721 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 11 Mar 2026 10:17:38 +0700 Subject: [PATCH 07/10] Move scripts to the folder and update README --- README.md | 10 +++++----- gen_coverage_gcovr.sh => scripts/gen_coverage_gcovr.sh | 0 gen_coverage_lcov.sh => scripts/gen_coverage_lcov.sh | 0 run.sh => scripts/run.sh | 0 4 files changed, 5 insertions(+), 5 deletions(-) rename gen_coverage_gcovr.sh => scripts/gen_coverage_gcovr.sh (100%) rename gen_coverage_lcov.sh => scripts/gen_coverage_lcov.sh (100%) rename run.sh => scripts/run.sh (100%) diff --git a/README.md b/README.md index 6a3c429..2a05e73 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 1. Overview **TL;DR** ```bash -./run.sh +./scripts/run.sh ``` **Project Hierarchy** @@ -56,8 +56,8 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma ``` * Run the application and the test ```bash - $ ./cpp_lab_project - $ ./cpp_lab_project_test + $ ./bin/cpp_lab_project + $ ./bin/cpp_lab_project_test ``` * Detect Memory Leak Using [valgrind](https://valgrind.org/) ```bash @@ -71,9 +71,9 @@ Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-forma ```bash $ clang-tidy -p build -header-filter='^src/.*' $(find src -name "*.cpp") ``` - * (Optional) Run coverage - clang-tidy + * (Optional) Run coverage - lcov ```bash - $ clang-tidy -p build -header-filter='^src/.*' $(find src -name "*.cpp") + $ ./scripts/gen_coverage_lcov.sh ``` - **Docker** * Update `Dockerfile` diff --git a/gen_coverage_gcovr.sh b/scripts/gen_coverage_gcovr.sh similarity index 100% rename from gen_coverage_gcovr.sh rename to scripts/gen_coverage_gcovr.sh diff --git a/gen_coverage_lcov.sh b/scripts/gen_coverage_lcov.sh similarity index 100% rename from gen_coverage_lcov.sh rename to scripts/gen_coverage_lcov.sh diff --git a/run.sh b/scripts/run.sh similarity index 100% rename from run.sh rename to scripts/run.sh From ae140c1ee6baf227fbf6b640f00237e5a347cbdb Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 11 Mar 2026 11:18:51 +0700 Subject: [PATCH 08/10] Update gitingore - clang --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6321306..d2d49df 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ coverage_gcovr coverage_lcov !.vscode/launch.json !.vscode/tasks.json +.cache \ No newline at end of file From 0e40a985b017b812215877d3d71a12657ab8e39c Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 11 Mar 2026 12:08:14 +0700 Subject: [PATCH 09/10] Update CMakelists.txt for library --- src/leetcode/CMakeLists.txt | 56 ++++++++++++++----------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/src/leetcode/CMakeLists.txt b/src/leetcode/CMakeLists.txt index 19acafa..f9e8b2f 100644 --- a/src/leetcode/CMakeLists.txt +++ b/src/leetcode/CMakeLists.txt @@ -1,40 +1,26 @@ -# ------------------------------------------------------------------------------ -# Create a library that contains implementations of multiple LeetCode problems. -# This library can be reused by: -# 1. main executable programs -# 2. unit tests -# -# Structure: -# leetcode_lib (library) -# │ -# ┌────┴────┐ -# ▼ ▼ -# main app unit tests -# -# # Use the library in unit test -# target_link_libraries(test_arrays -# PRIVATE -# leetcode_lib -# ) -# ------------------------------------------------------------------------------ +add_library(leetcode) -add_library(leetcode - arrays/two_sum/TwoSum.cpp - arrays/median_two_arrays/MedianTwoSortedArrays.cpp - arrays/container_with_most_water/ContainerWithMostWater.cpp - arrays/longest_common_prefix/Solution.cpp - arrays/3sum/Solution.cpp - arrays/4sum/Solution.cpp -) - -# ------------------------------------------------------------------------------ -# Expose header files to any target that links with this library. -# PUBLIC means: -# - This library can use the headers -# - Any target linking to this library can also use them -# ------------------------------------------------------------------------------ target_include_directories(leetcode PUBLIC - ${PROJECT_SOURCE_DIR} # for arrays/... headers ${PROJECT_SOURCE_DIR}/include # for shared public headers +) + +target_sources(leetcode + PRIVATE + arrays/two_sum/TwoSum.cpp + arrays/median_two_arrays/MedianTwoSortedArrays.cpp + arrays/container_with_most_water/ContainerWithMostWater.cpp + arrays/longest_common_prefix/Solution.cpp + arrays/3sum/Solution.cpp + arrays/4sum/Solution.cpp + PUBLIC + FILE_SET HEADERS + BASE_DIRS arrays + FILES + arrays/3sum/Solution.h + arrays/4sum/Solution.h + arrays/container_with_most_water/ContainerWithMostWater.h + arrays/longest_common_prefix/Solution.h + arrays/median_two_arrays/MedianTwoSortedArrays.h + arrays/two_sum/TwoSum.h ) \ No newline at end of file From 42c1965753371c58ec980399db885b8dc1c01882 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 11 Mar 2026 12:08:29 +0700 Subject: [PATCH 10/10] Add Driver_Account & Student_Manager --- src/CMakeLists.txt | 9 +- src/excercise/CMakeLists.txt | 9 ++ src/excercise/DriverAccount.cpp | 77 ++++++++++ src/excercise/DriverStudentManager.cpp | 70 +++++++++ src/excercise/account/Account.cpp | 65 +++++++++ src/excercise/account/Account.h | 22 +++ src/excercise/account/CMakeLists.txt | 10 ++ src/excercise/student_manager/CMakeLists.txt | 16 +++ src/excercise/student_manager/Manager.cpp | 144 +++++++++++++++++++ src/excercise/student_manager/Manager.h | 23 +++ src/excercise/student_manager/Student.cpp | 74 ++++++++++ src/excercise/student_manager/Student.h | 31 ++++ 12 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 src/excercise/CMakeLists.txt create mode 100644 src/excercise/DriverAccount.cpp create mode 100644 src/excercise/DriverStudentManager.cpp create mode 100644 src/excercise/account/Account.cpp create mode 100644 src/excercise/account/Account.h create mode 100644 src/excercise/account/CMakeLists.txt create mode 100644 src/excercise/student_manager/CMakeLists.txt create mode 100644 src/excercise/student_manager/Manager.cpp create mode 100644 src/excercise/student_manager/Manager.h create mode 100644 src/excercise/student_manager/Student.cpp create mode 100644 src/excercise/student_manager/Student.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e4fc7c..faafbfb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,7 @@ add_subdirectory(core) add_subdirectory(controller) add_subdirectory(patterns) add_subdirectory(socket) - +add_subdirectory(excercise) # main application executable does NOT link to this library. add_subdirectory(leetcode) @@ -25,6 +25,7 @@ add_executable(${PROJECT_NAME} ${SOCKET_SOURCES} ${CONTROLLER_SOURCES} ${DS_SOURCES} + ${EXCERCISE_SOURCES} ) # Add header include paths @@ -39,3 +40,9 @@ target_include_directories(${PROJECT_NAME} target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic ) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ex_account + ex_student_manager +) \ No newline at end of file diff --git a/src/excercise/CMakeLists.txt b/src/excercise/CMakeLists.txt new file mode 100644 index 0000000..6a72978 --- /dev/null +++ b/src/excercise/CMakeLists.txt @@ -0,0 +1,9 @@ +set(EXCERCISE_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/DriverAccount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/DriverStudentManager.cpp +) + +set(EXCERCISE_SOURCES ${EXCERCISE_SOURCES} PARENT_SCOPE) + +add_subdirectory(account) +add_subdirectory(student_manager) \ No newline at end of file diff --git a/src/excercise/DriverAccount.cpp b/src/excercise/DriverAccount.cpp new file mode 100644 index 0000000..6a8daf6 --- /dev/null +++ b/src/excercise/DriverAccount.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +#include "Account.h" +#include "ExampleRegistry.h" + +namespace { +void driver_test_account() { + std::cout << "\n====================\n" + << __func__ << "\n" + << "====================\n\n"; + + Account acc{0.0}; + while (true) { + std::cout << "=== MENU ===\n"; + std::cout << "1. Deposit\n"; + std::cout << "2. Withdraw\n"; + std::cout << "3. View balance\n"; + std::cout << "4. View transactions\n"; + std::cout << "5. Quit\n"; + + std::cout << "Input the choice: "; + int choice{}; + if (!(std::cin >> choice) || choice < 1 || choice > 5) { + std::cout << ">> " << "Invalid the choice\n"; + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + continue; + }; + + double amount{}; + if (choice == 1) { + std::cout << "Input the amount: "; + if (std::cin >> amount) { + try { + acc.credit(amount); + } catch (std::invalid_argument& e) { + std::cout << ">> " << e.what() << "\n"; + } + } else { + std::cout << ">> Invalid the amount\n"; + } + } else if (choice == 2) { + std::cout << "Input the amount: "; + if (std::cin >> amount) { + try { + acc.debit(amount); + } catch (std::invalid_argument& e) { + std::cout << ">> " << e.what() << "\n"; + } + } else { + std::cout << ">> Invalid the amount\n"; + } + } else if (choice == 3) { + std::cout << ">>" << acc.toString() << "\n"; + } else if (choice == 4) { + std::cout << ">> "; + acc.viewLog(); + std::cout << "\n"; + } else { + return; + } + } +} +} // namespace + +class DriverAccount : public IExample { + public: + std::string group() const override { return "excercise"; }; + std::string name() const override { return "DriverAccount"; } + std::string description() const override { return "Test Account"; } + + void execute() override { driver_test_account(); } +}; + +REGISTER_EXAMPLE(DriverAccount, "excercise", "DriverAccount") \ No newline at end of file diff --git a/src/excercise/DriverStudentManager.cpp b/src/excercise/DriverStudentManager.cpp new file mode 100644 index 0000000..4dc5db0 --- /dev/null +++ b/src/excercise/DriverStudentManager.cpp @@ -0,0 +1,70 @@ +#include +#include "ExampleRegistry.h" + +#include "Manager.h" + +namespace { +void driver_test_student_manager() { + std::cout << "\n====================\n" + << __func__ << "\n" + << "====================\n\n"; + Manager m; + char c; + + while (true) { + std::cout << "\n---------- MENU ----------\n"; + std::cout << "C Create a student\n"; + std::cout << "R Retrieve a student\n"; + std::cout << "U Update a student\n"; + std::cout << "D Delete a student\n"; + std::cout << "S Show student list\n"; + std::cout << "Q Quit\n"; + std::cout << "--------------------------\n"; + + std::cout << "Your choice: "; + std::cin >> c; + std::cin.ignore(); + + c = std::toupper(c); + + if (c == 'Q') + break; + + switch (c) { + case 'C': + m.add(); + break; + + case 'R': { + auto r = m.findByName(); + for (auto s : r) + std::cout << *s << "\n"; + break; + } + + case 'U': + m.update(); + break; + case 'D': + m.remove(); + break; + case 'S': + std::cout << m; + break; + } + } + + std::cout << "Bye bye!\n"; +} +} // namespace + +class DriverStudentManager : public IExample { + public: + std::string group() const override { return "excercise"; }; + std::string name() const override { return "DriverStudentManager"; } + std::string description() const override { return "Test StudentManager"; } + + void execute() override { driver_test_student_manager(); } +}; + +REGISTER_EXAMPLE(DriverStudentManager, "excercise", "DriverStudentManager") \ No newline at end of file diff --git a/src/excercise/account/Account.cpp b/src/excercise/account/Account.cpp new file mode 100644 index 0000000..762b2ad --- /dev/null +++ b/src/excercise/account/Account.cpp @@ -0,0 +1,65 @@ +#include "Account.h" +#include +#include +#include +#include + +Account::Account(double init) : balance_{init} { + if (init < 0) { + throw std::invalid_argument("Invalid credit amount\n"); + } +} + +double Account::getBalance() const { + return balance_; +} + +void Account::credit(double amount) { + if (amount < 0) { + throw std::invalid_argument("Invalid credit amount\n"); + } + balance_ += amount; + + std::ostringstream oss; + std::time_t now = time(NULL); + oss << std::fixed << std::setprecision(4) << "[credit] " << amount << " at " + << time2Str(now); + log_.push_back(oss.str()); +} + +void Account::debit(double amount) { + if (amount < 0) { + throw std::invalid_argument("Invalid debit amount\n"); + } + + double debit_amount = std::min(balance_, amount); + balance_ -= debit_amount; + + std::ostringstream oss; + std::time_t now = time(NULL); + oss << std::fixed << std::setprecision(4) << "[debit] " << debit_amount + << " at " << time2Str(now); + log_.push_back(oss.str()); +} + +void Account::viewLog() const { + if (log_.empty()) { + std::cout << "No transactions found.\n"; + return; + } + for (const auto& m : log_) { + std::cout << m << '\n'; + } +} + +std::string Account::toString() const { + std::ostringstream oss; + oss << std::fixed << std::setprecision(4) << "Account balance: " << balance_; + return oss.str(); +} + +const std::string Account::time2Str(time_t t) { + std::string time = ctime(&t); + time.pop_back(); // remove newline + return time; +} \ No newline at end of file diff --git a/src/excercise/account/Account.h b/src/excercise/account/Account.h new file mode 100644 index 0000000..1bda25f --- /dev/null +++ b/src/excercise/account/Account.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +class Account { + public: + explicit Account(double init = 0.0); + double getBalance() const; + + void credit(double amount); + void debit(double amount); + void viewLog() const; + std::string toString() const; + + private: + static const std::string time2Str(time_t t); + + private: + double balance_; + std::vector log_; +}; diff --git a/src/excercise/account/CMakeLists.txt b/src/excercise/account/CMakeLists.txt new file mode 100644 index 0000000..8fea504 --- /dev/null +++ b/src/excercise/account/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(ex_account) + +target_sources(ex_account + PRIVATE + Account.cpp + PUBLIC + FILE_SET HEADERS + FILES + Account.h +) \ No newline at end of file diff --git a/src/excercise/student_manager/CMakeLists.txt b/src/excercise/student_manager/CMakeLists.txt new file mode 100644 index 0000000..52ea98b --- /dev/null +++ b/src/excercise/student_manager/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(ex_student_manager) + +target_include_directories(ex_student_manager + PRIVATE + ${PROJECT_SOURCE_DIR}/include +) + +target_sources(ex_student_manager + PRIVATE + Student.cpp + Manager.cpp + PUBLIC + FILE_SET HEADERS + FILES + Manager.h +) \ No newline at end of file diff --git a/src/excercise/student_manager/Manager.cpp b/src/excercise/student_manager/Manager.cpp new file mode 100644 index 0000000..7e03f4f --- /dev/null +++ b/src/excercise/student_manager/Manager.cpp @@ -0,0 +1,144 @@ +#include "Manager.h" +#include +#include +#include + +Manager::~Manager() { + for (auto it : students_) { + delete it; + } +} + +void Manager::add() { + std::string name = inputStr("Name?", "Invalid"); + std::string address = inputStr("Address?", "Invalid"); + Student* s = new Student(name, address); + students_.push_back(s); + sortByName(); + std::cout << "Add successful!\n"; +} + +void Manager::remove() { + std::string code = inputStr("Code?", "Invalid"); + // for (auto it = students_.begin(); it != students_.end(); ++it) { + // if ((*it)->getCode().substr(0, code.size()) == code) { + // delete *it; + // students_.erase(it); + // std::cout << "Remove successfull!\n"; + // break; + // } + // } + auto it = std::find_if( + students_.begin(), students_.end(), + [&code](const Student* s) { return s && s->getCode().find(code) == 0; }); + + if (it != students_.end()) { + students_.erase(it); + std::cout << "Remove successfull!\n"; + } + + std::cout << "Student not found!\n"; +} + +void Manager::update() { + std::string code = inputStr("Code?", "Invalid"); + auto* s = findByCode(code); + if (s != nullptr) { + std::cout << "Hint: use old value, press Enter\n"; + std::string name = inputStr("New Name?", "Invalid"); + std::string address = inputStr("New Address?", "Invalid"); + std::string markStr = inputStr("New Mark?", "Invalid"); + double mark{}; + try { + mark = std::stod(markStr); + } catch (std::exception& e) { + std::cout << "invalid mark" << e.what(); + } + if (mark < 0.0 || mark > 10.0) { + std::cout << "invalid mark " << mark << "\n"; + return; + } + + s->setMark(mark); + s->setName(name); + s->setAddress(address); + std::cout << "Update successful! \n"; + } +} + +std::ostream& operator<<(std::ostream& os, const Manager& manager) { + for (const auto* const s : manager.students_) { + os << *s << "\n"; + } + return os; +} + +Student* Manager::findByCode(const std::string& cod) { + // for (auto it : students_) { + // // if (it->getCode().substr(0, cod.size()) == cod) // partial match + // if (it->getCode() == cod) { + // return it; + // } + // } + const auto it = std::find_if( + students_.begin(), students_.end(), + [&cod](const Student* s) { return s && s->getCode() == cod; }); + + if (it != students_.end()) { + return *it; + } + + std::cout << "Student not found !!\n"; + return nullptr; +} + +void Manager::sortByName() { + std::sort(students_.begin(), students_.end(), + [](const Student* a, const Student* b) { return *a < *b; }); +} + +std::string Manager::trim(const std::string& str) { + constexpr std::string_view whitespaces{" \n\r\t\v\x00"}; + auto firstPos = str.find_first_not_of(whitespaces); + if (firstPos == std::string::npos) { + return ""; + } + + auto secondPos = str.find_last_not_of(whitespaces); + return str.substr(firstPos, secondPos - firstPos + 1); +} + +std::vector Manager::findByName() { + std::string name = inputStr("Name?", "Invalid Name?"); + std::vector result; + // for (auto it : students_) { + // // contain + // if (it->getName().find(name) != std::string::npos) { + // result.push_back(it); + // } + // } + std::copy_if(students_.begin(), students_.end(), std::back_inserter(result), + [&name](const Student* const s) { + return s && s->getName().find(name) != std::string::npos; + }); + + return result; +} + +std::string Manager::inputStr(const std::string& prompt, + const std::string& error) { + std::string output; + std::cout << prompt << "\n"; + while (true) { + if (!std::getline(std::cin, output)) { + std::cout << "getline failed\n"; + return ""; + } + + output = trim(output); + if (output != "") { + return output; + } + std::cout << error; + } +} \ No newline at end of file diff --git a/src/excercise/student_manager/Manager.h b/src/excercise/student_manager/Manager.h new file mode 100644 index 0000000..258fd6c --- /dev/null +++ b/src/excercise/student_manager/Manager.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include "Student.h" +class Manager { + public: + ~Manager(); + + void add(); + void update(); + void remove(); + std::vector findByName(); + + private: + void sortByName(); + Student* findByCode(const std::string& cod); + static std::string trim(const std::string& str); + static std::string inputStr(const std::string& prompt, const std::string& error); + + friend std::ostream& operator<<(std::ostream& os, const Manager& manager); + + private: + std::vector students_; +}; \ No newline at end of file diff --git a/src/excercise/student_manager/Student.cpp b/src/excercise/student_manager/Student.cpp new file mode 100644 index 0000000..abbaa3e --- /dev/null +++ b/src/excercise/student_manager/Student.cpp @@ -0,0 +1,74 @@ +#include "Student.h" +#include +#include + +Student::Student(const std::string& name, const std::string& address) + : code_{}, name_{name}, address_{address}, mark_{0.0} { + setCode(); +} + +Student::~Student(){ + +} + +Student::Student(const Student& other) + : code_{other.code_}, + name_{other.name_}, + address_{other.address_}, + mark_{other.mark_} {} + +void Student::setCode() { + // get name code XXX + std::string nameCode = name_; + + name_.append(3, ' '); // so, name code at least 3 charactor + nameCode.resize(3); + std::replace(nameCode.begin(), nameCode.end(), ' ', 'X'); + std::transform(nameCode.begin(), nameCode.end(), nameCode.begin(), ::toupper); + + // get time code XXXX + time_t now = time(NULL); + std::string timeCode = std::to_string(now); + timeCode = timeCode.substr(timeCode.size() - 3, timeCode.size()); + code_ = nameCode + timeCode; +} + +std::ostream& operator<<(std::ostream& os, const Student& student) { + os << "[" << student.code_ << "] " << student.name_ << " " << student.mark_; + return os; +} + +bool operator<(const Student& a, const Student& b) { + if (a.name_ != b.name_) { + return a.name_ < b.name_; + }; + return a.code_ < b.code_; +} + +void Student::setName(const std::string& name) { + name_ = name; +} + +void Student::setAddress(const std::string& address) { + address_ = address; +} + +void Student::setMark(double mark) { + mark_ = mark; +} + +const std::string& Student::getAddress() const{ + return address_; +} + +const std::string& Student::getCode() const { + return code_; +} + +const std::string& Student::getName() const { + return name_; +} + +double Student::getMark() const { + return mark_; +} diff --git a/src/excercise/student_manager/Student.h b/src/excercise/student_manager/Student.h new file mode 100644 index 0000000..76d4053 --- /dev/null +++ b/src/excercise/student_manager/Student.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +class Student { + + public: + Student(const std::string& name, const std::string& address); + Student(const Student& other); + ~Student(); + + void setName(const std::string& name); + void setAddress(const std::string& address); + void setMark(double mark); + + const std::string& getCode() const; + const std::string& getAddress() const; + const std::string& getName() const; + double getMark() const; + + friend std::ostream& operator<<(std::ostream& os, const Student& student); + friend bool operator<(const Student& a, const Student& b); + + private: + void setCode(); + + private: + std::string code_; + std::string name_; + std::string address_; + double mark_; +}; \ No newline at end of file