Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,17 @@
"url": "https://github.com/trailofbits"
},
"source": "./plugins/dimensional-analysis"
},
{
"name": "modern-cpp",
"version": "1.0.0",
"description": "Modern C++ best practices (C++20/23/26). Use when writing C++ code, creating new C++ projects, or modernizing legacy C++ patterns.",
"author": {
"name": "Julius Alexandre",
"email": "opensource@trailofbits.com",
"url": "https://github.com/trailofbits"
},
"source": "./plugins/modern-cpp"
}
]
}
1 change: 1 addition & 0 deletions .codex/skills/modern-cpp
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
/plugins/git-cleanup/ @hbrodin @dguido
/plugins/insecure-defaults/ @dariushoule @dguido
/plugins/let-fate-decide/ @tob-scott-a @dguido
/plugins/modern-cpp/ @wizardengineer @dguido
/plugins/modern-python/ @Ninja3047 @dguido
/plugins/mutation-testing/ @bohendo @dguido
/plugins/property-based-testing/ @hbrodin @dguido
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ cd /path/to/parent # e.g., if repo is at ~/projects/skills, be in ~/projects
| [gh-cli](plugins/gh-cli/) | Intercept GitHub URL fetches and redirect to the authenticated `gh` CLI |
| [git-cleanup](plugins/git-cleanup/) | Safely clean up git worktrees and local branches with gated confirmation workflow |
| [let-fate-decide](plugins/let-fate-decide/) | Draw Tarot cards using cryptographic randomness to add entropy to vague planning |
| [modern-cpp](plugins/modern-cpp/) | Modern C++ best practices (C++20/23/26) with compiler hardening and safe idioms |
| [modern-python](plugins/modern-python/) | Modern Python tooling and best practices with uv, ruff, and pytest |
| [seatbelt-sandboxer](plugins/seatbelt-sandboxer/) | Generate minimal macOS Seatbelt sandbox configurations |
| [second-opinion](plugins/second-opinion/) | Run code reviews using external LLM CLIs (OpenAI Codex, Google Gemini) on changes, diffs, or commits. Bundles Codex's built-in MCP server. |
Expand Down
10 changes: 10 additions & 0 deletions plugins/modern-cpp/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "modern-cpp",
"version": "1.0.0",
"description": "Modern C++ best practices (C++20/23/26). Use when writing C++ code, creating new C++ projects, or modernizing legacy C++ patterns.",
"author": {
"name": "Julius Alexandre",
"email": "opensource@trailofbits.com",
"url": "https://github.com/trailofbits"
}
}
41 changes: 41 additions & 0 deletions plugins/modern-cpp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# modern-cpp

Modern C++ best practices plugin for Claude Code, guiding AI-assisted development toward C++20/23/26 idioms with a security emphasis from Trail of Bits.

## When to Use

- Writing new C++ code (functions, classes, libraries)
- Modernizing legacy C++ patterns (pre-C++20)
- Working on security-critical C++ code
- Reviewing C++ code for modern idiom adoption
- Choosing between legacy and modern approaches to a problem

## What It Covers

### Language Features (Tiered by Usability)

| Tier | Standard | What |
|------|----------|------|
| Use Today | C++20 | Concepts, ranges, `std::span`, `std::format`, coroutines, `<=>` |
| Use Today | C++23 | `std::expected`, `std::print`, deducing `this`, `std::flat_map` |
| Deploy Now | Any | Compiler hardening flags, sanitizers, hardened libc++ |
| Plan For | C++26 | Reflection (eliminates serialization boilerplate and code generators) |
| Watch | C++26 | Contracts, `std::execution` (promising but needs compiler maturity) |

### Security

- Compiler hardening flags (GCC + Clang, per OpenSSF guidelines)
- Hardened libc++ modes (bounds-checking with ~0.3% overhead)
- Sanitizer setup (ASan, UBSan, TSan, MSan)
- Safe idioms organized by vulnerability class (memory, type, integer, concurrency)

### Anti-Patterns

30+ legacy-to-modern pattern replacements with rationale, covering memory management, type safety, error handling, concurrency, and metaprogramming.

## Installation

```
/plugin marketplace add trailofbits/skills
/plugin install modern-cpp
```
187 changes: 187 additions & 0 deletions plugins/modern-cpp/skills/modern-cpp/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
name: modern-cpp
description: Guides C++ code toward modern idioms (C++20/23/26). Use when writing new C++ code, modernizing legacy patterns, or working on security-critical C++. Replaces raw pointers with smart pointers, SFINAE with concepts, printf with std::print, error codes with std::expected.
---

# Modern C++

Guide for writing modern C++ using C++20, C++23, and C++26 idioms. Focuses on patterns that eliminate vulnerability classes and reduce boilerplate, with a security emphasis from Trail of Bits.

