Claude Agent Skill · by Affaan M

Cpp Coding Standards

Install Cpp Coding Standards skill for Claude Code from affaan-m/everything-claude-code.

Install
Terminal · npx
$npx skills add https://github.com/affaan-m/everything-claude-code --skill cpp-coding-standards
Works with Paperclip

How Cpp Coding Standards fits into a Paperclip company.

Cpp Coding Standards drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md723 lines
Expand
---name: cpp-coding-standardsdescription: C++ coding standards based on the C++ Core Guidelines (isocpp.github.io). Use when writing, reviewing, or refactoring C++ code to enforce modern, safe, and idiomatic practices.origin: ECC--- # C++ Coding Standards (C++ Core Guidelines) Comprehensive coding standards for modern C++ (C++17/20/23) derived from the [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). Enforces type safety, resource safety, immutability, and clarity. ## When to Use - Writing new C++ code (classes, functions, templates)- Reviewing or refactoring existing C++ code- Making architectural decisions in C++ projects- Enforcing consistent style across a C++ codebase- Choosing between language features (e.g., `enum` vs `enum class`, raw pointer vs smart pointer) ### When NOT to Use - Non-C++ projects- Legacy C codebases that cannot adopt modern C++ features- Embedded/bare-metal contexts where specific guidelines conflict with hardware constraints (adapt selectively) ## Cross-Cutting Principles These themes recur across the entire guidelines and form the foundation: 1. **RAII everywhere** (P.8, R.1, E.6, CP.20): Bind resource lifetime to object lifetime2. **Immutability by default** (P.10, Con.1-5, ES.25): Start with `const`/`constexpr`; mutability is the exception3. **Type safety** (P.4, I.4, ES.46-49, Enum.3): Use the type system to prevent errors at compile time4. **Express intent** (P.3, F.1, NL.1-2, T.10): Names, types, and concepts should communicate purpose5. **Minimize complexity** (F.2-3, ES.5, Per.4-5): Simple code is correct code6. **Value semantics over pointer semantics** (C.10, R.3-5, F.20, CP.31): Prefer returning by value and scoped objects ## Philosophy & Interfaces (P.*, I.*) ### Key Rules | Rule | Summary ||------|---------|| **P.1** | Express ideas directly in code || **P.3** | Express intent || **P.4** | Ideally, a program should be statically type safe || **P.5** | Prefer compile-time checking to run-time checking || **P.8** | Don't leak any resources || **P.10** | Prefer immutable data to mutable data || **I.1** | Make interfaces explicit || **I.2** | Avoid non-const global variables || **I.4** | Make interfaces precisely and strongly typed || **I.11** | Never transfer ownership by a raw pointer or reference || **I.23** | Keep the number of function arguments low | ### DO ```cpp// P.10 + I.4: Immutable, strongly typed interfacestruct Temperature {    double kelvin;}; Temperature boil(const Temperature& water);``` ### DON'T ```cpp// Weak interface: unclear ownership, unclear unitsdouble boil(double* temp); // Non-const global variableint g_counter = 0;  // I.2 violation``` ## Functions (F.*) ### Key Rules | Rule | Summary ||------|---------|| **F.1** | Package meaningful operations as carefully named functions || **F.2** | A function should perform a single logical operation || **F.3** | Keep functions short and simple || **F.4** | If a function might be evaluated at compile time, declare it `constexpr` || **F.6** | If your function must not throw, declare it `noexcept` || **F.8** | Prefer pure functions || **F.16** | For "in" parameters, pass cheaply-copied types by value and others by `const&` || **F.20** | For "out" values, prefer return values to output parameters || **F.21** | To return multiple "out" values, prefer returning a struct || **F.43** | Never return a pointer or reference to a local object | ### Parameter Passing ```cpp// F.16: Cheap types by value, others by const&void print(int x);                           // cheap: by valuevoid analyze(const std::string& data);       // expensive: by const&void transform(std::string s);               // sink: by value (will move) // F.20 + F.21: Return values, not output parametersstruct ParseResult {    std::string token;    int position;}; ParseResult parse(std::string_view input);   // GOOD: return struct // BAD: output parametersvoid parse(std::string_view input,           std::string& token, int& pos);    // avoid this``` ### Pure Functions and constexpr ```cpp// F.4 + F.8: Pure, constexpr where possibleconstexpr int factorial(int n) noexcept {    return (n <= 1) ? 1 : n * factorial(n - 1);} static_assert(factorial(5) == 120);``` ### Anti-Patterns - Returning `T&&` from functions (F.45)- Using `va_arg` / C-style variadics (F.55)- Capturing by reference in lambdas passed to other threads (F.53)- Returning `const T` which inhibits move semantics (F.49) ## Classes & Class Hierarchies (C.*) ### Key Rules | Rule | Summary ||------|---------|| **C.2** | Use `class` if invariant exists; `struct` if data members vary independently || **C.9** | Minimize exposure of members || **C.20** | If you can avoid defining default operations, do (Rule of Zero) || **C.21** | If you define or `=delete` any copy/move/destructor, handle them all (Rule of Five) || **C.35** | Base class destructor: public virtual or protected non-virtual || **C.41** | A constructor should create a fully initialized object || **C.46** | Declare single-argument constructors `explicit` || **C.67** | A polymorphic class should suppress public copy/move || **C.128** | Virtual functions: specify exactly one of `virtual`, `override`, or `final` | ### Rule of Zero ```cpp// C.20: Let the compiler generate special membersstruct Employee {    std::string name;    std::string department;    int id;    // No destructor, copy/move constructors, or assignment operators needed};``` ### Rule of Five ```cpp// C.21: If you must manage a resource, define all fiveclass Buffer {public:    explicit Buffer(std::size_t size)        : data_(std::make_unique<char[]>(size)), size_(size) {}     ~Buffer() = default;     Buffer(const Buffer& other)        : data_(std::make_unique<char[]>(other.size_)), size_(other.size_) {        std::copy_n(other.data_.get(), size_, data_.get());    }     Buffer& operator=(const Buffer& other) {        if (this != &other) {            auto new_data = std::make_unique<char[]>(other.size_);            std::copy_n(other.data_.get(), other.size_, new_data.get());            data_ = std::move(new_data);            size_ = other.size_;        }        return *this;    }     Buffer(Buffer&&) noexcept = default;    Buffer& operator=(Buffer&&) noexcept = default; private:    std::unique_ptr<char[]> data_;    std::size_t size_;};``` ### Class Hierarchy ```cpp// C.35 + C.128: Virtual destructor, use overrideclass Shape {public:    virtual ~Shape() = default;    virtual double area() const = 0;  // C.121: pure interface}; class Circle : public Shape {public:    explicit Circle(double r) : radius_(r) {}    double area() const override { return 3.14159 * radius_ * radius_; } private:    double radius_;};``` ### Anti-Patterns - Calling virtual functions in constructors/destructors (C.82)- Using `memset`/`memcpy` on non-trivial types (C.90)- Providing different default arguments for virtual function and overrider (C.140)- Making data members `const` or references, which suppresses move/copy (C.12) ## Resource Management (R.*) ### Key Rules | Rule | Summary ||------|---------|| **R.1** | Manage resources automatically using RAII || **R.3** | A raw pointer (`T*`) is non-owning || **R.5** | Prefer scoped objects; don't heap-allocate unnecessarily || **R.10** | Avoid `malloc()`/`free()` || **R.11** | Avoid calling `new` and `delete` explicitly || **R.20** | Use `unique_ptr` or `shared_ptr` to represent ownership || **R.21** | Prefer `unique_ptr` over `shared_ptr` unless sharing ownership || **R.22** | Use `make_shared()` to make `shared_ptr`s | ### Smart Pointer Usage ```cpp// R.11 + R.20 + R.21: RAII with smart pointersauto widget = std::make_unique<Widget>("config");  // unique ownershipauto cache  = std::make_shared<Cache>(1024);        // shared ownership // R.3: Raw pointer = non-owning observervoid render(const Widget* w) {  // does NOT own w    if (w) w->draw();} render(widget.get());``` ### RAII Pattern ```cpp// R.1: Resource acquisition is initializationclass FileHandle {public:    explicit FileHandle(const std::string& path)        : handle_(std::fopen(path.c_str(), "r")) {        if (!handle_) throw std::runtime_error("Failed to open: " + path);    }     ~FileHandle() {        if (handle_) std::fclose(handle_);    }     FileHandle(const FileHandle&) = delete;    FileHandle& operator=(const FileHandle&) = delete;    FileHandle(FileHandle&& other) noexcept        : handle_(std::exchange(other.handle_, nullptr)) {}    FileHandle& operator=(FileHandle&& other) noexcept {        if (this != &other) {            if (handle_) std::fclose(handle_);            handle_ = std::exchange(other.handle_, nullptr);        }        return *this;    } private:    std::FILE* handle_;};``` ### Anti-Patterns - Naked `new`/`delete` (R.11)- `malloc()`/`free()` in C++ code (R.10)- Multiple resource allocations in a single expression (R.13 -- exception safety hazard)- `shared_ptr` where `unique_ptr` suffices (R.21) ## Expressions & Statements (ES.*) ### Key Rules | Rule | Summary ||------|---------|| **ES.5** | Keep scopes small || **ES.20** | Always initialize an object || **ES.23** | Prefer `{}` initializer syntax || **ES.25** | Declare objects `const` or `constexpr` unless modification is intended || **ES.28** | Use lambdas for complex initialization of `const` variables || **ES.45** | Avoid magic constants; use symbolic constants || **ES.46** | Avoid narrowing/lossy arithmetic conversions || **ES.47** | Use `nullptr` rather than `0` or `NULL` || **ES.48** | Avoid casts || **ES.50** | Don't cast away `const` | ### Initialization ```cpp// ES.20 + ES.23 + ES.25: Always initialize, prefer {}, default to constconst int max_retries{3};const std::string name{"widget"};const std::vector<int> primes{2, 3, 5, 7, 11}; // ES.28: Lambda for complex const initializationconst auto config = [&] {    Config c;    c.timeout = std::chrono::seconds{30};    c.retries = max_retries;    c.verbose = debug_mode;    return c;}();``` ### Anti-Patterns - Uninitialized variables (ES.20)- Using `0` or `NULL` as pointer (ES.47 -- use `nullptr`)- C-style casts (ES.48 -- use `static_cast`, `const_cast`, etc.)- Casting away `const` (ES.50)- Magic numbers without named constants (ES.45)- Mixing signed and unsigned arithmetic (ES.100)- Reusing names in nested scopes (ES.12) ## Error Handling (E.*) ### Key Rules | Rule | Summary ||------|---------|| **E.1** | Develop an error-handling strategy early in a design || **E.2** | Throw an exception to signal that a function can't perform its assigned task || **E.6** | Use RAII to prevent leaks || **E.12** | Use `noexcept` when throwing is impossible or unacceptable || **E.14** | Use purpose-designed user-defined types as exceptions || **E.15** | Throw by value, catch by reference || **E.16** | Destructors, deallocation, and swap must never fail || **E.17** | Don't try to catch every exception in every function | ### Exception Hierarchy ```cpp// E.14 + E.15: Custom exception types, throw by value, catch by referenceclass AppError : public std::runtime_error {public:    using std::runtime_error::runtime_error;}; class NetworkError : public AppError {public:    NetworkError(const std::string& msg, int code)        : AppError(msg), status_code(code) {}    int status_code;}; void fetch_data(const std::string& url) {    // E.2: Throw to signal failure    throw NetworkError("connection refused", 503);} void run() {    try {        fetch_data("https://api.example.com");    } catch (const NetworkError& e) {        log_error(e.what(), e.status_code);    } catch (const AppError& e) {        log_error(e.what());    }    // E.17: Don't catch everything here -- let unexpected errors propagate}``` ### Anti-Patterns - Throwing built-in types like `int` or string literals (E.14)- Catching by value (slicing risk) (E.15)- Empty catch blocks that silently swallow errors- Using exceptions for flow control (E.3)- Error handling based on global state like `errno` (E.28) ## Constants & Immutability (Con.*) ### All Rules | Rule | Summary ||------|---------|| **Con.1** | By default, make objects immutable || **Con.2** | By default, make member functions `const` || **Con.3** | By default, pass pointers and references to `const` || **Con.4** | Use `const` for values that don't change after construction || **Con.5** | Use `constexpr` for values computable at compile time | ```cpp// Con.1 through Con.5: Immutability by defaultclass Sensor {public:    explicit Sensor(std::string id) : id_(std::move(id)) {}     // Con.2: const member functions by default    const std::string& id() const { return id_; }    double last_reading() const { return reading_; }     // Only non-const when mutation is required    void record(double value) { reading_ = value; } private:    const std::string id_;  // Con.4: never changes after construction    double reading_{0.0};}; // Con.3: Pass by const referencevoid display(const Sensor& s) {    std::cout << s.id() << ": " << s.last_reading() << '\n';} // Con.5: Compile-time constantsconstexpr double PI = 3.14159265358979;constexpr int MAX_SENSORS = 256;``` ## Concurrency & Parallelism (CP.*) ### Key Rules | Rule | Summary ||------|---------|| **CP.2** | Avoid data races || **CP.3** | Minimize explicit sharing of writable data || **CP.4** | Think in terms of tasks, rather than threads || **CP.8** | Don't use `volatile` for synchronization || **CP.20** | Use RAII, never plain `lock()`/`unlock()` || **CP.21** | Use `std::scoped_lock` to acquire multiple mutexes || **CP.22** | Never call unknown code while holding a lock || **CP.42** | Don't wait without a condition || **CP.44** | Remember to name your `lock_guard`s and `unique_lock`s || **CP.100** | Don't use lock-free programming unless you absolutely have to | ### Safe Locking ```cpp// CP.20 + CP.44: RAII locks, always namedclass ThreadSafeQueue {public:    void push(int value) {        std::lock_guard<std::mutex> lock(mutex_);  // CP.44: named!        queue_.push(value);        cv_.notify_one();    }     int pop() {        std::unique_lock<std::mutex> lock(mutex_);        // CP.42: Always wait with a condition        cv_.wait(lock, [this] { return !queue_.empty(); });        const int value = queue_.front();        queue_.pop();        return value;    } private:    std::mutex mutex_;             // CP.50: mutex with its data    std::condition_variable cv_;    std::queue<int> queue_;};``` ### Multiple Mutexes ```cpp// CP.21: std::scoped_lock for multiple mutexes (deadlock-free)void transfer(Account& from, Account& to, double amount) {    std::scoped_lock lock(from.mutex_, to.mutex_);    from.balance_ -= amount;    to.balance_ += amount;}``` ### Anti-Patterns - `volatile` for synchronization (CP.8 -- it's for hardware I/O only)- Detaching threads (CP.26 -- lifetime management becomes nearly impossible)- Unnamed lock guards: `std::lock_guard<std::mutex>(m);` destroys immediately (CP.44)- Holding locks while calling callbacks (CP.22 -- deadlock risk)- Lock-free programming without deep expertise (CP.100) ## Templates & Generic Programming (T.*) ### Key Rules | Rule | Summary ||------|---------|| **T.1** | Use templates to raise the level of abstraction || **T.2** | Use templates to express algorithms for many argument types || **T.10** | Specify concepts for all template arguments || **T.11** | Use standard concepts whenever possible || **T.13** | Prefer shorthand notation for simple concepts || **T.43** | Prefer `using` over `typedef` || **T.120** | Use template metaprogramming only when you really need to || **T.144** | Don't specialize function templates (overload instead) | ### Concepts (C++20) ```cpp#include <concepts> // T.10 + T.11: Constrain templates with standard conceptstemplate<std::integral T>T gcd(T a, T b) {    while (b != 0) {        a = std::exchange(b, a % b);    }    return a;} // T.13: Shorthand concept syntaxvoid sort(std::ranges::random_access_range auto& range) {    std::ranges::sort(range);} // Custom concept for domain-specific constraintstemplate<typename T>concept Serializable = requires(const T& t) {    { t.serialize() } -> std::convertible_to<std::string>;}; template<Serializable T>void save(const T& obj, const std::string& path);``` ### Anti-Patterns - Unconstrained templates in visible namespaces (T.47)- Specializing function templates instead of overloading (T.144)- Template metaprogramming where `constexpr` suffices (T.120)- `typedef` instead of `using` (T.43) ## Standard Library (SL.*) ### Key Rules | Rule | Summary ||------|---------|| **SL.1** | Use libraries wherever possible || **SL.2** | Prefer the standard library to other libraries || **SL.con.1** | Prefer `std::array` or `std::vector` over C arrays || **SL.con.2** | Prefer `std::vector` by default || **SL.str.1** | Use `std::string` to own character sequences || **SL.str.2** | Use `std::string_view` to refer to character sequences || **SL.io.50** | Avoid `endl` (use `'\n'` -- `endl` forces a flush) | ```cpp// SL.con.1 + SL.con.2: Prefer vector/array over C arraysconst std::array<int, 4> fixed_data{1, 2, 3, 4};std::vector<std::string> dynamic_data; // SL.str.1 + SL.str.2: string owns, string_view observesstd::string build_greeting(std::string_view name) {    return "Hello, " + std::string(name) + "!";} // SL.io.50: Use '\n' not endlstd::cout << "result: " << value << '\n';``` ## Enumerations (Enum.*) ### Key Rules | Rule | Summary ||------|---------|| **Enum.1** | Prefer enumerations over macros || **Enum.3** | Prefer `enum class` over plain `enum` || **Enum.5** | Don't use ALL_CAPS for enumerators || **Enum.6** | Avoid unnamed enumerations | ```cpp// Enum.3 + Enum.5: Scoped enum, no ALL_CAPSenum class Color { red, green, blue };enum class LogLevel { debug, info, warning, error }; // BAD: plain enum leaks names, ALL_CAPS clashes with macrosenum { RED, GREEN, BLUE };           // Enum.3 + Enum.5 + Enum.6 violation#define MAX_SIZE 100                  // Enum.1 violation -- use constexpr``` ## Source Files & Naming (SF.*, NL.*) ### Key Rules | Rule | Summary ||------|---------|| **SF.1** | Use `.cpp` for code files and `.h` for interface files || **SF.7** | Don't write `using namespace` at global scope in a header || **SF.8** | Use `#include` guards for all `.h` files || **SF.11** | Header files should be self-contained || **NL.5** | Avoid encoding type information in names (no Hungarian notation) || **NL.8** | Use a consistent naming style || **NL.9** | Use ALL_CAPS for macro names only || **NL.10** | Prefer `underscore_style` names | ### Header Guard ```cpp// SF.8: Include guard (or #pragma once)#ifndef PROJECT_MODULE_WIDGET_H#define PROJECT_MODULE_WIDGET_H // SF.11: Self-contained -- include everything this header needs#include <string>#include <vector> namespace project::module { class Widget {public:    explicit Widget(std::string name);    const std::string& name() const; private:    std::string name_;}; }  // namespace project::module #endif  // PROJECT_MODULE_WIDGET_H``` ### Naming Conventions ```cpp// NL.8 + NL.10: Consistent underscore_stylenamespace my_project { constexpr int max_buffer_size = 4096;  // NL.9: not ALL_CAPS (it's not a macro) class tcp_connection {                 // underscore_style classpublic:    void send_message(std::string_view msg);    bool is_connected() const; private:    std::string host_;                 // trailing underscore for members    int port_;}; }  // namespace my_project``` ### Anti-Patterns - `using namespace std;` in a header at global scope (SF.7)- Headers that depend on inclusion order (SF.10, SF.11)- Hungarian notation like `strName`, `iCount` (NL.5)- ALL_CAPS for anything other than macros (NL.9) ## Performance (Per.*) ### Key Rules | Rule | Summary ||------|---------|| **Per.1** | Don't optimize without reason || **Per.2** | Don't optimize prematurely || **Per.6** | Don't make claims about performance without measurements || **Per.7** | Design to enable optimization || **Per.10** | Rely on the static type system || **Per.11** | Move computation from run time to compile time || **Per.19** | Access memory predictably | ### Guidelines ```cpp// Per.11: Compile-time computation where possibleconstexpr auto lookup_table = [] {    std::array<int, 256> table{};    for (int i = 0; i < 256; ++i) {        table[i] = i * i;    }    return table;}(); // Per.19: Prefer contiguous data for cache-friendlinessstd::vector<Point> points;           // GOOD: contiguousstd::vector<std::unique_ptr<Point>> indirect_points; // BAD: pointer chasing``` ### Anti-Patterns - Optimizing without profiling data (Per.1, Per.6)- Choosing "clever" low-level code over clear abstractions (Per.4, Per.5)- Ignoring data layout and cache behavior (Per.19) ## Quick Reference Checklist Before marking C++ work complete: - [ ] No raw `new`/`delete` -- use smart pointers or RAII (R.11)- [ ] Objects initialized at declaration (ES.20)- [ ] Variables are `const`/`constexpr` by default (Con.1, ES.25)- [ ] Member functions are `const` where possible (Con.2)- [ ] `enum class` instead of plain `enum` (Enum.3)- [ ] `nullptr` instead of `0`/`NULL` (ES.47)- [ ] No narrowing conversions (ES.46)- [ ] No C-style casts (ES.48)- [ ] Single-argument constructors are `explicit` (C.46)- [ ] Rule of Zero or Rule of Five applied (C.20, C.21)- [ ] Base class destructors are public virtual or protected non-virtual (C.35)- [ ] Templates are constrained with concepts (T.10)- [ ] No `using namespace` in headers at global scope (SF.7)- [ ] Headers have include guards and are self-contained (SF.8, SF.11)- [ ] Locks use RAII (`scoped_lock`/`lock_guard`) (CP.20)- [ ] Exceptions are custom types, thrown by value, caught by reference (E.14, E.15)- [ ] `'\n'` instead of `std::endl` (SL.io.50)- [ ] No magic numbers (ES.45)