ia-cpp-systems · git:20260907.228ec83 · 2026-09-07 · sha256 9d38c6c402395548
ia-cpp-systems git:20260907.228ec83A
Immutable. This exact content is served forever at /api/v1/blob/9d38c6c402395548.
--- name: ia-cpp-systems class: language description: >- Modern C++ patterns: RAII and ownership, rule of zero/five, exceptions and error handling, API and ABI boundaries, templates, and CMake tooling. Use when writing, reviewing, refactoring, or debugging C++, working with smart pointers, move semantics, memory leaks, template errors, or gtest. For plain C, see ia-c-systems. paths: "**/*.cpp,**/*.hpp,**/*.cc,**/*.hh,**/*.cxx,**/*.h,**/CMakeLists.txt,**/*.cmake" --- # C++ Systems & Libraries Covers C++17 as the baseline, with C++20 features called out where a project's standard allows them. For plain C (manual lifetimes, status enums, native extensions), see the `ia-c-systems` skill. ## Working rules - Make resource ownership explicit and use RAII for release on every exit path. - Keep borrowed views within backing-storage lifetimes and review copy/move behavior when special members change. - Follow the project's error model and supported language standard. - Preserve ABI and avoid invoking unknown callbacks while holding locks or invalidatable container iterators. ## Repo conventions outrank this skill Check `CMakeLists.txt` for `CXX_STANDARD`, read `.clang-format` and `.clang-tidy`, and read two adjacent translation units before writing. Where they conflict with the rules below, they win. The conflicts that actually happen: | Local constraint | Consequence | |---|---| | `-fno-exceptions` | Error handling is codes or `expected`-alikes. Constructors cannot report recoverable failure, so use a fallible factory or construct a valid fallback state. `new (std::nothrow)` only where the project's OOM policy is to observe null and recover; plain `new` is fine where the policy is termination | | Standard pinned below C++17 | No `std::optional`/`string_view`/structured bindings/`if constexpr`; check before using any | | Public header is ABI-stable | No layout changes, no inline-function changes, no added virtuals: load the ABI reference | | Embedded or freestanding target | No RTTI, no dynamic allocation in hot paths, possibly no STL containers | ## Ownership and RAII Every resource has exactly one owner, and that owner is an object whose destructor releases it. A raw `new` or `delete` in application code is a defect. - `std::unique_ptr<T>` for sole ownership. It is the default; it costs nothing over a raw pointer. - `std::shared_ptr<T>` only where lifetime is genuinely shared and cannot be expressed as "the owner outlives the users". Reach for it third, not first. - `std::weak_ptr<T>` to break ownership cycles. LeakSanitizer does report a cycle that is unreachable from any root, but not one still reachable from a global or other registered root, and detection varies by platform and configuration. Do not rely on the sanitizer to find these. - Raw `T*` and `T&` mean **non-owning observation**, and are correct in that role. A parameter taking `unique_ptr` by value is announcing that it consumes ownership; one taking `T*` is announcing it does not. - `std::span<T>` (C++20) or a pointer-plus-length pair for a borrowed contiguous range **in a new public API**, since `const std::vector<T>&` there refuses every other container. On a C++17 baseline, or for internal code whose callers all hold vectors anyway, `const std::vector<T>&` is fine and simpler. **Rule of zero**: a class that owns nothing declares no destructor, no copy, and no move. Composing members that manage themselves gets all five special members correct for free. **Rule of five**: declaring any one of destructor, copy constructor, copy assignment, move constructor, or move assignment obliges the author to reason about all five. A user-declared destructor suppresses the implicit move operations, so a class that gained a destructor silently started deep-copying where it used to move. ## Discipline - Preserve behavior and API compatibility unless a break was requested. A public header change is a decision, not a cleanup. - Do not introduce a template, an inheritance hierarchy, or a policy parameter for a single call site. - `#include` what the file uses; do not rely on transitive includes from another header. - No `using namespace` at namespace scope in a header. Fully qualify instead, or scope the `using` to a function body. - When a constraint forces a deviation, comment at the deviation site and state the constraint. ## Verify - Build clean with `-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror` - `clang-tidy` reports no new findings on the diff - Tests pass under `-fsanitize=address,undefined` with zero reports - Any threaded code touched by the change exercised under TSan with zero reports -- the warning bundle above does not catch lock misuse - `clang-format --dry-run --Werror` produces no diff - No new raw `new`/`delete`, no new `shared_ptr` where `unique_ptr` suffices - Any class that gained a destructor has its move operations reviewed ## Task-specific references Read the relevant reference before implementing or reviewing the matching behavior: - For constness, errors, API/ABI changes, templates, or standard-library behavior: [value-and-interface-design.md](./references/value-and-interface-design.md). - For threading, compiler setup, tests, or build analysis: [concurrency-and-verification.md](./references/concurrency-and-verification.md). Existing specialized references, when the corresponding topic applies: - [api-and-abi.md](./references/api-and-abi.md). - [cmake-and-tooling.md](./references/cmake-and-tooling.md). - [legibility-standard.md](./references/legibility-standard.md).