--- name: cpp-rules description: "C++ coding rules: style, patterns, security, testing. Triggers: .cpp, .cc, .cxx, .hpp, .h, CMakeLists.txt, Makefile, GoogleTest, clang-tidy." effort: medium user-invocable: false allowed-tools: Read --- # C++ Rules These rules come from `app/rules/cpp/` in ai-toolkit. They cover the project's standards for coding style, frameworks, patterns, security, and testing in C++. Apply them when writing or reviewing C++ code. # C++ Coding Style ## Naming - PascalCase: classes, structs, enums, type aliases, concepts. - camelCase or snake_case: functions, methods, variables (be consistent per project). - UPPER_SNAKE: macros, compile-time constants. - Prefix member variables with `m_` or suffix with `_` (pick one convention). - Namespace names: lowercase, short (`namespace io`, `namespace util`). ## Modern C++ (17/20/23) - Use `auto` for iterator types and complex template deductions. - Use `std::optional` instead of sentinel values or pointers for optional returns. - Use `std::variant` over union types. Use `std::visit` for dispatch. - Use `std::string_view` for non-owning string parameters. - Use structured bindings: `auto [key, value] = *map.begin();`. - Use `constexpr` for compile-time evaluation. Prefer over macros. ## Memory Management - Use RAII exclusively. Every resource acquisition is an initialization. - Use `std::unique_ptr` for exclusive ownership (default choice). - Use `std::shared_ptr` only when ownership is genuinely shared. - Never use raw `new`/`delete`. Use `std::make_unique` / `std::make_shared`. - Use `std::span` (C++20) for non-owning views over contiguous data. ## Functions - Pass small types by value. Pass large types by `const&`. - Use `[[nodiscard]]` on functions whose return value must not be ignored. - Use `noexcept` on functions that do not throw (move constructors, destructors). - Limit function parameters to 4. Use structs for configuration objects. - Use trailing return types for complex template return deductions. ## Includes and Dependencies - Use `#pragma once` or include guards. Prefer `#pragma once` for simplicity. - Order: corresponding header, C++ stdlib, third-party, project headers. - Forward-declare in headers when possible to reduce compile times. - Minimize header dependencies. Use the Pimpl idiom for ABI stability. ## Avoid - Raw pointers for ownership. Use smart pointers. - C-style casts. Use `static_cast`, `dynamic_cast`, `const_cast`. - Macros for constants or functions. Use `constexpr` and templates. - `using namespace std;` in headers. Acceptable in .cpp files with caution. - `std::endl` -- use `'\n'` (endl flushes the buffer unnecessarily). ## Formatting - Use clang-format with a committed `.clang-format` file. - Use clang-tidy for static analysis and automated modernization. - Max line length: 100-120 characters. - Braces: use Allman or K&R consistently per project. # C++ Frameworks ## CMake - Use modern CMake (3.14+): target-based, not directory-based. - Use `target_link_libraries` with `PUBLIC`/`PRIVATE`/`INTERFACE` visibility. - Use `FetchContent` for dependency management. Avoid manual submodule vendoring. - Set `CMAKE_CXX_STANDARD 20` (or 23) at the project level. - Use `target_compile_options` for per-target flags, not global `add_compile_options`. - Export targets with `install(TARGETS ... EXPORT ...)` for library consumers. ## Boost - Use Boost.Asio for async networking and I/O. - Use `boost::beast` for HTTP/WebSocket built on Asio. - Use `boost::json` or `nlohmann/json` for JSON parsing. - Prefer C++ stdlib equivalents when available (e.g., `std::optional` over `boost::optional`). - Link only the Boost libraries you actually use. Many are header-only. ## Qt - Use signals and slots for event-driven communication. - Use `QObject` parent-child ownership for automatic memory management. - Use `QML` for declarative UI. Keep business logic in C++ backend. - Use `QThread` with worker objects (moveToThread), not subclassing QThread. - Use smart pointers for non-QObject resources. QObject children are auto-deleted. ## gRPC - Define services in `.proto` files. Generate C++ stubs with `protoc`. - Use async server with `CompletionQueue` for high-throughput services. - Use `grpc::ClientContext` for per-call deadlines and metadata. - Use interceptors for logging, auth, and metrics. - Set deadlines on every RPC call to prevent hanging. ## Networking (Asio) - Use `io_context` as the event loop. Run from one or more threads. - Use `co_await` (C++20 coroutines) with Asio for clean async code. - Use `strand` for serializing access to shared state across handlers. - Use `steady_timer` for timeouts and periodic tasks. - Handle errors via `error_code` parameter, not exceptions, in async callbacks. ## Database - Use `libpq` (PostgreSQL) or `SOCI` for database access. - Use prepared statements exclusively. Never concatenate SQL strings. - Use connection pooling for multi-threaded server applications. - Use `SQLite` via `sqlite3` C API with RAII wrappers for embedded use cases. ## Package Management - Use `vcpkg` or `Conan 2` for dependency management. - Pin dependency versions in `vcpkg.json` or `conanfile.py`. - Use CI caching for build artifacts and dependency downloads. - Prefer pre-built binary packages for CI speed. # C++ Patterns ## Error Handling - Use exceptions for truly exceptional conditions. Use return types for expected failures. - Use `std::expected` (C++23) or `Result` pattern for recoverable errors. - Use `std::error_code` / `std::error_category` for system-level errors. - Use `noexcept` on functions that must not throw (destructors, move operations). - Catch by `const&`. Never catch by value (slicing) or pointer. ## RAII Patterns - Wrap every resource (memory, file, lock, socket) in an RAII type. - Use `std::lock_guard` or `std::scoped_lock` for mutex management. - Use `std::unique_lock` when deferred locking or condition variables are needed. - Use `std::fstream` (auto-closes) instead of `fopen`/`fclose`. - Write custom RAII wrappers for C library resources (file descriptors, handles). ## Smart Pointer Patterns - `unique_ptr`: default ownership model. Transfer with `std::move`. - `shared_ptr`: use only for genuinely shared ownership graphs. - `weak_ptr`: break cycles in `shared_ptr` graphs. Use `lock()` to access. - Factory functions should return `unique_ptr`. Let callers upgrade to `shared_ptr`. - Never pass smart pointers by reference. Pass `T&` or `T*` to non-owning consumers. ## Concurrency - Use `std::thread` with `std::jthread` (C++20) for auto-joining threads. - Use `std::mutex` + `std::scoped_lock` for shared data protection. - Use `std::atomic` for lock-free single-variable synchronization. - Use `std::condition_variable` for producer-consumer patterns. - Use `std::async` / `std::future` for simple parallel computation. - Use `std::counting_semaphore` (C++20) for resource pool limiting. ## Template Patterns - Use CRTP for compile-time polymorphism (static dispatch). - Use `concepts` (C++20) to constrain template parameters with clear error messages. - Use `if constexpr` for compile-time branching in templates. - Use variadic templates and fold expressions for parameter packs. - Prefer `constexpr` functions over template metaprogramming when possible. ## Design Patterns - Use `std::variant` + `std::visit` for type-safe visitor pattern. - Use `std::function` for type-erased callbacks and strategy pattern. - Use Pimpl idiom (`unique_ptr`) for ABI stability and compilation firewall. - Use Builder pattern with method chaining for complex object construction. - Use `std::move` semantics in move constructors for efficient resource transfer. ## Anti-Patterns - Raw `new`/`delete`: use smart pointers and containers. - Returning raw pointers from factory functions: return `unique_ptr`. - `const_cast` to remove constness: redesign the interface. - Deep inheritance hierarchies: prefer composition and templates. - Premature optimization over readability: profile first, optimize second. # C++ Security ## Buffer Overflow Prevention - Use `std::string`, `std::vector`, `std::array` instead of C arrays and `char[]`. - Use `std::span` (C++20) for safe, bounds-checked views over contiguous data. - Never use `strcpy`, `strcat`, `sprintf`. Use `std::string` operations or `snprintf`. - Enable `-D_FORTIFY_SOURCE=2` in release builds for runtime buffer checks. - Use `at()` for bounds-checked container access in untrusted input paths. ## Memory Safety - Use smart pointers exclusively. Zero raw `new`/`delete` in application code. - Enable AddressSanitizer (`-fsanitize=address`) in development and CI builds. - Enable UndefinedBehaviorSanitizer (`-fsanitize=undefined`) in test builds. - Use `-fstack-protector-strong` for stack buffer overflow detection. - Use Valgrind for memory leak detection in integration tests. ## Integer Safety - Check for overflow before arithmetic on untrusted integers. - Use `std::numeric_limits::max()` for boundary checks. - Use unsigned types only for bit manipulation. Prefer signed for arithmetic. - Use `static_cast` explicitly. Never rely on implicit narrowing conversions. - Enable `-Wconversion` and `-Wsign-conversion` warnings. ## Input Validation - Validate all external input: file data, network packets, command-line arguments. - Use `std::stoi` / `std::stol` with exception handling for string-to-number conversion. - Set maximum sizes for dynamic allocations based on untrusted input. - Validate file paths to prevent directory traversal (`../`). - Use allowlist validation for format specifiers and command strings. ## Secure Coding - Use `std::fill` or `explicit_bzero()` to zero sensitive memory before deallocation. - Use constant-time comparison for secrets (avoid timing side-channels). - Use `mlock()` to prevent sensitive memory from being swapped to disk. - Compile with `-fPIE -pie` for position-independent executables (ASLR). - Enable `-Werror` in CI to prevent warnings from becoming vulnerabilities. ## Dependencies - Audit third-party C libraries for known CVEs before inclusion. - Use `vcpkg` or `Conan` with pinned versions for reproducible builds. - Prefer well-maintained libraries with active security response teams. - Minimize C library usage. Prefer C++ standard library equivalents. ## Concurrency Safety - Use `std::mutex` with `std::scoped_lock` for all shared data access. - Use `std::atomic` for lock-free single-variable operations. - Enable ThreadSanitizer (`-fsanitize=thread`) in test builds for race detection. - Avoid `volatile` for synchronization. It does not provide atomicity or ordering. - Use RAII lock guards. Never manually `lock()`/`unlock()`. ## Compiler Hardening - Enable all warnings: `-Wall -Wextra -Wpedantic`. - Use `-D_GLIBCXX_ASSERTIONS` for debug iterator and container checks. - Use `-fno-exceptions` only when exception safety is not required. - Link with `-Wl,-z,relro,-z,now` for full RELRO (GOT hardening). # C++ Testing ## Framework - Use GoogleTest (gtest) as the primary test framework. - Use GoogleMock (gmock) for mocking interfaces and virtual classes. - Use Catch2 as a lightweight alternative (header-only, BDD-style). - Use CTest for test discovery and execution via CMake. ## File Naming - Test files: `foo_test.cpp` or `test_foo.cpp` in a dedicated `tests/` directory. - Mirror source directory structure in test directory. - One test file per source file or logical component. - Use `CMakeLists.txt` with `add_test()` to register tests. ## Structure (GoogleTest) - Use `TEST(SuiteName, TestName)` for simple tests. - Use `TEST_F(FixtureName, TestName)` for tests sharing setup/teardown. - Use `SetUp()` / `TearDown()` in fixtures for per-test initialization. - Keep tests focused: one logical assertion per test case. ## Assertions - Use `EXPECT_*` (non-fatal) by default. Use `ASSERT_*` only when continuation is meaningless. - `EXPECT_EQ`, `EXPECT_NE`, `EXPECT_LT`, `EXPECT_GT` for comparisons. - `EXPECT_TRUE`, `EXPECT_FALSE` for boolean conditions. - `EXPECT_THROW(expr, ExceptionType)` for exception testing. - `EXPECT_THAT(value, matcher)` with gmock matchers for complex assertions. ## Parameterized Tests - Use `INSTANTIATE_TEST_SUITE_P` with `testing::Values(...)` for value-parameterized tests. - Use `testing::Combine()` for multi-dimensional parameterization. - Use `TYPED_TEST_SUITE` for type-parameterized tests across template types. - Prefer parameterized tests over copy-pasting similar test bodies. ## Mocking (GoogleMock) - Define mock classes: `MOCK_METHOD(ReturnType, MethodName, (Args), (Qualifiers))`. - Use `EXPECT_CALL(mock, Method(matchers)).WillOnce(Return(value))`. - Use `NiceMock` to suppress uninteresting call warnings. - Use `StrictMock` to fail on any unexpected call. - Use dependency injection (constructor) to pass mock objects. ## Build Integration - Use `FetchContent` or `find_package` to integrate gtest in CMake. - Enable `BUILD_TESTING` option to conditionally include tests. - Use `ctest --output-on-failure` for CI runs. - Use sanitizers in test builds: `-fsanitize=address,undefined`. ## Best Practices - Test edge cases: empty input, max values, null pointers, boundary conditions. - Use RAII test fixtures for resource cleanup (no manual teardown). - Avoid testing private methods directly. Test through public API. - Use `valgrind` or ASan/UBSan in CI to detect memory errors. - Keep tests fast: mock I/O and external dependencies.