Modern C++ (C++20/23) Agent Rules
Project Context
You are working on a modern C++ project targeting C++20 or C++23, using CMake as the build system, RAII throughout, and contemporary standard library features.
Code Style & Naming
- Use `PascalCase` for types and classes, `camelCase` for functions and local variables, `UPPER_SNAKE_CASE` for constants and compile-time values.
- Prefix non-public member variables with `m_` (e.g., `m_buffer`) to distinguish them from local variables.
- Use `snake_case` for namespaces and file names (e.g., `memory_pool.hpp`, `memory_pool.cpp`).
- Run `clang-format` with a project `.clang-format` file before every commit; use `clang-tidy` for modernization checks.
- Write Doxygen-style `///` or `/** */` comments for all public APIs with `@param`, `@returns`, and `@throws`.
- Use `#pragma once`; keep headers self-contained with the minimum necessary includes.
Smart Pointers & RAII
- Use `std::unique_ptr<T>` as the default for heap-allocated resources; it has zero overhead.
- Use `std::shared_ptr<T>` only when ownership is genuinely shared; document why shared ownership is required.
- Allocate with `std::make_unique<T>(...)` and `std::make_shared<T>(...)` — never use raw `new` or `delete`.
- Implement the Rule of Zero for classes with only smart pointer members; implement the Rule of Five when managing raw resources.
- Use `std::span<T>` for non-owning views into contiguous data rather than raw pointer-plus-length pairs.
- Use `std::optional<T>` for values that may be absent; use `std::variant<T, E>` for discriminated unions.
Templates & Concepts
- Use C++20 `concept` to constrain template parameters with readable, self-documenting requirements.
- Prefer `void process(Sortable auto& container)` abbreviated function templates over explicit template parameter lists.
- Use `if constexpr` for compile-time branching inside templates instead of SFINAE or tag dispatch.
- Use `std::ranges` algorithms and range adaptors (`std::views::filter`, `std::views::transform`) for composable data processing.
- Mark compile-time computations `constexpr` and mark functions that must be evaluated at compile time `consteval`.
- Place concept definitions in shared headers so they are reused rather than duplicated.
Concurrency
- Use `std::jthread` over `std::thread` — it joins automatically on destruction and supports `std::stop_token` for cooperative cancellation.
- Use `std::atomic<T>` for lock-free shared flags and counters; specify memory order (`std::memory_order_acquire`, etc.) explicitly in performance-sensitive code.
- Use `std::scoped_lock` to lock multiple mutexes atomically and prevent deadlocks.
- Use `std::counting_semaphore`, `std::latch`, and `std::barrier` for synchronization patterns requiring specific coordination semantics.
- Use C++20 coroutines (`co_await`, `co_yield`, `co_return`) for asynchronous I/O — pair with a coroutine framework like `cppcoro` or `libunifex`.
- Avoid sharing mutable state between threads; prefer message passing with `std::queue` protected by a mutex and a condition variable.
Error Handling
- Use exceptions for truly exceptional conditions (out of memory, I/O failure, violated preconditions).
- Use `std::expected<T, E>` (C++23) for recoverable, expected errors in APIs where exception overhead is unacceptable.
- Mark destructors `noexcept` (default in C++11+); never throw from a destructor.
- Annotate functions returning error states with `[[nodiscard]]` to prevent silently ignored errors.
- Use `static_assert` for compile-time invariants; use `assert` for debug-mode precondition checking.
- Prefer specific exception types derived from `std::runtime_error` for runtime errors or `std::logic_error` for programming mistakes.
Build System (CMake)
- Require CMake 3.20+ with `cmake_minimum_required(VERSION 3.20)`.
- Use modern target-based CMake: `target_link_libraries`, `target_include_directories`, `target_compile_features` — no global `include_directories`.
- Specify language standard per target: `target_compile_features(mylib PUBLIC cxx_std_20)`.
- Use `CMakePresets.json` for reproducible debug, release, and CI configurations.
- Enable aggressive warnings: `-Wall -Wextra -Wpedantic -Werror` in CI builds.
- Use `FetchContent` for small dependencies; use `find_package` for system or vcpkg-managed ones.
Testing
- Use GoogleTest or Catch2 integrated with CTest for CMake-based test discovery.
- Name tests descriptively: `TEST(MemoryPool, ReturnsAlignedBlocksForAllSizes)`.
- Use parameterized tests (`TEST_P`, Catch2 generators) for testing across multiple input values.
- Enable sanitizers in CI builds: `-fsanitize=address,undefined,thread`.
- Write benchmarks with Google Benchmark; track performance regressions in CI with a baseline comparison step.
Performance
- Profile with `perf record` + `perf report` or `valgrind --tool=callgrind` before any optimization.
- Use `std::pmr` polymorphic memory resources for pool-based allocation in allocation-heavy subsystems.
- Prefer value semantics and stack allocation when the object lifetime is bounded to a scope.
- Use `[[likely]]` and `[[unlikely]]` attributes on branch arms to communicate hot-path information to the compiler.