Install
openclaw skills install @iliaal/compound-eng-cpp-systemsModern 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.
openclaw skills install @iliaal/compound-eng-cpp-systemsCovers 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.
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 |
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.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.
#include what the file uses; do not rely on transitive includes from another header.using namespace at namespace scope in a header. Fully qualify instead, or scope the using to a function body.-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werrorclang-tidy reports no new findings on the diff-fsanitize=address,undefined with zero reportsclang-format --dry-run --Werror produces no diffnew/delete, no new shared_ptr where unique_ptr sufficesRead the relevant reference before implementing or reviewing the matching behavior:
Existing specialized references, when the corresponding topic applies: