initial commit
This commit is contained in:
2
cplusplus/geminitutorial/README.md
Normal file
2
cplusplus/geminitutorial/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# GeminiTutorial
|
||||
|
||||
89
cplusplus/geminitutorial/gemini.md
Normal file
89
cplusplus/geminitutorial/gemini.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Gemini C++ Refresher Plan
|
||||
|
||||
This document summarizes the C++ refresher plan designed for an experienced programmer returning to C++ with a focus on quantitative finance (Quant) and High-Frequency Trading (HFT).
|
||||
|
||||
## Initial Request
|
||||
|
||||
The user, an experienced programmer last active with C++ around the year 2000, requested a detailed plan to get up to speed with modern C++. The plan needed to cover:
|
||||
|
||||
1. Build tools and development setup on Linux (VSCode/terminal).
|
||||
2. Modern programming constructs and concepts.
|
||||
3. Usage of modern standard libraries.
|
||||
4. A strong emphasis on design patterns and idioms for efficient, low-latency code.
|
||||
|
||||
The context is for application in quantitative modeling and HFT.
|
||||
|
||||
## Review of Expert Material
|
||||
|
||||
The initial plan was subsequently refined by reviewing two expert documents provided by the user:
|
||||
|
||||
1. **"Optimizing C++" by Agner Fog:** A guide to low-level, hardware-aware C++ optimization.
|
||||
2. **"Modern C++ for Financial Engineering" (arXiv:2309.04259):** A domain-specific paper on modern C++ patterns in finance.
|
||||
|
||||
## Final Refresher Plan
|
||||
|
||||
The final plan incorporates insights from these documents, focusing on practical, hands-on tutorials that build upon each other.
|
||||
|
||||
---
|
||||
|
||||
### **Part 1: The Modern Development Environment on Linux**
|
||||
|
||||
This part establishes a professional, reproducible development workflow.
|
||||
|
||||
* **Tutorial 1.1: Your First Modern C++ Project**
|
||||
* **Objective:** Build a simple program using the modern toolchain (CMake, vcpkg) and link an external library (`spdlog`).
|
||||
* **Tools:** `g++`/`clang++`, `CMake`, `vcpkg`, VSCode with C++ & CMake extensions.
|
||||
* **Key Takeaway:** Automate building and dependency management, leaving manual Makefiles in the past.
|
||||
|
||||
---
|
||||
|
||||
### **Part 2: Core Language, Libraries, and Concepts**
|
||||
|
||||
This section bridges the 20-year gap from C++98 to C++20+.
|
||||
|
||||
* **Tutorial 2.1: Resource Management & The Rule of Zero (Completed)****
|
||||
* **Objective:** Master RAII and move semantics to eliminate manual memory management.
|
||||
* **Concepts:** Smart Pointers (`std::unique_ptr`, `std::shared_ptr`), Move Semantics (`&&`, `std::move`), and The Rule of Zero.
|
||||
* **Project:** Refactor a C++98-style class using raw pointers to a modern class using `std::vector` and smart pointers, demonstrating the elimination of manual destructors/constructors.
|
||||
|
||||
* **Tutorial 2.2: Modern STL Containers & Algorithms**
|
||||
* **Objective:** Use the workhorse containers and algorithms with a focus on performance.
|
||||
* **Concepts:** `std::vector`, `std::array` (for stack allocation), `std::string_view` (for zero-copy parsing), `std::unordered_map`, Lambdas, and range-based `for` loops.
|
||||
* **Project:** Parse mock CSV tick data, using algorithms and lambdas to perform queries.
|
||||
|
||||
* **Tutorial 2.3: Compile-Time Programming & Type Safety**
|
||||
* **Objective:** Use the compiler to improve performance and safety.
|
||||
* **Concepts:** `constexpr` for compile-time calculations, `enum class` for type-safe enums, `auto` for type deduction.
|
||||
* **Project:** A simple program using these features to enforce type safety and move runtime calculations to compile-time.
|
||||
|
||||
---
|
||||
|
||||
### **Part 3: Design Patterns & Idioms for Low-Latency C++ (Revised)**
|
||||
|
||||
This section focuses on HFT/Quant performance, emphasizing hardware-aware programming.
|
||||
|
||||
* **Tutorial 3.1: Static vs. Dynamic Polymorphism (CRTP vs. Virtual Functions)**
|
||||
* **Objective:** Understand the performance cost of `virtual` functions and learn the **Curiously Recurring Template Pattern (CRTP)** as a zero-cost, compile-time alternative.
|
||||
* **Hardware Insight:** `virtual` calls can cause branch mispredictions; CRTP enables inlining.
|
||||
|
||||
* **Tutorial 3.2: Custom Memory Management & Cache Locality**
|
||||
* **Objective:** Understand that heap allocation (`new`) is slow primarily because it destroys **cache locality**.
|
||||
* **Concepts:** Memory Pools / Arena Allocators. The key benefit is placing related data contiguously in memory to maximize CPU cache efficiency.
|
||||
|
||||
* **Tutorial 3.3: Data-Oriented Design (Struct of Arrays)**
|
||||
* **Objective:** Implement a powerful, cache-friendly optimization by changing data layout.
|
||||
* **Concepts:** Switch from Array of Structs (AoS), e.g., `vector<Tick>`, to Struct of Arrays (SoA), e.g., `struct { vector<double> prices; vector<int> volumes; }`.
|
||||
* **Project:** Benchmark a calculation on a large dataset using both AoS and SoA layouts to demonstrate the significant performance gain of SoA, which is ideal for cache utilization and SIMD vectorization.
|
||||
|
||||
* **Tutorial 3.4: Type-Safe Polymorphism with `std::variant`**
|
||||
* **Objective:** Learn the modern, safe, and performant way to handle objects of different types (e.g., financial events).
|
||||
* **Concepts:** `std::variant` as a type-safe union and `std::visit` to operate on the contained types without `virtual` functions or unsafe casts.
|
||||
|
||||
---
|
||||
|
||||
### **Part 4: Additional Essential Material (Revised)**
|
||||
|
||||
* **Concurrency:** `std::thread`, `std::mutex`, `std::atomic` (critical for lock-free code), `std::condition_variable`.
|
||||
* **Essential Quant Libraries:** **Eigen** for linear algebra, **Boost** for various utilities.
|
||||
* **Advanced Topic - Adjoint Algorithmic Differentiation (AAD):** An introduction to the key technique used in modern derivatives pricing for calculating risk sensitivities ("Greeks") efficiently.
|
||||
* **Further Reading:** `cppreference.com`, "Effective Modern C++" (Scott Meyers), CppCon talks.
|
||||
69
cplusplus/geminitutorial/plan.md
Normal file
69
cplusplus/geminitutorial/plan.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# C++ Refresher Plan for Quantitative/HFT Development
|
||||
|
||||
This plan is designed for an experienced programmer returning to C++ with a focus on quantitative finance (Quant) and High-Frequency Trading (HFT). It incorporates expert feedback from "Optimizing C++" by Agner Fog and "Modern C++ for Financial Engineering" (arXiv:2309.04259).
|
||||
|
||||
---
|
||||
|
||||
### **Part 1: The Modern Development Environment on Linux**
|
||||
|
||||
This part establishes a professional, reproducible development workflow.
|
||||
|
||||
* **Tutorial 1.1: Your First Modern C++ Project**
|
||||
* **Objective:** Build a simple program using the modern toolchain (CMake, vcpkg) and link an external library (`spdlog`).
|
||||
* **Tools:** `g++`/`clang++`, `CMake`, `vcpkg`, VSCode with C++ & CMake extensions.
|
||||
* **Key Takeaway:** Automate building and dependency management, leaving manual Makefiles in the past.
|
||||
|
||||
* **Tutorial 1.2: incorporate gtest unit testing framework**
|
||||
|
||||
---
|
||||
|
||||
### **Part 2: Core Language, Libraries, and Concepts**
|
||||
|
||||
This section bridges the 20-year gap from C++98 to C++20+.
|
||||
|
||||
* **Tutorial 2.1: Resource Management & The Rule of Zero**
|
||||
* **Objective:** Master RAII and move semantics to eliminate manual memory management.
|
||||
* **Concepts:** Smart Pointers (`std::unique_ptr`, `std::shared_ptr`), Move Semantics (`&&`, `std::move`), and The Rule of Zero.
|
||||
* **Project:** Refactor a C++98-style class using raw pointers to a modern class using `std::vector` and smart pointers, demonstrating the elimination of manual destructors/constructors.
|
||||
|
||||
* **Tutorial 2.2: Modern STL Containers & Algorithms**
|
||||
* **Objective:** Use the workhorse containers and algorithms with a focus on performance.
|
||||
* **Concepts:** `std::vector`, `std::array` (for stack allocation), `std::string_view` (for zero-copy parsing), `std::unordered_map`, Lambdas, and range-based `for` loops.
|
||||
* **Project:** Parse mock CSV tick data, using algorithms and lambdas to perform queries.
|
||||
|
||||
* **Tutorial 2.3: Compile-Time Programming & Type Safety**
|
||||
* **Objective:** Use the compiler to improve performance and safety.
|
||||
* **Concepts:** `constexpr` for compile-time calculations, `enum class` for type-safe enums, `auto` for type deduction.
|
||||
* **Project:** A simple program using these features to enforce type safety and move runtime calculations to compile-time.
|
||||
|
||||
---
|
||||
|
||||
### **Part 3: Design Patterns & Idioms for Low-Latency C++ (Revised)**
|
||||
|
||||
This section focuses on HFT/Quant performance, emphasizing hardware-aware programming.
|
||||
|
||||
* **Tutorial 3.1: Static vs. Dynamic Polymorphism (CRTP vs. Virtual Functions)**
|
||||
* **Objective:** Understand the performance cost of `virtual` functions and learn the **Curiously Recurring Template Pattern (CRTP)** as a zero-cost, compile-time alternative.
|
||||
* **Hardware Insight:** `virtual` calls can cause branch mispredictions; CRTP enables inlining.
|
||||
|
||||
* **Tutorial 3.2: Custom Memory Management & Cache Locality**
|
||||
* **Objective:** Understand that heap allocation (`new`) is slow primarily because it destroys **cache locality**.
|
||||
* **Concepts:** Memory Pools / Arena Allocators. The key benefit is placing related data contiguously in memory to maximize CPU cache efficiency.
|
||||
|
||||
* **Tutorial 3.3: Data-Oriented Design (Struct of Arrays)**
|
||||
* **Objective:** Implement a powerful, cache-friendly optimization by changing data layout.
|
||||
* **Concepts:** Switch from Array of Structs (AoS), e.g., `vector<Tick>`, to Struct of Arrays (SoA), e.g., `struct { vector<double> prices; vector<int> volumes; }`.
|
||||
* **Project:** Benchmark a calculation on a large dataset using both AoS and SoA layouts to demonstrate the significant performance gain of SoA, which is ideal for cache utilization and SIMD vectorization.
|
||||
|
||||
* **Tutorial 3.4: Type-Safe Polymorphism with `std::variant`**
|
||||
* **Objective:** Learn the modern, safe, and performant way to handle objects of different types (e.g., financial events).
|
||||
* **Concepts:** `std::variant` as a type-safe union and `std::visit` to operate on the contained types without `virtual` functions or unsafe casts.
|
||||
|
||||
---
|
||||
|
||||
### **Part 4: Additional Essential Material (Revised)**
|
||||
|
||||
* **Concurrency:** `std::thread`, `std::mutex`, `std::atomic` (critical for lock-free code), `std::condition_variable`.
|
||||
* **Essential Quant Libraries:** **Eigen** for linear algebra, **Boost** for various utilities.
|
||||
* **Advanced Topic - Adjoint Algorithmic Differentiation (AAD):** An introduction to the key technique used in modern derivatives pricing for calculating risk sensitivities ("Greeks") efficiently.
|
||||
* **Further Reading:** `cppreference.com`, "Effective Modern C++" (Scott Meyers), CppCon talks.
|
||||
6
cplusplus/geminitutorial/tutorial1/.vscode/settings.json
vendored
Normal file
6
cplusplus/geminitutorial/tutorial1/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"cmake.sourceDirectory": "/home/ys/code/cplusplus/GeminiTutorial/tutorial-1",
|
||||
"cmake.configureArgs": [
|
||||
"-DCMAKE_TOOLCHAIN_FILE=/home/ys/code/cplusplus/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||
]
|
||||
}
|
||||
33
cplusplus/geminitutorial/tutorial1/CMakeLists.txt
Normal file
33
cplusplus/geminitutorial/tutorial1/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(Tutorial1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Find dependencies
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
|
||||
# Create a library for our application code
|
||||
# This allows us to share the code between the main app and the tests
|
||||
add_library(MyLib src/calculator.cpp src/calculator.h)
|
||||
target_include_directories(MyLib PUBLIC src)
|
||||
|
||||
# Define our main executable
|
||||
add_executable(App src/main.cpp)
|
||||
|
||||
# Link our App against spdlog and our library
|
||||
target_link_libraries(App PRIVATE spdlog::spdlog MyLib)
|
||||
|
||||
# Enable testing with CTest (CMake's testing framework)
|
||||
enable_testing()
|
||||
|
||||
# Define the test executable
|
||||
add_executable(RunTests test/test_calculator.cpp)
|
||||
|
||||
# Link our test executable against GTest and our library
|
||||
target_link_libraries(RunTests PRIVATE GTest::gtest_main MyLib)
|
||||
|
||||
# Add the test to CTest
|
||||
include(GoogleTest)
|
||||
gtest_add_tests(TARGET RunTests)
|
||||
5
cplusplus/geminitutorial/tutorial1/src/calculator.cpp
Normal file
5
cplusplus/geminitutorial/tutorial1/src/calculator.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "calculator.h"
|
||||
|
||||
int Calculator::add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
9
cplusplus/geminitutorial/tutorial1/src/calculator.h
Normal file
9
cplusplus/geminitutorial/tutorial1/src/calculator.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef CALCULATOR_H
|
||||
#define CALCULATOR_H
|
||||
|
||||
class Calculator {
|
||||
public:
|
||||
int add(int a, int b);
|
||||
};
|
||||
|
||||
#endif // CALCULATOR_H
|
||||
16
cplusplus/geminitutorial/tutorial1/src/main.cpp
Normal file
16
cplusplus/geminitutorial/tutorial1/src/main.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <iostream>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "calculator.h"
|
||||
|
||||
int main() {
|
||||
spdlog::info("Hello, Modern C++! Initializing trading strategy...");
|
||||
|
||||
Calculator calc;
|
||||
int result = calc.add(40, 2);
|
||||
|
||||
spdlog::info("The result of 40 + 2 is: {}", result);
|
||||
|
||||
std::cout << "Check your terminal for spdlog output!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
cplusplus/geminitutorial/tutorial1/test/test_calculator.cpp
Normal file
22
cplusplus/geminitutorial/tutorial1/test/test_calculator.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "../src/calculator.h" // Include the header for the class we are testing
|
||||
|
||||
// Test fixture for Calculator class
|
||||
class CalculatorTest : public ::testing::Test {
|
||||
protected:
|
||||
Calculator calc;
|
||||
};
|
||||
|
||||
// Test case for the add method
|
||||
TEST_F(CalculatorTest, Add) {
|
||||
EXPECT_EQ(calc.add(2, 2), 4);
|
||||
EXPECT_EQ(calc.add(-1, 1), 0);
|
||||
EXPECT_EQ(calc.add(-5, -5), -10);
|
||||
}
|
||||
|
||||
// Another test case for the add method for good measure
|
||||
TEST_F(CalculatorTest, AddWithZero) {
|
||||
EXPECT_EQ(calc.add(0, 5), 5);
|
||||
EXPECT_EQ(calc.add(5, 0), 5);
|
||||
EXPECT_EQ(calc.add(0, 0), 0);
|
||||
}
|
||||
173
cplusplus/geminitutorial/tutorial1/tutorial-part1-1.md
Normal file
173
cplusplus/geminitutorial/tutorial1/tutorial-part1-1.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Tutorial 1.1: Your First Modern C++ Project
|
||||
|
||||
**Objective:** To compile, run, debug, and manage dependencies for a C++ application using the modern, industry-standard toolchain on Linux. This single tutorial covers the entire end-to-end workflow.
|
||||
|
||||
---
|
||||
|
||||
## 1. Prerequisites: System Setup
|
||||
|
||||
First, you need to ensure the necessary base tools are installed. Open your terminal and run the following commands.
|
||||
|
||||
```bash
|
||||
# Update package lists
|
||||
sudo apt update
|
||||
|
||||
# Install compiler (g++), make, git, and other essential build tools
|
||||
sudo apt install build-essential g++ git cmake curl
|
||||
```
|
||||
|
||||
You will also need **Visual Studio Code**, which you can download from the [official website](https://code.visualstudio.com/).
|
||||
|
||||
After installing VSCode, launch it and install these two essential extensions from the Extensions view (Ctrl+Shift+X):
|
||||
1. `C/C++` (Publisher: Microsoft)
|
||||
2. `CMake Tools` (Publisher: Microsoft)
|
||||
|
||||
## 2. The Game Changer: `vcpkg` Package Manager
|
||||
|
||||
In C++ circa 2000, adding a library was a manual ordeal. `vcpkg` automates this. It downloads, builds, and prepares libraries for your project, acting like `pip` for Python.
|
||||
|
||||
In your terminal, choose a location for your development tools (e.g., `~/dev/tools`) and clone `vcpkg`:
|
||||
|
||||
```bash
|
||||
# Create a directory for tools if you don't have one
|
||||
mkdir -p ~/dev/tools
|
||||
cd ~/dev/tools
|
||||
|
||||
# Clone the vcpkg repository
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
|
||||
# Run the bootstrap script to build vcpkg itself
|
||||
./vcpkg/bootstrap-vcpkg.sh
|
||||
```
|
||||
|
||||
**IMPORTANT:** Note the absolute path to your vcpkg installation. You'll need it later. It will be something like `/home/YOUR_USERNAME/dev/tools/vcpkg`.
|
||||
|
||||
## 3. Project Setup
|
||||
|
||||
Now, let's create the project.
|
||||
|
||||
```bash
|
||||
# Go to where you keep your code projects
|
||||
cd ~/code/cplusplus/GeminiTutorial
|
||||
|
||||
# Create and enter the project directory
|
||||
mkdir tutorial-1
|
||||
cd tutorial-1
|
||||
```
|
||||
|
||||
## 4. Installing a Library
|
||||
|
||||
Let's install `spdlog`, a popular and high-performance logging library.
|
||||
|
||||
```bash
|
||||
# Use the vcpkg executable to install the library
|
||||
# Replace /path/to/vcpkg with your actual vcpkg path
|
||||
/home/YOUR_USERNAME/dev/tools/vcpkg/vcpkg install spdlog
|
||||
```
|
||||
|
||||
This command will download, compile, and install `spdlog` into your `vcpkg` directory, making it available for any project.
|
||||
|
||||
## 5. Writing the Build Script (`CMakeLists.txt`)
|
||||
|
||||
This file tells `CMake` how to build your project. Create a file named `CMakeLists.txt` inside the `tutorial-1` directory with the following content.
|
||||
|
||||
```cmake
|
||||
# Specifies the minimum version of CMake required.
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
# Defines the project name and sets C++ as the language.
|
||||
project(Tutorial1 LANGUAGES CXX)
|
||||
|
||||
# Set the C++ standard to C++17. Modern C++ starts with C++11.
|
||||
# C++17 is a great, stable choice.
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Find the spdlog library.
|
||||
# CMake needs to be told how to find packages installed by vcpkg.
|
||||
# We will do this in the VSCode settings in the next step.
|
||||
# 'CONFIG REQUIRED' means it's a fatal error if the package isn't found.
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
|
||||
# Define our executable target, named 'App', from the source file 'main.cpp'.
|
||||
add_executable(App main.cpp)
|
||||
|
||||
# Link our App against the spdlog library.
|
||||
# This tells the compiler to include the necessary spdlog files when building App.
|
||||
target_link_libraries(App PRIVATE spdlog::spdlog)
|
||||
```
|
||||
|
||||
## 6. Writing the C++ Code (`main.cpp`)
|
||||
|
||||
Create a file named `main.cpp` in the `tutorial-1` directory.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
// Include the main header from the spdlog library.
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
int main() {
|
||||
// Use the library to print an informational message.
|
||||
// This is much more powerful than std::cout for real applications,
|
||||
// as it can be configured to log to files, rotate logs, filter by level, etc.
|
||||
spdlog::info("Hello, Modern C++! Initializing trading strategy...");
|
||||
|
||||
int x = 42;
|
||||
double price = 102.5;
|
||||
|
||||
// spdlog supports formatted output similar to Python's f-strings.
|
||||
spdlog::warn("A variable 'x' has value: {}", x);
|
||||
spdlog::critical("Critical event! Price is: {}", price);
|
||||
|
||||
std::cout << "Check your terminal for spdlog output!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Building and Debugging in VSCode
|
||||
|
||||
This is where all the pieces come together.
|
||||
|
||||
1. **Open the Project:** Launch VSCode and open the `tutorial-1` folder (`File > Open Folder...`).
|
||||
|
||||
2. **Configure CMake:**
|
||||
* The CMake Tools extension will activate. You may see a prompt asking to configure the project.
|
||||
* **This will fail initially!** This is expected. An error will appear saying `Could not find a package configuration file for "spdlog"`.
|
||||
* **Here is the fix:** We must tell CMake where to find `vcpkg`-installed libraries.
|
||||
* Open the VSCode settings: `File > Preferences > Settings` (or `Ctrl+,`).
|
||||
* Click the "Workspace" tab. Search for `cmake.configureArgs`.
|
||||
* Click "Edit in settings.json".
|
||||
* Add the following entry, **replacing the path with the actual absolute path to your vcpkg directory**:
|
||||
```json
|
||||
{
|
||||
"cmake.configureArgs": [
|
||||
"-DCMAKE_TOOLCHAIN_FILE=/home/YOUR_USERNAME/dev/tools/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||
]
|
||||
}
|
||||
```
|
||||
* Save `settings.json`. A new `.vscode` directory will be created in your project folder to store this setting.
|
||||
|
||||
3. **Build the Project:**
|
||||
* Run the `CMake: Configure` command from the command palette (Ctrl+Shift+P). It should now succeed.
|
||||
* The status bar at the bottom of VSCode now shows buttons for building. Click the **`Build`** button.
|
||||
* You'll see output in the terminal as CMake and the compiler build your `App` executable.
|
||||
|
||||
4. **Run and Debug:**
|
||||
* **Run:** The "play" button in the status bar will run the executable. You'll see the `spdlog` output in the VSCode terminal.
|
||||
* **Debug:**
|
||||
1. Open `main.cpp`.
|
||||
2. Click in the gutter to the left of the line number for `int x = 42;` to set a **breakpoint** (a red dot will appear).
|
||||
3. Press **F5** (or click the "bug" icon next to the play button).
|
||||
4. Execution will start and then **stop** at your breakpoint.
|
||||
5. You can now inspect variables in the "Run and Debug" side panel and use the controls to step through the code (F10, F11).
|
||||
|
||||
---
|
||||
|
||||
**Congratulations!** You have successfully set up a modern C++ development environment, managed an external dependency, and used the integrated build/debug tools. This workflow is the foundation for all subsequent tutorials.
|
||||
|
||||
|
||||
# 8 Feedback
|
||||
|
||||
There is a dependency on gdb debugger that was not mentioned. after building and installing everything works as expected
|
||||
201
cplusplus/geminitutorial/tutorial1/tutorial-part1-2.md
Normal file
201
cplusplus/geminitutorial/tutorial1/tutorial-part1-2.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Tutorial 1.2: Introduction to Unit Testing
|
||||
|
||||
**Objective:** To introduce unit testing to the C++ project from Tutorial 1.1. This tutorial will cover adding a testing framework (`googletest`), creating a simple class, writing tests for that class, and integrating the tests into the CMake build system for easy execution within VSCode.
|
||||
|
||||
---
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
* A completed and working project from "Tutorial 1.1: Your First Modern C++ Project".
|
||||
* `vcpkg` installed and configured as per Tutorial 1.1.
|
||||
|
||||
---
|
||||
|
||||
## 2. Installing the Testing Framework
|
||||
|
||||
We will use `googletest`, a popular and powerful testing framework for C++.
|
||||
|
||||
Just like with `spdlog`, we use `vcpkg` to install it.
|
||||
|
||||
```bash
|
||||
# Use the vcpkg executable to install the library
|
||||
# Replace /path/to/vcpkg with your actual vcpkg path
|
||||
/home/YOUR_USERNAME/dev/tools/vcpkg/vcpkg install gtest
|
||||
```
|
||||
*Note: The package name is `gtest` for `googletest`.*
|
||||
|
||||
---
|
||||
|
||||
## 3. Refactoring the Project Structure
|
||||
|
||||
To keep our project organized, we'll separate our main application code from the test code. The new structure inside `tutorial-1` will look like this:
|
||||
```
|
||||
tutorial-1/
|
||||
├── CMakeLists.txt
|
||||
├── src/
|
||||
│ ├── calculator.h
|
||||
│ ├── calculator.cpp
|
||||
│ └── main.cpp
|
||||
└── test/
|
||||
└── test_calculator.cpp
|
||||
```
|
||||
The previous steps in this session have already created this structure for you.
|
||||
|
||||
---
|
||||
|
||||
## 4. Creating a Class to Test
|
||||
|
||||
We need some logic to test. We've created a simple `Calculator` class.
|
||||
|
||||
**File:** `tutorial-1/src/calculator.h`
|
||||
```cpp
|
||||
#ifndef CALCULATOR_H
|
||||
#define CALCULATOR_H
|
||||
|
||||
class Calculator {
|
||||
public:
|
||||
int add(int a, int b);
|
||||
};
|
||||
|
||||
#endif // CALCULATOR_H
|
||||
```
|
||||
|
||||
**File:** `tutorial-1/src/calculator.cpp`
|
||||
```cpp
|
||||
#include "calculator.h"
|
||||
|
||||
int Calculator::add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
We also updated `main.cpp` to use this new class.
|
||||
|
||||
**File:** `tutorial-1/src/main.cpp` (Updated)
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "calculator.h"
|
||||
|
||||
int main() {
|
||||
spdlog::info("Hello, Modern C++! Initializing trading strategy...");
|
||||
|
||||
Calculator calc;
|
||||
int result = calc.add(40, 2);
|
||||
|
||||
spdlog::info("The result of 40 + 2 is: {}", result);
|
||||
|
||||
std::cout << "Check your terminal for spdlog output!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Writing the Unit Tests
|
||||
|
||||
Now, we'll write our first test in the `test` directory.
|
||||
|
||||
**File:** `tutorial-1/test/test_calculator.cpp`
|
||||
```cpp
|
||||
#include <gtest/gtest.h>
|
||||
#include "../src/calculator.h" // Include the header for the class we are testing
|
||||
|
||||
// Test fixture for Calculator class
|
||||
class CalculatorTest : public ::testing::Test {
|
||||
protected:
|
||||
Calculator calc;
|
||||
};
|
||||
|
||||
// Test case for the add method
|
||||
TEST_F(CalculatorTest, Add) {
|
||||
EXPECT_EQ(calc.add(2, 2), 4);
|
||||
EXPECT_EQ(calc.add(-1, 1), 0);
|
||||
EXPECT_EQ(calc.add(-5, -5), -10);
|
||||
}
|
||||
|
||||
// Another test case for the add method for good measure
|
||||
TEST_F(CalculatorTest, AddWithZero) {
|
||||
EXPECT_EQ(calc.add(0, 5), 5);
|
||||
EXPECT_EQ(calc.add(5, 0), 5);
|
||||
EXPECT_EQ(calc.add(0, 0), 0);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Updating the Build System (CMake)
|
||||
|
||||
This is the most critical part. We have updated our `CMakeLists.txt` to build both our main application and our tests as separate executables.
|
||||
|
||||
**File:** `tutorial-1/CMakeLists.txt` (Updated)
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(Tutorial1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Find dependencies
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
|
||||
# Create a library for our application code
|
||||
# This allows us to share the code between the main app and the tests
|
||||
add_library(MyLib src/calculator.cpp src/calculator.h)
|
||||
target_include_directories(MyLib PUBLIC src)
|
||||
|
||||
# Define our main executable
|
||||
add_executable(App src/main.cpp)
|
||||
|
||||
# Link our App against spdlog and our library
|
||||
target_link_libraries(App PRIVATE spdlog::spdlog MyLib)
|
||||
|
||||
# Enable testing with CTest (CMake's testing framework)
|
||||
enable_testing()
|
||||
|
||||
# Define the test executable
|
||||
add_executable(RunTests test/test_calculator.cpp)
|
||||
|
||||
# Link our test executable against GTest and our library
|
||||
target_link_libraries(RunTests PRIVATE GTest::GTest GTest::Main MyLib)
|
||||
|
||||
# Add the test to CTest
|
||||
include(GoogleTest)
|
||||
gtest_add_tests(TARGET RunTests)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Building and Running Tests in VSCode
|
||||
|
||||
1. **Install `gtest`:** Make sure you have installed `gtest` using `vcpkg` as described in step 2.
|
||||
2. **Configure CMake:** Run `CMake: Configure` from the command palette (Ctrl+Shift+P). It should detect the changes and find `gtest`.
|
||||
3. **Build:** Click the **Build** button in the status bar. This will now build `App` and `RunTests`.
|
||||
4. **Run Tests:** The "CMake Tools" extension provides a "Test Explorer" view in VSCode.
|
||||
* Open the Test Explorer from the side bar (the icon looks like a beaker).
|
||||
* You should see your test suite (`CalculatorTest`) and the individual tests.
|
||||
* Click the "play" button at the top of the Test Explorer to run all tests.
|
||||
* You can also run individual tests. The results will be shown directly in the UI.
|
||||
|
||||
---
|
||||
|
||||
**Congratulations!** You have now added unit testing to your project. This is a crucial skill for writing robust and maintainable C++ applications. You've learned how to:
|
||||
* Add a testing library (`googletest`) with `vcpkg`.
|
||||
* Structure your project to separate app code from test code.
|
||||
* Create a shared library and multiple executables with CMake.
|
||||
* Write and run unit tests using the VSCode Test Explorer.
|
||||
|
||||
|
||||
## Feedback and Questions
|
||||
|
||||
1. The line ```target_link_libraries(RunTests PRIVATE GTest::GTest GTest::Main MyLib)``` caused cmake compilation error, I changed to
|
||||
```target_link_libraries(RunTests PRIVATE GTest::gtest_main MyLib)```
|
||||
I looked up documentation for this.
|
||||
|
||||
2. Are there alternative ways to install dependencies other the through vcpkg? For example would FetchContent work (https://cmake.org/cmake/help/latest/module/FetchContent.html)?
|
||||
|
||||
3.Interestingly, in vscode, the line ```#include <gtest/gtest.h>``` i was curious where the files as located - as i had not specified where it is other the download gtest in a separate process via vcpkg. when in vscode i hovered and clicked on go to definition it went to the location. curious how vscode knows this information.
|
||||
|
||||
4.I was interested to debug unit tests individually and managed to do this by following https://github.com/microsoft/vscode-cmake-tools/blob/main/docs/debug-launch.md#debugging-tests
|
||||
6
cplusplus/geminitutorial/tutorial2/.vscode/settings.json
vendored
Normal file
6
cplusplus/geminitutorial/tutorial2/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"cmake.sourceDirectory": "/home/ys/code/cplusplus/GeminiTutorial/tutorial-2",
|
||||
"cmake.configureArgs": [
|
||||
"-DCMAKE_TOOLCHAIN_FILE=/home/ys/code/cplusplus/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||
]
|
||||
}
|
||||
31
cplusplus/geminitutorial/tutorial2/CMakeLists.txt
Normal file
31
cplusplus/geminitutorial/tutorial2/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(Tutorial2 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
||||
|
||||
# Find dependencies
|
||||
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
|
||||
|
||||
|
||||
# Create a library for our application code
|
||||
|
||||
add_library(MyLib src/StockData_Old.cpp src/StockData_Modern.cpp src/CsvParser.cpp)
|
||||
|
||||
target_include_directories(MyLib PUBLIC src)
|
||||
|
||||
|
||||
|
||||
# Define our main executable
|
||||
|
||||
add_executable(App src/main.cpp)
|
||||
|
||||
|
||||
|
||||
# Link our App against spdlog and our library
|
||||
|
||||
target_link_libraries(App PRIVATE spdlog::spdlog MyLib)
|
||||
73
cplusplus/geminitutorial/tutorial2/src/CsvParser.cpp
Normal file
73
cplusplus/geminitutorial/tutorial2/src/CsvParser.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "CsvParser.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <algorithm> // For std::remove_if
|
||||
|
||||
namespace CsvParser {
|
||||
|
||||
TickData parseTickCsvLine(std::string_view line) {
|
||||
TickData tick;
|
||||
std::string s_line(line); // Convert string_view to string for stringstream
|
||||
std::stringstream ss(s_line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while(std::getline(ss, segment, ',')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
if (seglist.size() != 3) {
|
||||
throw std::runtime_error("CSV line does not have 3 segments: " + std::string(line));
|
||||
}
|
||||
|
||||
// Parse timestamp (simplified for now, assumes epoch seconds)
|
||||
// In a real scenario, this would involve more robust date/time parsing
|
||||
try {
|
||||
long long timestamp_sec = std::stoll(seglist[0]);
|
||||
tick.timestamp = std::chrono::system_clock::from_time_t(timestamp_sec);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to parse timestamp: " + seglist[0] + " - " + e.what());
|
||||
}
|
||||
|
||||
// Parse price
|
||||
try {
|
||||
tick.price = std::stod(seglist[1]);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to parse price: " + seglist[1] + " - " + e.what());
|
||||
}
|
||||
|
||||
// Parse volume
|
||||
try {
|
||||
tick.volume = std::stol(seglist[2]);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to parse volume: " + seglist[2] + " - " + e.what());
|
||||
}
|
||||
|
||||
return tick;
|
||||
}
|
||||
|
||||
std::vector<TickData> parseTickCsvFile(std::string_view filename)
|
||||
{
|
||||
std::vector<TickData> ticks;
|
||||
std::ifstream file(std::string{filename}); // Convert string_view to string for ifstream
|
||||
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("Could not open file: " + std::string(filename));
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (line.empty()) continue; // Skip empty lines
|
||||
try {
|
||||
ticks.push_back(parseTickCsvLine(line));
|
||||
} catch (const std::runtime_error& e) {
|
||||
std::cerr << "Error parsing line: \"" << line << "\" - " << e.what() << std::endl;
|
||||
// Depending on requirements, could rethrow or continue
|
||||
}
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
|
||||
} // namespace CsvParser
|
||||
11
cplusplus/geminitutorial/tutorial2/src/CsvParser.h
Normal file
11
cplusplus/geminitutorial/tutorial2/src/CsvParser.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "TickData.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string_view> // For std::string_view
|
||||
|
||||
namespace CsvParser {
|
||||
TickData parseTickCsvLine(std::string_view line);
|
||||
std::vector<TickData> parseTickCsvFile(std::string_view filename);
|
||||
} // namespace CsvParser
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "StockData_Modern.h"
|
||||
|
||||
// Constructor: Initialize the vector with a given size
|
||||
StockData_Modern::StockData_Modern(size_t size) : data_(size) {}
|
||||
|
||||
// Public method to access data (with bounds checking provided by std::vector::at)
|
||||
double& StockData_Modern::at(size_t index) {
|
||||
return data_.at(index);
|
||||
}
|
||||
19
cplusplus/geminitutorial/tutorial2/src/StockData_Modern.h
Normal file
19
cplusplus/geminitutorial/tutorial2/src/StockData_Modern.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <cstddef> // For size_t
|
||||
|
||||
#include <vector>
|
||||
|
||||
class StockData_Modern {
|
||||
public:
|
||||
// Constructor
|
||||
StockData_Modern(size_t size);
|
||||
|
||||
// No Copy Constructor, Copy Assignment, or Destructor needed!
|
||||
// The compiler-generated versions work perfectly because `std::vector` handles itself.
|
||||
|
||||
// Public method to access data
|
||||
double& at(size_t index);
|
||||
|
||||
private:
|
||||
std::vector<double> data_;
|
||||
};
|
||||
37
cplusplus/geminitutorial/tutorial2/src/StockData_Old.cpp
Normal file
37
cplusplus/geminitutorial/tutorial2/src/StockData_Old.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "StockData_Old.h"
|
||||
#include <algorithm> // For std::copy
|
||||
|
||||
// Constructor: Allocate memory for the array
|
||||
StockData_Old::StockData_Old(size_t size) : size_(size) {
|
||||
data_ = new double[size_];
|
||||
}
|
||||
|
||||
// Destructor: Free the allocated memory
|
||||
StockData_Old::~StockData_Old() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
// Copy Constructor: Perform a deep copy
|
||||
StockData_Old::StockData_Old(const StockData_Old& other) : size_(other.size_) {
|
||||
data_ = new double[size_];
|
||||
std::copy(other.data_, other.data_ + size_, data_);
|
||||
}
|
||||
|
||||
// Copy Assignment Operator: Handle self-assignment and perform a deep copy
|
||||
StockData_Old& StockData_Old::operator=(const StockData_Old& other) {
|
||||
if (this != &other) { // Protect against self-assignment
|
||||
// Free the old memory
|
||||
delete[] data_;
|
||||
|
||||
// Allocate new memory and copy data
|
||||
size_ = other.size_;
|
||||
data_ = new double[size_];
|
||||
std::copy(other.data_, other.data_ + size_, data_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Public method to access data (no bounds checking for simplicity)
|
||||
double& StockData_Old::at(size_t index) {
|
||||
return data_[index];
|
||||
}
|
||||
25
cplusplus/geminitutorial/tutorial2/src/StockData_Old.h
Normal file
25
cplusplus/geminitutorial/tutorial2/src/StockData_Old.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once // Use modern include guard
|
||||
|
||||
#include <cstddef> // For size_t
|
||||
|
||||
class StockData_Old {
|
||||
public:
|
||||
// Constructor
|
||||
StockData_Old(size_t size);
|
||||
|
||||
// Copy Constructor (Rule 1 of 3)
|
||||
StockData_Old(const StockData_Old& other);
|
||||
|
||||
// Copy Assignment Operator (Rule 2 of 3)
|
||||
StockData_Old& operator=(const StockData_Old& other);
|
||||
|
||||
// Destructor (Rule 3 of 3)
|
||||
~StockData_Old();
|
||||
|
||||
// Public method to access data
|
||||
double& at(size_t index);
|
||||
|
||||
private:
|
||||
double* data_;
|
||||
size_t size_;
|
||||
};
|
||||
25
cplusplus/geminitutorial/tutorial2/src/TickData.h
Normal file
25
cplusplus/geminitutorial/tutorial2/src/TickData.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <sstream> // For std::ostringstream
|
||||
|
||||
struct TickData {
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
double price;
|
||||
long volume;
|
||||
|
||||
// Optional: for easy printing/debugging
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
// Convert timestamp to a readable format (simple example)
|
||||
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
|
||||
std::string time_str = std::ctime(&time);
|
||||
time_str.pop_back(); // Remove trailing newline
|
||||
|
||||
oss << "Timestamp: " << time_str
|
||||
<< ", Price: " << price
|
||||
<< ", Volume: " << volume;
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
26
cplusplus/geminitutorial/tutorial2/src/Trade.h
Normal file
26
cplusplus/geminitutorial/tutorial2/src/Trade.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
|
||||
class Trade {
|
||||
public:
|
||||
Trade(const std::string& instrument, double price)
|
||||
: instrument_(instrument), price_(price) {
|
||||
spdlog::info("Trade constructor: {} @ {}", instrument_, price_);
|
||||
}
|
||||
|
||||
~Trade() {
|
||||
spdlog::info("Trade destructor: {} @ {}", instrument_, price_);
|
||||
}
|
||||
|
||||
void print() const {
|
||||
spdlog::info("Executing trade: {} for ${}", instrument_, price_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string instrument_;
|
||||
double price_;
|
||||
};
|
||||
202
cplusplus/geminitutorial/tutorial2/src/main.cpp
Normal file
202
cplusplus/geminitutorial/tutorial2/src/main.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "StockData_Old.h"
|
||||
#include "StockData_Modern.h"
|
||||
#include "Trade.h"
|
||||
#include "TickData.h"
|
||||
#include "CsvParser.h"
|
||||
|
||||
#include <memory> // For smart pointers
|
||||
#include <iostream> // For std::cout, std::cerr
|
||||
#include <vector> // For std::vector
|
||||
#include <numeric> // For std::accumulate
|
||||
#include <algorithm> // For std::max_element, std::for_each, std::count_if
|
||||
#include <map> // For std::map (instead of unordered_map for simplicity in example)
|
||||
#include <string_view> // For std::string_view
|
||||
#include <fstream> // For std::ofstream
|
||||
|
||||
// --- Existing Tutorial 2.1 Demonstrations ---
|
||||
|
||||
void demonstrate_old_way() {
|
||||
spdlog::info("--- Demonstrating the Old Way (Manual Memory Management) ---");
|
||||
StockData_Old prices(5);
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
prices.at(i) = 100.0 + i;
|
||||
}
|
||||
|
||||
spdlog::info("Copied prices_copy1(prices)");
|
||||
StockData_Old prices_copy1(prices); // Copy constructor
|
||||
prices_copy1.at(0) = 999.0;
|
||||
|
||||
spdlog::info("prices.at(0) = {}", prices.at(0));
|
||||
spdlog::info("prices_copy1.at(0) = {}", prices_copy1.at(0));
|
||||
|
||||
spdlog::info("Copied prices_copy2 = prices");
|
||||
StockData_Old prices_copy2(1);
|
||||
prices_copy2 = prices; // Copy assignment
|
||||
prices_copy2.at(1) = 888.0;
|
||||
|
||||
spdlog::info("prices.at(1) = {}", prices.at(1));
|
||||
spdlog::info("prices_copy2.at(1) = {}", prices_copy2.at(1));
|
||||
|
||||
spdlog::info("--- Leaving scope for Old Way demo ---");
|
||||
}
|
||||
|
||||
void demonstrate_modern_way() {
|
||||
spdlog::info("--- Demonstrating the Modern Way (RAII with std::vector) ---");
|
||||
StockData_Modern prices(5);
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
prices.at(i) = 100.0 + i;
|
||||
}
|
||||
|
||||
spdlog::info("Copied prices_copy1(prices)");
|
||||
StockData_Modern prices_copy1(prices); // Copy constructor (compiler-generated)
|
||||
prices_copy1.at(0) = 999.0;
|
||||
|
||||
spdlog::info("prices.at(0) = {}", prices.at(0));
|
||||
spdlog::info("prices_copy1.at(0) = {}", prices_copy1.at(0));
|
||||
|
||||
spdlog::info("Copied prices_copy2 = prices");
|
||||
StockData_Modern prices_copy2(1);
|
||||
prices_copy2 = prices; // Copy assignment (compiler-generated)
|
||||
prices_copy2.at(1) = 888.0;
|
||||
|
||||
spdlog::info("prices.at(1) = {}", prices.at(1));
|
||||
spdlog::info("prices_copy2.at(1) = {}", prices_copy2.at(1));
|
||||
spdlog::info("--- Leaving scope for Modern Way demo ---");
|
||||
}
|
||||
|
||||
void demonstrate_smart_pointers() {
|
||||
spdlog::info("--- Demonstrating Smart Pointers ---");
|
||||
|
||||
// Demonstrate unique_ptr for exclusive ownership
|
||||
{
|
||||
spdlog::info("Creating a unique_ptr-managed Trade");
|
||||
auto unique_trade = std::make_unique<Trade>("AAPL", 150.0);
|
||||
unique_trade->print();
|
||||
// unique_trade is automatically destroyed when this scope ends
|
||||
} // ~Trade() is called here
|
||||
|
||||
// Demonstrate shared_ptr for shared ownership
|
||||
{
|
||||
spdlog::info("Creating a shared_ptr-managed Trade");
|
||||
std::shared_ptr<Trade> shared_trade1;
|
||||
{
|
||||
auto shared_trade2 = std::make_shared<Trade>("GOOG", 2800.0);
|
||||
shared_trade1 = shared_trade2; // Both pointers now share ownership
|
||||
spdlog::info("shared_ptr use_count: {}", shared_trade1.use_count());
|
||||
shared_trade2->print();
|
||||
} // shared_trade2 goes out of scope, but the object is not destroyed
|
||||
|
||||
spdlog::info("shared_ptr use_count after inner scope: {}", shared_trade1.use_count());
|
||||
shared_trade1->print();
|
||||
// shared_trade1 is automatically destroyed when this scope ends
|
||||
} // ~Trade() is called here
|
||||
spdlog::info("--- Leaving scope for Smart Pointers demo ---");
|
||||
}
|
||||
|
||||
// --- New Tutorial 2.2 Demonstrations ---
|
||||
|
||||
void generate_mock_csv_file(std::string_view filename, int num_lines) {
|
||||
spdlog::info("Generating mock CSV file: {}", std::string(filename));
|
||||
std::ofstream ofs(std::string{filename});
|
||||
if (!ofs.is_open()) {
|
||||
throw std::runtime_error("Could not create mock CSV file.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_lines; ++i) {
|
||||
long long timestamp = std::chrono::system_clock::now().time_since_epoch().count() / 1000000000LL + i;
|
||||
double price = 100.0 + (i * 0.5);
|
||||
long volume = 100 + (i * 10);
|
||||
ofs << timestamp << "," << price << "," << volume << "\n";
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
|
||||
void demonstrate_stl_containers_algorithms() {
|
||||
spdlog::info("--- Demonstrating Modern STL Containers & Algorithms (Tutorial 2.2) ---");
|
||||
|
||||
const std::string_view csv_filename = "mock_tick_data.csv";
|
||||
generate_mock_csv_file(csv_filename, 10);
|
||||
|
||||
std::vector<TickData> ticks;
|
||||
try {
|
||||
ticks = CsvParser::parseTickCsvFile(csv_filename);
|
||||
spdlog::info("Successfully parsed {} ticks.", ticks.size());
|
||||
} catch (const std::runtime_error& e) {
|
||||
spdlog::error("Error parsing CSV file: {}", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
// Example 1: Find the highest price
|
||||
auto max_price_it = std::max_element(ticks.begin(), ticks.end(),
|
||||
[](const TickData& a, const TickData& b) {
|
||||
return a.price < b.price;
|
||||
});
|
||||
if (max_price_it != ticks.end()) {
|
||||
spdlog::info("Highest price found: {}", max_price_it->price);
|
||||
}
|
||||
|
||||
// Example 2: Calculate total volume
|
||||
long total_volume = std::accumulate(ticks.begin(), ticks.end(), 0LL,
|
||||
[](long sum, const TickData& tick) {
|
||||
return sum + tick.volume;
|
||||
});
|
||||
spdlog::info("Total volume: {}", total_volume);
|
||||
|
||||
// Example 3: Filter ticks above a certain price (e.g., > 102.0)
|
||||
std::vector<TickData> high_price_ticks;
|
||||
std::copy_if(ticks.begin(), ticks.end(), std::back_inserter(high_price_ticks),
|
||||
[](const TickData& tick) {
|
||||
return tick.price > 102.0;
|
||||
});
|
||||
spdlog::info("Number of ticks with price > 102.0: {}", high_price_ticks.size());
|
||||
// std::for_each(high_price_ticks.begin(), high_price_ticks.end(),
|
||||
// [](const TickData& tick) { spdlog::info(" {}", tick.toString()); });
|
||||
|
||||
// Example 4: Average price using a lambda
|
||||
double sum_prices = 0.0;
|
||||
std::for_each(ticks.begin(), ticks.end(),
|
||||
[&sum_prices](const TickData& tick) {
|
||||
sum_prices += tick.price;
|
||||
});
|
||||
if (!ticks.empty()) {
|
||||
spdlog::info("Average price: {}", sum_prices / ticks.size());
|
||||
}
|
||||
|
||||
// Example 5: Aggregate volume by second (using std::map for simplicity)
|
||||
// For high performance, a custom hash map or a more direct time-series approach would be used.
|
||||
std::map<long long, long> volume_by_second;
|
||||
for (const auto& tick : ticks) {
|
||||
long long sec = std::chrono::duration_cast<std::chrono::seconds>(tick.timestamp.time_since_epoch()).count();
|
||||
volume_by_second[sec] += tick.volume;
|
||||
}
|
||||
|
||||
spdlog::info("Volume by second (first 3 entries):");
|
||||
int count = 0;
|
||||
for (const auto& entry : volume_by_second) {
|
||||
if (count >= 3) break;
|
||||
spdlog::info(" Timestamp (sec): {}, Volume: {}", entry.first, entry.second);
|
||||
count++;
|
||||
}
|
||||
|
||||
spdlog::info("--- Leaving scope for STL Containers & Algorithms demo ---");
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
spdlog::info("Starting Tutorial 2: Core Language, Libraries, and Concepts");
|
||||
|
||||
demonstrate_old_way();
|
||||
std::cout << "\n";
|
||||
demonstrate_modern_way();
|
||||
std::cout << "\n";
|
||||
demonstrate_smart_pointers();
|
||||
std::cout << "\n";
|
||||
demonstrate_stl_containers_algorithms();
|
||||
|
||||
|
||||
spdlog::info("Tutorial 2 finished.");
|
||||
return 0;
|
||||
}
|
||||
109
cplusplus/geminitutorial/tutorial2/tutorial-2.1-plan.md
Normal file
109
cplusplus/geminitutorial/tutorial2/tutorial-2.1-plan.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Tutorial 2.1 Plan: Resource Management & The Rule of Zero
|
||||
|
||||
**Objective:** To master modern C++ resource management techniques, moving away from manual memory handling (`new`/`delete`) to the robust RAII (Resource Acquisition Is Initialization) idiom. This tutorial will demonstrate the "Rule of Zero" by refactoring a C++98-style class that requires manual memory management into a modern, safer equivalent using standard library containers and smart pointers.
|
||||
|
||||
---
|
||||
|
||||
## 1. Project Setup
|
||||
|
||||
To keep our tutorials organized, we will create a new directory for this lesson.
|
||||
|
||||
1. **Create a `tutorial-2` directory:** Inside the `GeminiTutorial` project root.
|
||||
2. **Copy Configuration:** Copy the `CMakeLists.txt` and the `.vscode` directory from `tutorial-1` into the new `tutorial-2` directory. This preserves our build and debug configurations.
|
||||
3. **Update `CMakeLists.txt`:** Modify the `project()` name at the top of `tutorial-2/CMakeLists.txt` from `Tutorial1` to `Tutorial2`.
|
||||
|
||||
---
|
||||
|
||||
## 2. The "Old Way": Manual Memory Management (The Rule of Three/Five)
|
||||
|
||||
First, we'll create a class that manually manages a dynamic array of doubles. This will highlight the complexity and risks of the C++98 approach.
|
||||
|
||||
**File:** `tutorial-2/src/StockData_Old.h`
|
||||
```cpp
|
||||
#pragma once // Use modern include guard
|
||||
|
||||
#include <cstddef> // For size_t
|
||||
|
||||
class StockData_Old {
|
||||
public:
|
||||
// Constructor
|
||||
StockData_Old(size_t size);
|
||||
|
||||
// Copy Constructor (Rule 1 of 3)
|
||||
StockData_Old(const StockData_Old& other);
|
||||
|
||||
// Copy Assignment Operator (Rule 2 of 3)
|
||||
StockData_Old& operator=(const StockData_Old& other);
|
||||
|
||||
// Destructor (Rule 3 of 3)
|
||||
~StockData_Old();
|
||||
|
||||
// Public method to access data
|
||||
double& at(size_t index);
|
||||
|
||||
private:
|
||||
double* data_;
|
||||
size_t size_;
|
||||
};
|
||||
```
|
||||
We will also create the corresponding `.cpp` file to implement these special member functions, demonstrating the need for deep copies and manual cleanup.
|
||||
|
||||
---
|
||||
|
||||
## 3. The "Modern Way": Automatic Resource Management (The Rule of Zero)
|
||||
|
||||
Next, we will create a modern equivalent that leverages `std::vector`. This class will achieve the same functionality with significantly less code and greater safety.
|
||||
|
||||
**File:** `tutorial-2/src/StockData_Modern.h`
|
||||
```cpp
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
class StockData_Modern {
|
||||
public:
|
||||
// Constructor
|
||||
StockData_Modern(size_t size);
|
||||
|
||||
// No Copy Constructor, Copy Assignment, or Destructor needed!
|
||||
// The compiler-generated versions work perfectly because `std::vector` handles itself.
|
||||
|
||||
// Public method to access data
|
||||
double& at(size_t index);
|
||||
|
||||
private:
|
||||
std::vector<double> data_;
|
||||
};
|
||||
```
|
||||
This class adheres to the **Rule of Zero**, as it owns its resources through a standard library container and requires no custom resource management code.
|
||||
|
||||
---
|
||||
|
||||
## 4. Introducing Smart Pointers for Single Object Ownership
|
||||
|
||||
While containers are for sequences of objects, smart pointers manage the lifetime of a single dynamically allocated object.
|
||||
|
||||
1. **`std::unique_ptr`:** Represents exclusive ownership. The object is destroyed automatically when the `unique_ptr` goes out of scope.
|
||||
2. **`std::shared_ptr`:** Represents shared ownership. The object is destroyed only when the last `shared_ptr` pointing to it is destroyed.
|
||||
|
||||
We will create a simple `Trade` class and a `main.cpp` to demonstrate creating and managing `Trade` objects on the heap using both `std::unique_ptr` and `std::shared_ptr`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Updating the Build System and Main Application
|
||||
|
||||
We will modify `tutorial-2/CMakeLists.txt` to include all our new source files (`.cpp` files for `StockData_Old`, `StockData_Modern`, and the new `main.cpp`).
|
||||
|
||||
The `main.cpp` will first use `StockData_Old` to illustrate its usage (and potential pitfalls), then use `StockData_Modern` to show the simplicity and safety of the modern approach, and finally demonstrate the usage of smart pointers.
|
||||
|
||||
---
|
||||
|
||||
## 6. Summary and Key Takeaways
|
||||
|
||||
This tutorial will provide hands-on experience with the core principles of modern C++ resource management:
|
||||
* **RAII:** Let objects manage resources. Acquisition is in the constructor, release is in the destructor.
|
||||
* **Rule of Zero:** By using standard library classes like `std::vector`, `std::string`, `std::unique_ptr`, and `std::shared_ptr` to handle resource ownership, your own classes often don't need any custom destructor, copy/move constructors, or assignment operators.
|
||||
* **Expressing Ownership:** Choose the right tool for the job:
|
||||
* `std::vector` for owning a dynamic array of elements.
|
||||
* `std::unique_ptr` for exclusive ownership of a single heap-allocated object.
|
||||
* `std::shared_ptr` for shared ownership of a single heap-allocated object.
|
||||
10
cplusplus/geminitutorial/tutorial2/tutorial-2.2-followup.md
Normal file
10
cplusplus/geminitutorial/tutorial2/tutorial-2.2-followup.md
Normal file
@@ -0,0 +1,10 @@
|
||||
Take Aways:
|
||||
|
||||
1. std::string_view: manipulation of strings without the costs of ownership:
|
||||
i. copying and storing large string objects
|
||||
implemented as an type storing the first element and lenght of string
|
||||
see [link](https://stackoverflow.com/questions/20803826/what-is-string-view)
|
||||
|
||||
2. Explain sync pattern or idiom in relation to string_view
|
||||
3. clang-tidy static checking -how does it work?
|
||||
|
||||
83
cplusplus/geminitutorial/tutorial2/tutorial-2.2-plan.md
Normal file
83
cplusplus/geminitutorial/tutorial2/tutorial-2.2-plan.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Tutorial 2.2 Plan: Modern STL Containers & Algorithms
|
||||
|
||||
**Objective:** Use the workhorse containers and algorithms with a focus on performance. The project involves parsing mock CSV tick data using algorithms and lambdas to perform queries.
|
||||
|
||||
---
|
||||
|
||||
## 1. Project Setup
|
||||
|
||||
We will continue working within the `tutorial-2` directory. No new directory creation is needed.
|
||||
|
||||
## 2. Defining Tick Data Structure
|
||||
|
||||
We'll define a simple struct to represent a tick, containing a timestamp, price, and volume.
|
||||
|
||||
**File:** `tutorial-2/src/TickData.h`
|
||||
```cpp
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
struct TickData {
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
double price;
|
||||
long volume;
|
||||
|
||||
// Optional: for easy printing/debugging
|
||||
std::string toString() const;
|
||||
};
|
||||
```
|
||||
|
||||
## 3. CSV Parsing Utility
|
||||
|
||||
We'll implement a function to parse a single line of CSV data into a `TickData` object. This will involve string manipulation and conversion. `std::string_view` will be crucial here for zero-copy parsing.
|
||||
|
||||
**File:** `tutorial-2/src/CsvParser.h`
|
||||
```cpp
|
||||
#pragma once
|
||||
|
||||
#include "TickData.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace CsvParser {
|
||||
TickData parseTickCsvLine(std::string_view line);
|
||||
std::vector<TickData> parseTickCsvFile(std::string_view filename);
|
||||
} // namespace CsvParser
|
||||
```
|
||||
|
||||
**File:** `tutorial-2/src/CsvParser.cpp`
|
||||
```cpp
|
||||
#include "CsvParser.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <iostream> // For error reporting
|
||||
|
||||
// Implement parseTickCsvLine and parseTickCsvFile
|
||||
```
|
||||
|
||||
## 4. Main Application: Parsing and Querying
|
||||
|
||||
In `tutorial-2/src/main.cpp`, we will:
|
||||
1. Generate some mock CSV tick data (or read from a static file).
|
||||
2. Use `CsvParser::parseTickCsvFile` to load the data into a `std::vector<TickData>`.
|
||||
3. Perform various queries using modern STL algorithms and lambdas:
|
||||
* Find the highest price.
|
||||
* Calculate the total volume.
|
||||
* Filter ticks above a certain price.
|
||||
* Find the average price within a time range.
|
||||
* Use `std::unordered_map` to aggregate volume by some key (e.g., minute of the hour).
|
||||
|
||||
## 5. Updating the Build System
|
||||
|
||||
Modify `tutorial-2/CMakeLists.txt` to include `TickData.cpp` and `CsvParser.cpp` (if needed, `TickData` might just be a header). Ensure proper linking.
|
||||
|
||||
## 6. Summary and Key Takeaways
|
||||
|
||||
This tutorial will provide hands-on experience with:
|
||||
* Efficient string handling using `std::string_view`.
|
||||
* Storing structured data in `std::vector`.
|
||||
* Performing data transformations and queries using STL algorithms and lambdas.
|
||||
* Using `std::unordered_map` for fast lookups and aggregations.
|
||||
Reference in New Issue
Block a user