## When to Use This Skill

- Writing new C++ functions, classes, or libraries
- Modernizing existing C++ code (pre-C++20 patterns)
- Choosing between legacy and modern approaches
- Working on security-critical or safety-sensitive C++
- Reviewing C++ code for modern idiom adoption

## When NOT to Use This Skill

- **User explicitly requires older standard**: Respect constraints (embedded, legacy ABI)
- **Pure C code**: This skill is C++-specific
- **Build system questions**: CMake, Meson, Bazel configuration is out of scope
- **Non-C++ projects**: Mixed codebases where C++ isn't primary

## Anti-Patterns to Avoid

| Avoid | Use Instead | Why |
|-------|-------------|-----|
| `new`/`delete` | `std::make_unique`, `std::make_shared` | Eliminates leaks, double-free |
| Raw owning pointers | `std::unique_ptr`, `std::shared_ptr` | RAII ownership semantics |
| C arrays (`int arr[N]`) | `std::array<int, N>` | Bounds-aware, value semantics |
| Pointer + length params | `std::span<T>` | Non-owning, bounds-checkable |
| `printf` / `sprintf` | `std::format`, `std::print` | Type-safe, no buffer overflow |
| C-style casts `(int)x` | `static_cast<int>(x)` | Explicit intent, auditable |
| `#define` constants | `constexpr` variables | Scoped, typed, debuggable |
| SFINAE / `enable_if` | Concepts + `requires` | Readable constraints and errors |
| Error codes + out params | `std::expected<T, E>` | Composable, type-safe errors |
| `union` | `std::variant` | Type-safe, no silent UB |
| Raw `mutex.lock()/unlock()` | `std::scoped_lock` | Exception-safe, no deadlocks |
| `std::thread` | `std::jthread` | Auto-join, stop token support |
| `assert()` macro | `contract_assert` (C++26) | Visible to tooling, configurable |
| Manual CRTP | Deducing `this` (C++23) | Simpler, no template boilerplate |
| Macro code generation | Reflection (C++26) | Zero-overhead, composable |

See [anti-patterns.md](./references/anti-patterns.md) for the full table (30+ patterns).

## Decision Tree

```
What are you doing?
|
+-- Writing new C++ code?
| +-- Use modern idioms by default (C++20/23)
| +-- Choose the newest standard your compiler supports
| +-- See Feature Tiers below
|
+-- Modernizing existing code?
| +-- Start with Tier 1 (C++20/23) replacements
| +-- Prioritize by security impact (memory > types > style)
| +-- See anti-patterns.md for the migration table
|
+-- Security-critical code?
| +-- Enable compiler hardening flags (see below)
| +-- Enable hardened libc++ mode
| +-- Run sanitizers in CI
| +-- See safe-idioms.md and compiler-hardening.md
|
+-- Using C++26 features?
+-- Reflection: YES, plan for it (GCC 16+)
+-- Contracts: cautiously, for new API boundaries
+-- std::execution: wait for ecosystem maturity
+-- See cpp26-features.md
```

## Feature Tiers

Features are ranked by **practical usability today**, not by standard version.

### Tier 1: Use Today (C++20/23, solid compiler support)

| Feature | Replaces | Standard |
|---------|----------|----------|
| Concepts + `requires` | SFINAE, `enable_if` | C++20 |
| Ranges + views | Raw iterator loops | C++20 |
| `std::span<T>` | Pointer + length | C++20 |
| `std::format` | `sprintf`, iostream chains | C++20 |
| Three-way comparison `<=>` | Manual comparison operators | C++20 |
| `std::jthread` | `std::thread` + manual join | C++20 |
| Designated initializers | Positional struct init | C++20 |
| `std::expected<T,E>` | Error codes, exceptions at boundaries | C++23 |
| `std::print` / `std::println` | `printf`, `std::cout <<` | C++23 |
| Deducing `this` | CRTP, const/non-const duplication | C++23 |
| `std::flat_map` | `std::map` for read-heavy use | C++23 |
| Monadic `std::optional` | Nested if-checks on optionals | C++23 |

See [cpp20-features.md](./references/cpp20-features.md) and [cpp23-features.md](./references/cpp23-features.md).

### Tier 2: Deploy Now (no standard bump needed)

These improve safety without changing your C++ standard version:

- **Compiler hardening flags** — `-D_FORTIFY_SOURCE=3`, `-fstack-protector-strong`, `-ftrivial-auto-var-init=zero`
- **Hardened libc++** — `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST` for ~0.3% overhead bounds-checking
- **Sanitizers in CI** — ASan + UBSan as minimum; TSan for concurrent code
- **Warning flags** — `-Wall -Wextra -Wpedantic -Werror`

