All skills

Write, review, or refactor safe and clear C++17, C++20, and C++23 code using the C++ Core Guidelines.

  • 1 file
  • 7.3 KB
  • Updated 4 weeks ago
  • GitHub

Use this Skill: https://skilld.dev/gh/agenticluke/safe-modern-cpp-plus/skill

This session only. Nothing lands on disk.

SKILL.md

≈27 tokens always: the name and description. ≈1.8k when used: this file.

C++ Coding Standards

This skill comes from ECC. Credit belongs to the original author.

Use the C++ Core Guidelines as the main guide. Write code that is safe, clear, simple, and easy to change.

When to Use This Skill

Use this skill when you:

  • Write C++ classes, functions, or templates
  • Review or fix C++ code
  • Plan a C++ project
  • Set a shared code style
  • Choose between language tools
  • Work with memory, files, locks, or other resources

First Steps

Before you change code:

  1. Check the C++ version used by the project.
  2. Read the local build and style files.
  3. Follow project rules when they are clear.
  4. Do not use a newer C++ feature than the build allows.
  5. Keep public APIs stable unless the task says to change them.

Core Rules

  1. Use RAII. Put each resource inside an object that cleans it up.
  2. Keep data fixed by default. Start with const or constexpr.
  3. Use strong types. Let the compiler catch bad values and bad calls.
  4. Show intent. Use clear names, types, and return values.
  5. Keep code simple. Prefer small functions and plain control flow.
  6. Use values when you can. Use pointers only when they show a real link or owner.
  7. Check errors. Do not hide or ignore a failed call.
  8. Avoid leaks and races. Memory, files, locks, and threads must be safe.

Key C++ Core Rules

Rule Meaning
P.1 Show ideas in the code itself.
P.3 Make intent clear.
P.4 Keep the program type-safe.
P.5 Prefer checks at build time.
P.8 Do not leak resources.
P.10 Prefer fixed data over data that can change.
I.1 Make APIs clear.
I.2 Avoid global data that can change.
I.4 Use exact and strong types in APIs.

Ownership and Pointers

Choose a type that shows who owns an object:

  • Use a value such as Widget when no pointer is needed.
  • Use std::unique_ptr<T> for one owner.
  • Use std::shared_ptr<T> only when owners truly share the same life span.
  • Use std::weak_ptr<T> to watch shared data without owning it.
  • Use T* for a link that does not own the object and may be null.
  • Use T& for a link that does not own the object and must not be null.
  • Use std::span<T> for a view of a list when the project supports it.
  • Do not make a smart pointer just to pass an object to a function.
  • Do not build two smart pointers from the same raw pointer.
  • Do not use delete on memory owned by a smart pointer.
  • Do not return a pointer or reference to local data.

Resource Safety

Use RAII for all resources, not only memory. This includes:

  • Files
  • Locks
  • Sockets
  • Threads
  • Handles
  • Database work
  • Temporary state

Do not pair manual open and close calls when a safe wrapper can do both.

Functions and APIs

  • Keep functions short and focused.
  • Pass small values by value.
  • Pass large read-only values by const T&.
  • Return values instead of output pointer args when this is clear.
  • Use [[nodiscard]] for results that callers must check.
  • Use noexcept only when the function can keep that promise.
  • Mark one-arg constructors explicit unless auto conversion is wanted.
  • Use enum class instead of plain enum.
  • Avoid magic numbers. Use named constants.
  • Do not use 0 or NULL for a null pointer. Use nullptr.

Classes

  • Start with the Rule of Zero. Let member types manage their own cleanup.
  • If a class owns a raw resource, define or block copy and move steps with care.
  • Keep class rules true after each public call.
  • Hide data that callers should not change.
  • Use inheritance only for a real "is a" link.
  • Give a base class a virtual destructor if it may be deleted through a base pointer.
  • Prefer simple parts joined together over deep class trees.

Const and Build-Time Checks

  • Mark data and methods const when they do not change state.
  • Use constexpr when a value can be found at build time.
  • Use static_assert for rules that can be checked at build time.
  • Do not use const_cast to hide a weak design.
  • Use auto when the type is clear from the right side.
  • Write the full type when auto would hide key facts.

Errors

Follow the error style already used by the project.

  • Use exceptions when the project allows them and the error cannot be handled there.
  • Use a result type, such as std::expected in C++23, when failure is a normal result.
  • Do not throw from destructors.
  • Do not catch every error and then ignore it.
  • Do not use exceptions across C APIs or other borders that cannot carry them.
  • Check for null before use when null is allowed.

Casts and Low-Level Code

  • Avoid C-style casts.
  • Use static_cast, dynamic_cast, const_cast, or reinterpret_cast only for their exact job.
  • Treat reinterpret_cast and pointer math as high-risk code.
  • Keep low-level code small and behind a clear API.
  • Do not read data through the wrong type.
  • Check size, range, sign, and overflow when values cross type bounds.

Loops and Lists

  • Prefer range-based for loops.
  • Use standard library tools when they make the code clearer.
  • Check list bounds.
  • Use signed and unsigned values with care.
  • Do not keep iterators, pointers, or references after a list change may make them invalid.

Threads

  • Share as little data as possible.
  • Use RAII lock types such as std::lock_guard or std::scoped_lock.
  • Do not call unknown code while holding a lock.
  • Use one clear lock order to avoid deadlock.
  • Do not use volatile for thread safety.
  • Make sure each thread is joined, stopped, or owned by a safe wrapper.

Edge Cases

Check these before you finish:

  • Empty input
  • Null links
  • Moved-from objects
  • Self-copy and self-move
  • Very large sizes
  • Negative values
  • Signed and unsigned mixes
  • Integer overflow
  • Failed file or memory work
  • Exceptions during setup
  • Early returns while a resource is held
  • Object life span across async work
  • Iterator or pointer invalidation
  • Shared pointer cycles
  • Base class deletion
  • Code built with exceptions or RTTI turned off

Concrete Example

Bad code:

Widget* make_widget(const char* name)
{
    Widget* item = new Widget;
    item->name = name;
    return item;
}

void use_widget()
{
    Widget* item = make_widget("clock");
    run(*item);
}

This code has unclear ownership and leaks memory.

Better code:

[[nodiscard]] std::unique_ptr<Widget> make_widget(std::string name)
{
    return std::make_unique<Widget>(std::move(name));
}

void use_widget()
{
    auto item = make_widget("clock");
    run(*item);
}

If a pointer is not needed, prefer a value:

[[nodiscard]] Widget make_widget(std::string name)
{
    return Widget{std::move(name)};
}

void use_widget()
{
    const Widget item = make_widget("clock");
    run(item);
}

Review Checklist

Before you give the code back, confirm that:

  • The code builds with the project C++ version.
  • Tests pass.
  • Ownership is clear.
  • Resources clean themselves up.
  • Null and empty cases are safe.
  • No local pointer or reference escapes its life span.
  • Public API changes are needed and named.
  • Error results are checked.
  • Casts and raw memory work are rare and explained.
  • The code is as simple as the task allows.

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at 5432501. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 4 weeks ago.

Activeupdated 4 weeks ago
origin
ECC

README badge

README badge for agenticluke/safe-modern-cpp-plus