initial commit

This commit is contained in:
dl92
2026-01-06 15:05:27 +00:00
parent a94f174a39
commit 62ab80b1d6
25 changed files with 1291 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# GeminiTutorial

View 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.

View 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.

View 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"
]
}

View 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)

View File

@@ -0,0 +1,5 @@
#include "calculator.h"
int Calculator::add(int a, int b) {
return a + b;
}

View File

@@ -0,0 +1,9 @@
#ifndef CALCULATOR_H
#define CALCULATOR_H
class Calculator {
public:
int add(int a, int b);
};
#endif // CALCULATOR_H

View 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;
}

View 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);
}

View 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

View 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

View 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"
]
}

View 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)

View 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

View 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

View File

@@ -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);
}

View 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_;
};

View 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];
}

View 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_;
};

View 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();
}
};

View 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_;
};

View 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;
}

View 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.

View 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?

View 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.