See [compiler-hardening.md](./references/compiler-hardening.md).

### Tier 3: Plan For (C++26, worth restructuring around)

**Reflection** is the single most transformative C++26 feature. It eliminates:
- Serialization boilerplate (one generic function replaces per-struct `to_json`)
- Code generators (protobuf codegen, Qt MOC)
- Macro-based registration and enum-to-string hacks

GCC 16 (April 2026) has reflection merged. Plan new code to benefit from it.

### Tier 4: Watch (C++26, needs maturation)

- **Contracts** (`pre`/`post`/`contract_assert`) — Better than `assert()`, but no virtual function support and limited compiler support. Adopt cautiously for new API boundaries.
- **std::execution** (senders/receivers) — Powerful async framework, but steep learning curve, no scheduler ships with it, and poor documentation. Wait for ecosystem maturity.

See [cpp26-features.md](./references/cpp26-features.md).

## Compiler Hardening Quick Reference

### Essential Flags (GCC + Clang)

```
-Wall -Wextra -Wpedantic -Werror
-D_FORTIFY_SOURCE=3
-fstack-protector-strong
-fstack-clash-protection
-ftrivial-auto-var-init=zero
-fPIE -pie
-Wl,-z,relro,-z,now
```

### Clang-Specific

```
-Wunsafe-buffer-usage
```

### Hardened libc++ (Clang/libc++ only)

```
-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST
```

Google deployed this across Chrome and their server fleet: ~0.3% overhead, 1000+ bugs found, 30% reduction in production segfaults.

See [compiler-hardening.md](./references/compiler-hardening.md) for the full guide.

## Rationalizations to Reject

| Rationalization | Why It's Wrong |
|----------------|----------------|
| "It compiles without warnings" | Warnings depend on which flags you enable. Add `-Wall -Wextra -Wpedantic`. |
| "ASan is too slow for production" | Use GWP-ASan for sampling-based production detection (~0% overhead). |
| "We only use safe containers" | Iterator invalidation and unchecked `optional` access are still exploitable. |
| "Smart pointers are slower" | `std::unique_ptr` has zero overhead vs raw pointers. Measure before claiming. |
| "Our code doesn't have memory bugs" | Google found 1000+ bugs when enabling hardened libc++. So did everyone else. |
| "C++26 features aren't available yet" | C++20/23 features are. Hardening flags work on any standard. Start there. |
| "Modern C++ is harder to read" | `std::expected` is more readable than checking error codes across 5 out-params. |

## Best Practices Checklist

- [ ] Use smart pointers for ownership, raw pointers only for non-owning observation
- [ ] Prefer `std::span` over pointer + length for function parameters
- [ ] Use `std::expected` for functions that can fail with typed errors
- [ ] Constrain templates with concepts, not SFINAE
- [ ] Enable compiler hardening flags and hardened libc++ in all builds
- [ ] Run ASan + UBSan in CI; add TSan for concurrent code
- [ ] Use `constexpr` / `consteval` where possible (UB-free by design)
- [ ] Mark functions `[[nodiscard]]` when ignoring the return value is likely a bug
- [ ] Prefer value semantics; use `std::variant` over `union`, `enum class` over `enum`
- [ ] Initialize all variables at declaration

## Read Next

- [anti-patterns.md](./references/anti-patterns.md) — Full legacy-to-modern migration table (30+ patterns)
- [cpp20-features.md](./references/cpp20-features.md) — Concepts, ranges, span, format, coroutines
- [cpp23-features.md](./references/cpp23-features.md) — expected, print, deducing this, flat_map
- [cpp26-features.md](./references/cpp26-features.md) — Reflection, contracts, memory safety improvements
- [compiler-hardening.md](./references/compiler-hardening.md) — Flags, sanitizers, hardened libc++
- [safe-idioms.md](./references/safe-idioms.md) — Security patterns by vulnerability class
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Anti-Patterns: Legacy to Modern C++

Comprehensive reference of legacy C++ patterns and their modern replacements. Each entry explains WHY the modern version is better — not just that it exists.

