From 83e5aaa9bbbfb69477fcaddcda6c4c88f0730897 Mon Sep 17 00:00:00 2001 From: Champbreed Date: Mon, 23 Mar 2026 18:55:53 +0100 Subject: [PATCH] fix: restore missing cpp-pro reference documentation (Issue #382) (#383) * fix: restore missing cpp-pro reference documentation (Issue #382) * docs: restore implementation-playbook with idiomatic C++ patterns * Update implementation-playbook.md * Update implementation-playbook.md --- skills/cpp-pro/references/build-tooling.md | 440 ++++++++++++++++++ skills/cpp-pro/references/concurrency.md | 437 +++++++++++++++++ .../cpp-pro/references/memory-performance.md | 397 ++++++++++++++++ skills/cpp-pro/references/modern-cpp.md | 304 ++++++++++++ skills/cpp-pro/references/templates.md | 357 ++++++++++++++ .../resources/implementation-playbook.md | 43 ++ 6 files changed, 1978 insertions(+) create mode 100644 skills/cpp-pro/references/build-tooling.md create mode 100644 skills/cpp-pro/references/concurrency.md create mode 100644 skills/cpp-pro/references/memory-performance.md create mode 100644 skills/cpp-pro/references/modern-cpp.md create mode 100644 skills/cpp-pro/references/templates.md create mode 100644 skills/cpp-pro/resources/implementation-playbook.md diff --git a/skills/cpp-pro/references/build-tooling.md b/skills/cpp-pro/references/build-tooling.md new file mode 100644 index 00000000..3d413063 --- /dev/null +++ b/skills/cpp-pro/references/build-tooling.md @@ -0,0 +1,440 @@ +# Build Systems and Tooling + +## Modern CMake + +```cmake +cmake_minimum_required(VERSION 3.20) +project(MyProject VERSION 1.0.0 LANGUAGES CXX) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Export compile commands for tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Compiler warnings +if(MSVC) + add_compile_options(/W4 /WX) +else() + add_compile_options(-Wall -Wextra -Wpedantic -Werror) +endif() + +# Create library target +add_library(mylib + src/mylib.cpp + include/mylib.h +) + +target_include_directories(mylib + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + +target_compile_features(mylib PUBLIC cxx_std_20) + +# Create executable +add_executable(myapp src/main.cpp) +target_link_libraries(myapp PRIVATE mylib) + +# Dependencies with FetchContent +include(FetchContent) + +FetchContent_Declare( + fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 10.1.1 +) +FetchContent_MakeAvailable(fmt) + +target_link_libraries(mylib PUBLIC fmt::fmt) + +# Testing +enable_testing() +add_subdirectory(tests) + +# Install rules +include(GNUInstallDirs) +install(TARGETS mylib myapp + EXPORT MyProjectTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +``` + +## Sanitizers + +```cmake +# AddressSanitizer (ASan) - memory errors +set(CMAKE_CXX_FLAGS_ASAN + "-g -O1 -fsanitize=address -fno-omit-frame-pointer" + CACHE STRING "Flags for ASan build" +) + +# UndefinedBehaviorSanitizer (UBSan) +set(CMAKE_CXX_FLAGS_UBSAN + "-g -O1 -fsanitize=undefined -fno-omit-frame-pointer" + CACHE STRING "Flags for UBSan build" +) + +# ThreadSanitizer (TSan) - data races +set(CMAKE_CXX_FLAGS_TSAN + "-g -O1 -fsanitize=thread -fno-omit-frame-pointer" + CACHE STRING "Flags for TSan build" +) + +# MemorySanitizer (MSan) - uninitialized reads +set(CMAKE_CXX_FLAGS_MSAN + "-g -O1 -fsanitize=memory -fno-omit-frame-pointer" + CACHE STRING "Flags for MSan build" +) + +# Usage: cmake -DCMAKE_BUILD_TYPE=ASAN .. +``` + +## Static Analysis + +```yaml +# .clang-tidy configuration +--- +Checks: > + *, + -fuchsia-*, + -google-*, + -llvm-*, + -modernize-use-trailing-return-type, + -readability-identifier-length + +WarningsAsErrors: '*' + +CheckOptions: + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: lower_case + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.ConstantCase + value: UPPER_CASE + - key: readability-identifier-naming.MemberCase + value: lower_case + - key: readability-identifier-naming.MemberSuffix + value: '_' + - key: modernize-use-nullptr.NullMacros + value: 'NULL' +``` + +```bash +# Run clang-tidy +clang-tidy src/*.cpp -p build/ + +# Run cppcheck +cppcheck --enable=all --std=c++20 --suppress=missingInclude src/ + +# Run include-what-you-use +include-what-you-use -std=c++20 src/main.cpp +``` + +## Testing with Catch2 + +```cpp +#include +#include +#include "mylib.h" + +TEST_CASE("Vector operations", "[vector]") { + std::vector vec{1, 2, 3}; + + SECTION("push_back") { + vec.push_back(4); + REQUIRE(vec.size() == 4); + REQUIRE(vec.back() == 4); + } + + SECTION("pop_back") { + vec.pop_back(); + REQUIRE(vec.size() == 2); + REQUIRE(vec.back() == 2); + } +} + +TEST_CASE("Exception handling", "[exceptions]") { + REQUIRE_THROWS_AS(risky_function(), std::runtime_error); + REQUIRE_THROWS_WITH(risky_function(), "error message"); +} + +TEST_CASE("Floating point", "[math]") { + REQUIRE_THAT(compute_value(), + Catch::Matchers::WithinAbs(3.14, 0.01)); +} + +BENCHMARK("Vector creation") { + return std::vector(1000); +}; + +BENCHMARK("Vector fill") { + std::vector vec(1000); + for (int i = 0; i < 1000; ++i) { + vec[i] = i; + } + return vec; +}; +``` + +## Testing with GoogleTest + +```cpp +#include +#include +#include "calculator.h" + +class CalculatorTest : public ::testing::Test { +protected: + void SetUp() override { + calc = std::make_unique(); + } + + void TearDown() override { + calc.reset(); + } + + std::unique_ptr calc; +}; + +TEST_F(CalculatorTest, Addition) { + EXPECT_EQ(calc->add(2, 3), 5); + EXPECT_EQ(calc->add(-1, 1), 0); +} + +TEST_F(CalculatorTest, Division) { + EXPECT_DOUBLE_EQ(calc->divide(10, 2), 5.0); + EXPECT_THROW(calc->divide(10, 0), std::invalid_argument); +} + +// Parameterized tests +class AdditionTest : public ::testing::TestWithParam> {}; + +TEST_P(AdditionTest, ValidAddition) { + auto [a, b, expected] = GetParam(); + Calculator calc; + EXPECT_EQ(calc.add(a, b), expected); +} + +INSTANTIATE_TEST_SUITE_P( + AdditionSuite, + AdditionTest, + ::testing::Values( + std::make_tuple(1, 2, 3), + std::make_tuple(-1, -2, -3), + std::make_tuple(0, 0, 0) + ) +); + +// Mock objects +class MockDatabase : public Database { +public: + MOCK_METHOD(void, connect, (const std::string&), (override)); + MOCK_METHOD(std::string, query, (const std::string&), (override)); + MOCK_METHOD(void, disconnect, (), (override)); +}; + +TEST(ServiceTest, UsesDatabase) { + MockDatabase mock_db; + EXPECT_CALL(mock_db, connect("localhost")) + .Times(1); + EXPECT_CALL(mock_db, query("SELECT *")) + .WillOnce(::testing::Return("result")); + + Service service(mock_db); + service.process(); +} +``` + +## Performance Profiling + +```cpp +// Benchmark with Google Benchmark +#include + +static void BM_VectorPush(benchmark::State& state) { + for (auto _ : state) { + std::vector vec; + for (int i = 0; i < state.range(0); ++i) { + vec.push_back(i); + } + benchmark::DoNotOptimize(vec); + } +} +BENCHMARK(BM_VectorPush)->Range(8, 8<<10); + +static void BM_VectorReserve(benchmark::State& state) { + for (auto _ : state) { + std::vector vec; + vec.reserve(state.range(0)); + for (int i = 0; i < state.range(0); ++i) { + vec.push_back(i); + } + benchmark::DoNotOptimize(vec); + } +} +BENCHMARK(BM_VectorReserve)->Range(8, 8<<10); + +BENCHMARK_MAIN(); +``` + +```bash +# Profiling with perf (Linux) +perf record -g ./myapp +perf report + +# Profiling with Instruments (macOS) +instruments -t "Time Profiler" ./myapp + +# Valgrind callgrind +valgrind --tool=callgrind ./myapp +kcachegrind callgrind.out.* + +# Memory profiling +valgrind --tool=massif ./myapp +ms_print massif.out.* +``` + +## Conan Package Manager + +```python +# conanfile.txt +[requires] +fmt/10.1.1 +spdlog/1.12.0 +catch2/3.4.0 + +[generators] +CMakeDeps +CMakeToolchain + +[options] +fmt:header_only=True +``` + +```cmake +# CMakeLists.txt with Conan +cmake_minimum_required(VERSION 3.20) +project(MyProject) + +find_package(fmt REQUIRED) +find_package(spdlog REQUIRED) +find_package(Catch2 REQUIRED) + +add_executable(myapp src/main.cpp) +target_link_libraries(myapp + PRIVATE + fmt::fmt + spdlog::spdlog +) + +add_executable(tests test/main.cpp) +target_link_libraries(tests + PRIVATE + Catch2::Catch2WithMain +) +``` + +```bash +# Install dependencies +conan install . --output-folder=build --build=missing +cd build +cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake +cmake --build . +``` + +## CI/CD with GitHub Actions + +```yaml +# .github/workflows/ci.yml +name: CI + +on: [push, pull_request] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + compiler: [gcc, clang, msvc] + build_type: [Debug, Release] + + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + run: | + pip install conan + conan install . --output-folder=build --build=missing + + - name: Configure + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} + + - name: Test + run: ctest --test-dir build -C ${{ matrix.build_type }} + + sanitizers: + runs-on: ubuntu-latest + strategy: + matrix: + sanitizer: [asan, ubsan, tsan] + + steps: + - uses: actions/checkout@v3 + + - name: Build with sanitizer + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.sanitizer }} + cmake --build build + + - name: Run tests + run: ctest --test-dir build + + static-analysis: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Run clang-tidy + run: | + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + clang-tidy src/*.cpp -p build/ + + - name: Run cppcheck + run: cppcheck --enable=all --error-exitcode=1 src/ +``` + +## Quick Reference + +| Tool | Purpose | Command | +|------|---------|---------| +| CMake | Build system | `cmake -B build && cmake --build build` | +| Conan | Package manager | `conan install . --build=missing` | +| ASan | Memory errors | `-fsanitize=address` | +| UBSan | Undefined behavior | `-fsanitize=undefined` | +| TSan | Data races | `-fsanitize=thread` | +| clang-tidy | Static analysis | `clang-tidy src/*.cpp` | +| cppcheck | Static analysis | `cppcheck --enable=all src/` | +| Catch2 | Unit testing | `TEST_CASE("name") { REQUIRE(...); }` | +| GoogleTest | Unit testing | `TEST(Suite, Name) { EXPECT_EQ(...); }` | +| Google Benchmark | Performance | `BENCHMARK(func)->Range(...)` | +| Valgrind | Memory profiler | `valgrind --tool=memcheck ./app` | diff --git a/skills/cpp-pro/references/concurrency.md b/skills/cpp-pro/references/concurrency.md new file mode 100644 index 00000000..2eedf193 --- /dev/null +++ b/skills/cpp-pro/references/concurrency.md @@ -0,0 +1,437 @@ +# Concurrency and Parallel Programming + +## Atomics and Memory Ordering + +```cpp +#include +#include + +// Basic atomics +std::atomic counter{0}; +std::atomic flag{false}; + +// Memory ordering +void producer(std::atomic& data, std::atomic& ready) { + data.store(42, std::memory_order_relaxed); + ready.store(true, std::memory_order_release); // Release barrier +} + +void consumer(std::atomic& data, std::atomic& ready) { + while (!ready.load(std::memory_order_acquire)) { // Acquire barrier + std::this_thread::yield(); + } + int value = data.load(std::memory_order_relaxed); +} + +// Compare-and-swap +bool try_acquire_lock(std::atomic& lock) { + bool expected = false; + return lock.compare_exchange_strong(expected, true, + std::memory_order_acquire, + std::memory_order_relaxed); +} + +// Fetch-and-add +int increment_counter(std::atomic& counter) { + return counter.fetch_add(1, std::memory_order_relaxed); +} +``` + +## Lock-Free Data Structures + +```cpp +#include +#include + +// Lock-free stack +template +class LockFreeStack { + struct Node { + T data; + Node* next; + Node(const T& value) : data(value), next(nullptr) {} + }; + + std::atomic head_{nullptr}; + +public: + void push(const T& value) { + Node* new_node = new Node(value); + new_node->next = head_.load(std::memory_order_relaxed); + + while (!head_.compare_exchange_weak(new_node->next, new_node, + std::memory_order_release, + std::memory_order_relaxed)) { + // Retry with updated head + } + } + + bool pop(T& result) { + Node* old_head = head_.load(std::memory_order_relaxed); + + while (old_head && + !head_.compare_exchange_weak(old_head, old_head->next, + std::memory_order_acquire, + std::memory_order_relaxed)) { + // Retry + } + + if (old_head) { + result = old_head->data; + delete old_head; // Note: ABA problem exists + return true; + } + return false; + } +}; + +// Lock-free queue (single producer, single consumer) +template +class SPSCQueue { + std::array buffer_; + alignas(64) std::atomic head_{0}; + alignas(64) std::atomic tail_{0}; + +public: + bool push(const T& item) { + size_t head = head_.load(std::memory_order_relaxed); + size_t next_head = (head + 1) % Size; + + if (next_head == tail_.load(std::memory_order_acquire)) { + return false; // Queue full + } + + buffer_[head] = item; + head_.store(next_head, std::memory_order_release); + return true; + } + + bool pop(T& item) { + size_t tail = tail_.load(std::memory_order_relaxed); + + if (tail == head_.load(std::memory_order_acquire)) { + return false; // Queue empty + } + + item = buffer_[tail]; + tail_.store((tail + 1) % Size, std::memory_order_release); + return true; + } +}; +``` + +## Thread Pool + +```cpp +#include +#include +#include +#include +#include +#include + +class ThreadPool { + std::vector workers_; + std::queue> tasks_; + std::mutex queue_mutex_; + std::condition_variable condition_; + bool stop_ = false; + +public: + ThreadPool(size_t num_threads) { + for (size_t i = 0; i < num_threads; ++i) { + workers_.emplace_back([this] { + while (true) { + std::function task; + + { + std::unique_lock lock(queue_mutex_); + condition_.wait(lock, [this] { + return stop_ || !tasks_.empty(); + }); + + if (stop_ && tasks_.empty()) { + return; + } + + task = std::move(tasks_.front()); + tasks_.pop(); + } + + task(); + } + }); + } + } + + ~ThreadPool() { + { + std::unique_lock lock(queue_mutex_); + stop_ = true; + } + condition_.notify_all(); + for (auto& worker : workers_) { + worker.join(); + } + } + + template + auto enqueue(F&& f, Args&&... args) + -> std::future> { + + using return_type = typename std::invoke_result_t; + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future result = task->get_future(); + + { + std::unique_lock lock(queue_mutex_); + if (stop_) { + throw std::runtime_error("enqueue on stopped ThreadPool"); + } + tasks_.emplace([task]() { (*task)(); }); + } + + condition_.notify_one(); + return result; + } +}; +``` + +## Parallel STL Algorithms + +```cpp +#include +#include +#include +#include + +void parallel_algorithms_demo() { + std::vector vec(1'000'000); + std::iota(vec.begin(), vec.end(), 0); + + // Parallel sort + std::sort(std::execution::par, vec.begin(), vec.end()); + + // Parallel for_each + std::for_each(std::execution::par_unseq, vec.begin(), vec.end(), + [](int& x) { x *= 2; }); + + // Parallel transform + std::vector result(vec.size()); + std::transform(std::execution::par, vec.begin(), vec.end(), + result.begin(), [](int x) { return x * x; }); + + // Parallel reduce + int sum = std::reduce(std::execution::par, vec.begin(), vec.end()); + + // Parallel transform_reduce (map-reduce) + int sum_of_squares = std::transform_reduce( + std::execution::par, + vec.begin(), vec.end(), + 0, + std::plus<>(), + [](int x) { return x * x; } + ); +} +``` + +## Synchronization Primitives + +```cpp +#include +#include +#include + +// Mutex types +std::mutex mtx; +std::recursive_mutex rec_mtx; +std::timed_mutex timed_mtx; +std::shared_mutex shared_mtx; + +// RAII locks +void exclusive_access() { + std::lock_guard lock(mtx); + // Critical section +} + +void unique_lock_example() { + std::unique_lock lock(mtx); + // Can unlock and relock + lock.unlock(); + // Do some work + lock.lock(); +} + +// Reader-writer lock +class SharedData { + mutable std::shared_mutex mutex_; + std::string data_; + +public: + std::string read() const { + std::shared_lock lock(mutex_); + return data_; + } + + void write(std::string new_data) { + std::unique_lock lock(mutex_); + data_ = std::move(new_data); + } +}; + +// Condition variable +class Queue { + std::queue queue_; + std::mutex mutex_; + std::condition_variable cv_; + +public: + void push(int value) { + { + std::lock_guard lock(mutex_); + queue_.push(value); + } + cv_.notify_one(); + } + + int pop() { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return !queue_.empty(); }); + int value = queue_.front(); + queue_.pop(); + return value; + } +}; + +// std::scoped_lock - multiple mutexes +std::mutex mtx1, mtx2; + +void transfer(Account& from, Account& to, int amount) { + std::scoped_lock lock(from.mutex, to.mutex); // Deadlock-free + from.balance -= amount; + to.balance += amount; +} +``` + +## Async and Futures + +```cpp +#include + +// std::async +auto future = std::async(std::launch::async, []() { + return expensive_computation(); +}); + +// Get result (blocks until ready) +auto result = future.get(); + +// Promise and future +void producer(std::promise promise) { + int value = compute_value(); + promise.set_value(value); +} + +void consumer(std::future future) { + int value = future.get(); +} + +std::promise promise; +std::future future = promise.get_future(); + +std::thread producer_thread(producer, std::move(promise)); +std::thread consumer_thread(consumer, std::move(future)); + +// Packaged task +std::packaged_task task([](int a, int b) { + return a + b; +}); + +std::future task_future = task.get_future(); +std::thread task_thread(std::move(task), 5, 3); + +int sum = task_future.get(); // 8 +task_thread.join(); +``` + +## Coroutine-Based Concurrency + +```cpp +#include +#include + +// Async task coroutine +template +struct AsyncTask { + struct promise_type { + std::optional value; + std::exception_ptr exception; + + AsyncTask get_return_object() { + return AsyncTask{ + std::coroutine_handle::from_promise(*this) + }; + } + + std::suspend_never initial_suspend() { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + + void return_value(T v) { + value = std::move(v); + } + + void unhandled_exception() { + exception = std::current_exception(); + } + }; + + std::coroutine_handle handle; + + AsyncTask(std::coroutine_handle h) : handle(h) {} + ~AsyncTask() { if (handle) handle.destroy(); } + + T get() { + if (!handle.done()) { + handle.resume(); + } + + if (handle.promise().exception) { + std::rethrow_exception(handle.promise().exception); + } + + return *handle.promise().value; + } +}; + +// Usage +AsyncTask async_compute() { + co_return 42; +} +``` + +## Quick Reference + +| Primitive | Use Case | Performance | +|-----------|----------|-------------| +| std::atomic | Simple shared state | Lock-free | +| std::mutex | Exclusive access | Kernel call | +| std::shared_mutex | Read-heavy workload | Better than mutex | +| Lock-free structures | High contention | Best throughput | +| Thread pool | Task parallelism | Avoid thread overhead | +| Parallel STL | Data parallelism | Automatic scaling | +| std::async | Simple async tasks | Thread pool | +| Coroutines | Async I/O | Minimal overhead | + +## Memory Ordering Guide + +| Ordering | Guarantees | Use Case | +|----------|-----------|----------| +| relaxed | No synchronization | Counters | +| acquire | Load barrier | Consumer | +| release | Store barrier | Producer | +| acq_rel | Both | RMW operations | +| seq_cst | Total order | Default | diff --git a/skills/cpp-pro/references/memory-performance.md b/skills/cpp-pro/references/memory-performance.md new file mode 100644 index 00000000..c60b49d3 --- /dev/null +++ b/skills/cpp-pro/references/memory-performance.md @@ -0,0 +1,397 @@ +# Memory Management & Performance + +## Smart Pointers + +```cpp +#include + +// unique_ptr - exclusive ownership +auto create_resource() { + return std::make_unique("data"); +} + +// shared_ptr - reference counting +std::shared_ptr shared = std::make_shared(42); +std::weak_ptr weak = shared; // Non-owning reference + +// Custom deleters +auto file_deleter = [](FILE* fp) { if (fp) fclose(fp); }; +std::unique_ptr file( + fopen("data.txt", "r"), + file_deleter +); + +// enable_shared_from_this +class Node : public std::enable_shared_from_this { +public: + std::shared_ptr get_shared() { + return shared_from_this(); + } +}; +``` + +## Custom Allocators + +```cpp +#include +#include + +// Pool allocator for fixed-size objects +template +class PoolAllocator { + struct Block { + alignas(T) std::byte data[sizeof(T)]; + Block* next; + }; + + Block pool_[PoolSize]; + Block* free_list_ = nullptr; + +public: + using value_type = T; + + PoolAllocator() { + // Initialize free list + for (size_t i = 0; i < PoolSize - 1; ++i) { + pool_[i].next = &pool_[i + 1]; + } + pool_[PoolSize - 1].next = nullptr; + free_list_ = &pool_[0]; + } + + T* allocate(size_t n) { + if (n != 1 || !free_list_) { + throw std::bad_alloc(); + } + Block* block = free_list_; + free_list_ = free_list_->next; + return reinterpret_cast(block->data); + } + + void deallocate(T* p, size_t n) { + if (n != 1) return; + Block* block = reinterpret_cast(p); + block->next = free_list_; + free_list_ = block; + } +}; + +// Usage +std::vector> vec; + +// Arena allocator - bump allocator +class Arena { + std::byte* buffer_; + size_t size_; + size_t offset_ = 0; + +public: + Arena(size_t size) : size_(size) { + buffer_ = new std::byte[size]; + } + + ~Arena() { + delete[] buffer_; + } + + template + T* allocate(size_t n = 1) { + size_t alignment = alignof(T); + size_t space = size_ - offset_; + void* ptr = buffer_ + offset_; + + if (std::align(alignment, sizeof(T) * n, ptr, space)) { + offset_ = size_ - space + sizeof(T) * n; + return static_cast(ptr); + } + + throw std::bad_alloc(); + } + + void reset() { + offset_ = 0; + } +}; +``` + +## Move Semantics + +```cpp +#include +#include + +class Buffer { + size_t size_; + char* data_; + +public: + // Constructor + Buffer(size_t size) : size_(size), data_(new char[size]) {} + + // Destructor + ~Buffer() { delete[] data_; } + + // Copy constructor + Buffer(const Buffer& other) : size_(other.size_), data_(new char[size_]) { + std::copy(other.data_, other.data_ + size_, data_); + } + + // Copy assignment + Buffer& operator=(const Buffer& other) { + if (this != &other) { + delete[] data_; + size_ = other.size_; + data_ = new char[size_]; + std::copy(other.data_, other.data_ + size_, data_); + } + return *this; + } + + // Move constructor + Buffer(Buffer&& other) noexcept + : size_(other.size_), data_(other.data_) { + other.size_ = 0; + other.data_ = nullptr; + } + + // Move assignment + Buffer& operator=(Buffer&& other) noexcept { + if (this != &other) { + delete[] data_; + size_ = other.size_; + data_ = other.data_; + other.size_ = 0; + other.data_ = nullptr; + } + return *this; + } +}; + +// Perfect forwarding +template +void wrapper(T&& arg) { + process(std::forward(arg)); // Preserves lvalue/rvalue +} +``` + +## SIMD Optimization + +```cpp +#include // AVX/AVX2 +#include + +// Vectorized sum using AVX2 +float simd_sum(const float* data, size_t size) { + __m256 sum_vec = _mm256_setzero_ps(); + + size_t i = 0; + // Process 8 floats at a time + for (; i + 8 <= size; i += 8) { + __m256 vec = _mm256_loadu_ps(&data[i]); + sum_vec = _mm256_add_ps(sum_vec, vec); + } + + // Horizontal sum + alignas(32) float temp[8]; + _mm256_store_ps(temp, sum_vec); + float result = 0.0f; + for (int j = 0; j < 8; ++j) { + result += temp[j]; + } + + // Handle remaining elements + for (; i < size; ++i) { + result += data[i]; + } + + return result; +} + +// Vectorized multiply-add +void fma_operation(float* result, const float* a, const float* b, + const float* c, size_t size) { + for (size_t i = 0; i + 8 <= size; i += 8) { + __m256 va = _mm256_loadu_ps(&a[i]); + __m256 vb = _mm256_loadu_ps(&b[i]); + __m256 vc = _mm256_loadu_ps(&c[i]); + + // result[i] = a[i] * b[i] + c[i] + __m256 vr = _mm256_fmadd_ps(va, vb, vc); + _mm256_storeu_ps(&result[i], vr); + } +} +``` + +## Cache-Friendly Design + +```cpp +// Structure of Arrays (SoA) - better cache locality +struct ParticlesAoS { + struct Particle { + float x, y, z; + float vx, vy, vz; + }; + std::vector particles; +}; + +struct ParticlesSoA { + std::vector x, y, z; + std::vector vx, vy, vz; + + void update_positions(float dt) { + // All x coordinates are contiguous - better cache usage + for (size_t i = 0; i < x.size(); ++i) { + x[i] += vx[i] * dt; + y[i] += vy[i] * dt; + z[i] += vz[i] * dt; + } + } +}; + +// Cache line padding to avoid false sharing +struct alignas(64) CacheLinePadded { + std::atomic counter; + char padding[64 - sizeof(std::atomic)]; +}; + +// Prefetching +void process_with_prefetch(const int* data, size_t size) { + for (size_t i = 0; i < size; ++i) { + // Prefetch data for next iteration + if (i + 8 < size) { + __builtin_prefetch(&data[i + 8], 0, 1); + } + // Process current data + process(data[i]); + } +} +``` + +## Memory Pool + +```cpp +#include +#include + +template +class MemoryPool { + struct Chunk { + alignas(T) std::byte data[sizeof(T) * ChunkSize]; + }; + + std::vector> chunks_; + std::vector free_list_; + size_t current_chunk_offset_ = ChunkSize; + +public: + T* allocate() { + if (!free_list_.empty()) { + T* ptr = free_list_.back(); + free_list_.pop_back(); + return ptr; + } + + if (current_chunk_offset_ >= ChunkSize) { + chunks_.push_back(std::make_unique()); + current_chunk_offset_ = 0; + } + + Chunk* chunk = chunks_.back().get(); + T* ptr = reinterpret_cast( + &chunk->data[sizeof(T) * current_chunk_offset_++] + ); + return ptr; + } + + void deallocate(T* ptr) { + free_list_.push_back(ptr); + } + + template + T* construct(Args&&... args) { + T* ptr = allocate(); + new (ptr) T(std::forward(args)...); + return ptr; + } + + void destroy(T* ptr) { + ptr->~T(); + deallocate(ptr); + } +}; +``` + +## Copy Elision and RVO + +```cpp +// Return Value Optimization (RVO) +std::vector create_vector() { + std::vector vec{1, 2, 3, 4, 5}; + return vec; // RVO applies, no copy/move +} + +// Named Return Value Optimization (NRVO) +std::string build_string(bool condition) { + std::string result; + if (condition) { + result = "condition true"; + } else { + result = "condition false"; + } + return result; // NRVO may apply +} + +// Guaranteed copy elision (C++17) +struct NonMovable { + NonMovable() = default; + NonMovable(const NonMovable&) = delete; + NonMovable(NonMovable&&) = delete; +}; + +NonMovable create() { + return NonMovable{}; // Guaranteed no copy/move in C++17 +} + +auto obj = create(); // OK in C++17 +``` + +## Alignment and Memory Layout + +```cpp +#include + +// Control alignment +struct alignas(64) CacheAligned { + int data[16]; +}; + +// Check alignment +static_assert(alignof(CacheAligned) == 64); + +// Aligned allocation +void* aligned_alloc_wrapper(size_t alignment, size_t size) { + void* ptr = nullptr; + if (posix_memalign(&ptr, alignment, size) != 0) { + throw std::bad_alloc(); + } + return ptr; +} + +// Placement new with alignment +alignas(32) std::byte buffer[sizeof(Data)]; +Data* obj = new (buffer) Data(); +obj->~Data(); // Manual destruction needed +``` + +## Quick Reference + +| Technique | Use Case | Benefit | +|-----------|----------|---------| +| Smart Pointers | Ownership management | Memory safety | +| Move Semantics | Avoid copies | Performance | +| Custom Allocators | Specialized allocation | Speed + control | +| SIMD | Parallel computation | 4-8x speedup | +| SoA Layout | Sequential access | Cache efficiency | +| Memory Pools | Frequent alloc/dealloc | Reduced fragmentation | +| Alignment | SIMD/cache optimization | Performance | +| RVO/NRVO | Return objects | Zero-copy | diff --git a/skills/cpp-pro/references/modern-cpp.md b/skills/cpp-pro/references/modern-cpp.md new file mode 100644 index 00000000..1a541fbd --- /dev/null +++ b/skills/cpp-pro/references/modern-cpp.md @@ -0,0 +1,304 @@ +# Modern C++20/23 Features + +## Concepts and Constraints + +```cpp +#include + +// Define custom concepts +template +concept Numeric = std::integral || std::floating_point; + +template +concept Hashable = requires(T a) { + { std::hash{}(a) } -> std::convertible_to; +}; + +template +concept Container = requires(T c) { + typename T::value_type; + typename T::iterator; + { c.begin() } -> std::same_as; + { c.end() } -> std::same_as; + { c.size() } -> std::convertible_to; +}; + +// Use concepts for function constraints +template +T add(T a, T b) { + return a + b; +} + +// Concept-based overloading +template +void process(T value) { + std::cout << "Processing integer: " << value << '\n'; +} + +template +void process(T value) { + std::cout << "Processing float: " << value << '\n'; +} +``` + +## Ranges and Views + +```cpp +#include +#include +#include + +// Ranges-based algorithms +std::vector numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +// Filter, transform, take - all lazy evaluation +auto result = numbers + | std::views::filter([](int n) { return n % 2 == 0; }) + | std::views::transform([](int n) { return n * n; }) + | std::views::take(3); + +// Copy to vector only when needed +std::vector materialized(result.begin(), result.end()); + +// Custom range adaptor +auto is_even = [](int n) { return n % 2 == 0; }; +auto square = [](int n) { return n * n; }; + +auto pipeline = std::views::filter(is_even) + | std::views::transform(square); + +auto processed = numbers | pipeline; +``` + +## Coroutines + +```cpp +#include +#include +#include + +// Generator coroutine +template +struct Generator { + struct promise_type { + T current_value; + + auto get_return_object() { + return Generator{std::coroutine_handle::from_promise(*this)}; + } + + std::suspend_always initial_suspend() { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + + std::suspend_always yield_value(T value) { + current_value = value; + return {}; + } + + void return_void() {} + void unhandled_exception() { std::terminate(); } + }; + + std::coroutine_handle handle; + + Generator(std::coroutine_handle h) : handle(h) {} + ~Generator() { if (handle) handle.destroy(); } + + bool move_next() { + handle.resume(); + return !handle.done(); + } + + T current_value() { + return handle.promise().current_value; + } +}; + +// Usage +Generator fibonacci() { + int a = 0, b = 1; + while (true) { + co_yield a; + auto next = a + b; + a = b; + b = next; + } +} + +// Async coroutine +#include + +struct Task { + struct promise_type { + Task get_return_object() { + return Task{std::coroutine_handle::from_promise(*this)}; + } + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; + + std::coroutine_handle handle; +}; + +Task async_operation() { + std::cout << "Starting async work\n"; + co_await std::suspend_always{}; + std::cout << "Resuming async work\n"; +} +``` + +## Three-Way Comparison (Spaceship) + +```cpp +#include + +struct Point { + int x, y; + + // Auto-generate all comparison operators + auto operator<=>(const Point&) const = default; +}; + +// Custom spaceship operator +struct Version { + int major, minor, patch; + + std::strong_ordering operator<=>(const Version& other) const { + if (auto cmp = major <=> other.major; cmp != 0) return cmp; + if (auto cmp = minor <=> other.minor; cmp != 0) return cmp; + return patch <=> other.patch; + } + + bool operator==(const Version& other) const = default; +}; +``` + +## Designated Initializers + +```cpp +struct Config { + std::string host = "localhost"; + int port = 8080; + bool ssl_enabled = false; + int timeout_ms = 5000; +}; + +// C++20 designated initializers +Config cfg { + .host = "example.com", + .port = 443, + .ssl_enabled = true + // timeout_ms uses default +}; +``` + +## Modules (C++20) + +```cpp +// math.cppm - module interface +export module math; + +export namespace math { + template + T add(T a, T b) { + return a + b; + } + + class Calculator { + public: + int multiply(int a, int b); + }; +} + +// Implementation +module math; + +int math::Calculator::multiply(int a, int b) { + return a * b; +} + +// Usage in other files +import math; + +int main() { + auto result = math::add(5, 3); + math::Calculator calc; + auto product = calc.multiply(4, 7); +} +``` + +## constexpr Enhancements + +```cpp +#include +#include +#include + +// C++20: constexpr std::string and std::vector +constexpr auto compute_at_compile_time() { + std::vector vec{1, 2, 3, 4, 5}; + std::ranges::reverse(vec); + return vec[0]; // Returns 5 +} + +constexpr int value = compute_at_compile_time(); + +// constexpr virtual functions (C++20) +struct Base { + constexpr virtual int get_value() const { return 42; } + constexpr virtual ~Base() = default; +}; + +struct Derived : Base { + constexpr int get_value() const override { return 100; } +}; +``` + +## std::format (C++20) + +```cpp +#include +#include + +int main() { + std::string msg = std::format("Hello, {}!", "World"); + + // Positional arguments + auto text = std::format("{1} {0}", "World", "Hello"); + + // Formatting options + double pi = 3.14159265; + auto formatted = std::format("Pi: {:.2f}", pi); // "Pi: 3.14" + + // Custom types + struct Point { int x, y; }; +} + +// Custom formatter +template<> +struct std::formatter { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + + auto format(const Point& p, format_context& ctx) const { + return std::format_to(ctx.out(), "({}, {})", p.x, p.y); + } +}; +``` + +## Quick Reference + +| Feature | C++17 | C++20 | C++23 | +|---------|-------|-------|-------| +| Concepts | - | ✓ | ✓ | +| Ranges | - | ✓ | ✓ | +| Coroutines | - | ✓ | ✓ | +| Modules | - | ✓ | ✓ | +| Spaceship | - | ✓ | ✓ | +| std::format | - | ✓ | ✓ | +| std::expected | - | - | ✓ | +| std::print | - | - | ✓ | +| Deducing this | - | - | ✓ | diff --git a/skills/cpp-pro/references/templates.md b/skills/cpp-pro/references/templates.md new file mode 100644 index 00000000..1b3f2e6e --- /dev/null +++ b/skills/cpp-pro/references/templates.md @@ -0,0 +1,357 @@ +# Template Metaprogramming + +## Variadic Templates + +```cpp +#include +#include + +// Fold expressions (C++17) +template +auto sum(Args... args) { + return (args + ...); // Unary right fold +} + +template +void print(Args&&... args) { + ((std::cout << args << ' '), ...); // Binary left fold + std::cout << '\n'; +} + +// Recursive variadic template +template +void log(T&& value) { + std::cout << value << '\n'; +} + +template +void log(T&& first, Args&&... rest) { + std::cout << first << ", "; + log(std::forward(rest)...); +} + +// Parameter pack expansion +template +struct TypeList { + static constexpr size_t size = sizeof...(Types); +}; + +template +auto make_tuple_advanced(Args&&... args) { + return std::tuple...>(std::forward(args)...); +} +``` + +## SFINAE and if constexpr + +```cpp +#include + +// SFINAE with std::enable_if (older style) +template +std::enable_if_t, T> +double_value(T value) { + return value * 2; +} + +template +std::enable_if_t, T> +double_value(T value) { + return value * 2.0; +} + +// Modern: if constexpr (C++17) +template +auto process(T value) { + if constexpr (std::is_integral_v) { + return value * 2; + } else if constexpr (std::is_floating_point_v) { + return value * 2.0; + } else { + return value; + } +} + +// Detection idiom +template +struct has_serialize : std::false_type {}; + +template +struct has_serialize().serialize())>> + : std::true_type {}; + +template +constexpr bool has_serialize_v = has_serialize::value; + +// Use with if constexpr +template +void save(const T& obj) { + if constexpr (has_serialize_v) { + obj.serialize(); + } else { + // Default serialization + } +} +``` + +## Type Traits + +```cpp +#include + +// Custom type traits +template +struct remove_all_pointers { + using type = T; +}; + +template +struct remove_all_pointers { + using type = typename remove_all_pointers::type; +}; + +template +using remove_all_pointers_t = typename remove_all_pointers::type; + +// Conditional types +template +struct conditional_type { + using type = T; +}; + +template +struct conditional_type { + using type = F; +}; + +// Compile-time type selection +template +struct best_integral_type { + using type = std::conditional_t>>; +}; + +// Check for member functions +template +struct has_reserve : std::false_type {}; + +template +struct has_reserve().reserve(size_t{}))>> + : std::true_type {}; +``` + +## CRTP (Curiously Recurring Template Pattern) + +```cpp +// Static polymorphism with CRTP +template +class Shape { +public: + double area() const { + return static_cast(this)->area_impl(); + } + + void draw() const { + static_cast(this)->draw_impl(); + } +}; + +class Circle : public Shape { + double radius_; +public: + Circle(double r) : radius_(r) {} + + double area_impl() const { + return 3.14159 * radius_ * radius_; + } + + void draw_impl() const { + std::cout << "Drawing circle\n"; + } +}; + +class Rectangle : public Shape { + double width_, height_; +public: + Rectangle(double w, double h) : width_(w), height_(h) {} + + double area_impl() const { + return width_ * height_; + } + + void draw_impl() const { + std::cout << "Drawing rectangle\n"; + } +}; + +// CRTP for mixin capabilities +template +class Printable { +public: + void print() const { + std::cout << static_cast(this)->to_string() << '\n'; + } +}; + +class User : public Printable { + std::string name_; +public: + User(std::string name) : name_(std::move(name)) {} + + std::string to_string() const { + return "User: " + name_; + } +}; +``` + +## Template Template Parameters + +```cpp +#include +#include +#include + +// Template template parameter +template class Container> +class Stack { + Container> data_; + +public: + void push(const T& value) { + data_.push_back(value); + } + + T pop() { + T value = data_.back(); + data_.pop_back(); + return value; + } + + size_t size() const { + return data_.size(); + } +}; + +// Usage with different containers +Stack vector_stack; +Stack deque_stack; +Stack list_stack; +``` + +## Compile-Time Computation + +```cpp +#include + +// Compile-time factorial +constexpr int factorial(int n) { + return n <= 1 ? 1 : n * factorial(n - 1); +} + +constexpr int fact_5 = factorial(5); // Computed at compile time + +// Compile-time prime checking +constexpr bool is_prime(int n) { + if (n < 2) return false; + for (int i = 2; i * i <= n; ++i) { + if (n % i == 0) return false; + } + return true; +} + +// Generate compile-time array of primes +template +constexpr auto generate_primes() { + std::array primes{}; + int count = 0; + int candidate = 2; + + while (count < N) { + if (is_prime(candidate)) { + primes[count++] = candidate; + } + ++candidate; + } + + return primes; +} + +constexpr auto first_10_primes = generate_primes<10>(); +``` + +## Expression Templates + +```cpp +// Lazy evaluation with expression templates +template +class VecExpression { +public: + double operator[](size_t i) const { + return static_cast(*this)[i]; + } + + size_t size() const { + return static_cast(*this).size(); + } +}; + +class Vec : public VecExpression { + std::vector data_; + +public: + Vec(size_t n) : data_(n) {} + + double operator[](size_t i) const { return data_[i]; } + double& operator[](size_t i) { return data_[i]; } + size_t size() const { return data_.size(); } + + // Evaluate expression template + template + Vec& operator=(const VecExpression& expr) { + for (size_t i = 0; i < size(); ++i) { + data_[i] = expr[i]; + } + return *this; + } +}; + +// Binary operation expression +template +class VecSum : public VecExpression> { + const E1& lhs_; + const E2& rhs_; + +public: + VecSum(const E1& lhs, const E2& rhs) : lhs_(lhs), rhs_(rhs) {} + + double operator[](size_t i) const { + return lhs_[i] + rhs_[i]; + } + + size_t size() const { return lhs_.size(); } +}; + +// Operator overload +template +VecSum operator+(const VecExpression& lhs, + const VecExpression& rhs) { + return VecSum(static_cast(lhs), + static_cast(rhs)); +} + +// Usage: a = b + c + d (no temporaries created!) +``` + +## Quick Reference + +| Technique | Use Case | Performance | +|-----------|----------|-------------| +| Variadic Templates | Variable arguments | Zero overhead | +| SFINAE | Conditional compilation | Compile-time | +| if constexpr | Type-based branching | Zero overhead | +| CRTP | Static polymorphism | No vtable cost | +| Expression Templates | Lazy evaluation | Eliminates temps | +| Type Traits | Type introspection | Compile-time | +| Fold Expressions | Parameter pack ops | Optimal | +| Template Specialization | Type-specific impl | Zero overhead | diff --git a/skills/cpp-pro/resources/implementation-playbook.md b/skills/cpp-pro/resources/implementation-playbook.md new file mode 100644 index 00000000..af2a5407 --- /dev/null +++ b/skills/cpp-pro/resources/implementation-playbook.md @@ -0,0 +1,43 @@ +# C++ Implementation Playbook + +**Date:** March 23, 2026 +**Author:** champbreed +--- + +## 1. RAII & Resource Management +Always wrap raw resources in manager objects to ensure cleanup on scope exit. +```cpp +// Good: Scope-bound cleanup +void process() { + auto data = std::make_unique(1024); + // memory is freed automatically +} +``` +## 2. Smart Pointer Ownership +- **unique_ptr**: Use for exclusive ownership. +- **shared_ptr**: Use for shared ownership across components. +- **weak_ptr**: Use to break circular reference cycles. + +## 3. Concurrency Safety +Always use RAII-style locks like `std::lock_guard` or `std::unique_lock`. +```cpp +void update() { + std::lock_guard lock(mutex_); // Released automatically + // thread-safe logic +} +``` +## 4. Move Semantics & Efficiency +Avoid expensive copies by utilizing move constructors and `std::move`. +```cpp +void processData(std::vector&& data) { + auto internalData = std::move(data); // Transfers ownership, no copy +} +``` +## 5. Modern STL Algorithms +Prefer algorithms over manual loops for readability and optimization. + +```cpp +void sortData(std::vector& myVector) { + // Use std::ranges (C++20) for cleaner, safer iteration + std::ranges::sort(myVector); +}