Install
$ agentstack add skill-affaan-m-ecc-cpp-coding-standards ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
C++ Coding Standards (C++ Core Guidelines)
Comprehensive coding standards for modern C++ (C++17/20/23) derived from the C++ Core Guidelines. 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.,
enumvsenum 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:
- RAII everywhere (P.8, R.1, E.6, CP.20): Bind resource lifetime to object lifetime
- Immutability by default (P.10, Con.1-5, ES.25): Start with
const/constexpr; mutability is the exception - Type safety (P.4, I.4, ES.46-49, Enum.3): Use the type system to prevent errors at compile time
- Express intent (P.3, F.1, NL.1-2, T.10): Names, types, and concepts should communicate purpose
- Minimize complexity (F.2-3, ES.5, Per.4-5): Simple code is correct code
- 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
// P.10 + I.4: Immutable, strongly typed interface
struct Temperature {
double kelvin;
};
Temperature boil(const Temperature& water);
DON'T
// Weak interface: unclear ownership, unclear units
double boil(double* temp);
// Non-const global variable
int 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
// F.16: Cheap types by value, others by const&
void print(int x); // cheap: by value
void 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 parameters
struct ParseResult {
std::string token;
int position;
};
ParseResult parse(std::string_view input); // GOOD: return struct
// BAD: output parameters
void parse(std::string_view input,
std::string& token, int& pos); // avoid this
Pure Functions and constexpr
// F.4 + F.8: Pure, constexpr where possible
constexpr int factorial(int n) noexcept {
return (n (size)), size_(size) {}
~Buffer() = default;
Buffer(const Buffer& other)
: data_(std::make_unique(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(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 data_;
std::size_t size_;
};
Class Hierarchy
// C.35 + C.128: Virtual destructor, use override
class 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/memcpyon non-trivial types (C.90) - Providing different default arguments for virtual function and overrider (C.140)
- Making data members
constor 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_ptrs |
Smart Pointer Usage
// R.11 + R.20 + R.21: RAII with smart pointers
auto widget = std::make_unique("config"); // unique ownership
auto cache = std::make_shared(1024); // shared ownership
// R.3: Raw pointer = non-owning observer
void render(const Widget* w) { // does NOT own w
if (w) w->draw();
}
render(widget.get());
RAII Pattern
// R.1: Resource acquisition is initialization
class 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_ptrwhereunique_ptrsuffices (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
// ES.20 + ES.23 + ES.25: Always initialize, prefer {}, default to const
const int max_retries{3};
const std::string name{"widget"};
const std::vector primes{2, 3, 5, 7, 11};
// ES.28: Lambda for complex const initialization
const 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
0orNULLas pointer (ES.47 -- usenullptr) - 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
// E.14 + E.15: Custom exception types, throw by value, catch by reference
class 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
intor 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 |
// Con.1 through Con.5: Immutability by default
class 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 reference
void display(const Sensor& s) {
std::cout lock(mutex_); // CP.44: named!
queue_.push(value);
cv_.notify_one();
}
int pop() {
std::unique_lock 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 queue_;
};
Multiple Mutexes
// 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
volatilefor 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(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)
#include
// T.10 + T.11: Constrain templates with standard concepts
template
T gcd(T a, T b) {
while (b != 0) {
a = std::exchange(b, a % b);
}
return a;
}
// T.13: Shorthand concept syntax
void sort(std::ranges::random_access_range auto& range) {
std::ranges::sort(range);
}
// Custom concept for domain-specific constraints
template
concept Serializable = requires(const T& t) {
{ t.serialize() } -> std::convertible_to;
};
template
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
constexprsuffices (T.120) typedefinstead ofusing(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) |
// SL.con.1 + SL.con.2: Prefer vector/array over C arrays
const std::array fixed_data{1, 2, 3, 4};
std::vector dynamic_data;
// SL.str.1 + SL.str.2: string owns, string_view observes
std::string build_greeting(std::string_view name) {
return "Hello, " + std::string(name) + "!";
}
// SL.io.50: Use '\n' not endl
std::cout
#include
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
// NL.8 + NL.10: Consistent underscore_style
namespace my_project {
constexpr int max_buffer_size = 4096; // NL.9: not ALL_CAPS (it's not a macro)
class tcp_connection { // underscore_style class
public:
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 measurement
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: affaan-m
- Source: affaan-m/ECC
- License: MIT
- Homepage: https://ecc.tools
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.