## Memory Management

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| `new T` / `delete p` | `std::make_unique<T>()` | C++14 | Eliminates memory leaks, double-free; exception-safe construction |
| `new T[]` / `delete[] p` | `std::vector<T>` or `std::make_unique<T[]>(n)` | C++14 | Automatic sizing, bounds-aware with `.at()` |
| Raw owning `T*` | `std::unique_ptr<T>` | C++11 | RAII ownership; zero overhead vs raw pointer |
| Raw shared `T*` | `std::shared_ptr<T>` via `std::make_shared` | C++11 | Reference-counted lifetime; thread-safe refcount |
| C arrays `int arr[N]` | `std::array<int, N>` | C++11 | Value semantics, `.size()`, no decay to pointer |
| `malloc`/`free` | Containers or smart pointers | C++11 | Type-safe, exception-safe, RAII |
| `memcpy(dst, src, n)` | `std::copy(src, src+n, dst)` or container assignment | C++11 | Type-safe, works with non-trivial types |
| `memset(buf, 0, n)` | Value initialization or `std::fill` | C++11 | No risk of zeroing non-trivially-constructible types |
| Pointer + length parameter pairs | `std::span<T>` | C++20 | Carries size, bounds-checkable with hardened mode |
| `const char*` for non-owning strings | `std::string_view` | C++17 | Carries length, no null-terminator assumption |
| Nullable `T*` for optional values | `std::optional<T>` | C++17 | Explicit intent, no null dereference risk |

## Type Safety

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| C-style cast `(int)x` | `static_cast<int>(x)` | C++11 | Explicit intent, greppable, won't silently reinterpret |
| `reinterpret_cast` for type punning | `std::bit_cast<T>(x)` | C++20 | Defined behavior, `constexpr`-compatible |
| `union` for variants | `std::variant<A, B, C>` | C++17 | Type-safe access via `std::visit`, no silent UB |
| `void*` type erasure | `std::any`, `std::variant`, or templates | C++17 | Type-safe, no manual casting |
| Plain `enum` | `enum class` | C++11 | Scoped, no implicit int conversion |
| `NULL` or `0` | `nullptr` | C++11 | Unambiguous null pointer, no overload confusion |
| Implicit single-arg constructors | Mark `explicit` | C++11 | Prevents surprising implicit conversions |
| Unchecked return values | `[[nodiscard]]` | C++17 | Compiler warns when return value is ignored |
| `= delete` without message | `= delete("reason")` | C++26 | Documents why the overload is forbidden |

## Error Handling

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| Error codes + out-params | `std::expected<T, E>` | C++23 | Composable with `.and_then()`, `.transform()`, `.or_else()` |
| `assert()` macro | `contract_assert` | C++26 | Visible to tooling, configurable enforcement modes |
| Unchecked `errno` | `std::expected` or exceptions | C++23 | Forces caller to handle the error path |
| Throwing in constructor for expected failures | Factory returning `std::expected` | C++23 | No exception overhead for expected failure paths |

## I/O and Formatting

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| `printf` / `sprintf` | `std::format` / `std::print` | C++20/23 | Type-safe, no format string vulnerabilities |
| `snprintf` for string building | `std::format` | C++20 | Returns `std::string`, no buffer management |
| `std::cout << a << b << c` | `std::println("{} {} {}", a, b, c)` | C++23 | Readable, no stream state issues, faster |

## Concurrency

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| `mutex.lock()` / `mutex.unlock()` | `std::scoped_lock` | C++17 | Exception-safe, supports multiple mutexes (no deadlock) |
| `std::thread` + manual `.join()` | `std::jthread` | C++20 | Auto-joins on destruction, supports stop tokens |
| `volatile` for thread sync | `std::atomic<T>` | C++11 | Actually guarantees atomic operations and memory ordering |
| Manual condition variable notify | `std::latch`, `std::barrier` | C++20 | Higher-level, less error-prone synchronization |
| Hand-rolled atomic CAS loops | `std::atomic::fetch_max/fetch_min` | C++26 | Standard, correct, optimized |

## Metaprogramming

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| `std::enable_if` / SFINAE | Concepts + `requires` | C++20 | Readable constraints, clear error messages |
| CRTP for static polymorphism | Deducing `this` | C++23 | No template boilerplate, works with lambdas |
| Macros for code generation | Static reflection | C++26 | Zero-overhead, composable, type-safe |
| `std::tuple_element` for pack access | Pack indexing `T...[N]` | C++26 | Direct access, no recursive template machinery |
| `std::function` for non-owning callbacks | `std::function_ref` | C++26 | No allocation, passable in registers |

## Containers

| Avoid | Use Instead | Standard | Why |
|-------|-------------|----------|-----|
| `std::map` for read-heavy lookups | `std::flat_map` | C++23 | Cache-friendly, 8-17x faster lookup for small-medium maps |
| `boost::static_vector` | `std::inplace_vector` | C++26 | Standard, no Boost dependency, fallible API |
| `std::function` (when non-owning) | `std::function_ref` | C++26 | Zero allocation overhead |
| Raw pointer polymorphism | `std::indirect<T>`, `std::polymorphic<T>` | C++26 | Value semantics, copyable, no manual lifetime management |
| Manual iterator loops | `std::ranges::` algorithms + views | C++20 | Composable, lazy, no off-by-one |
Loading
Loading