Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Cpp Coroutines

ASecurity

Use when working with C++20 co_await, co_yield, and co_return, implementing promise_type, sizing coroutine frames, or debugging suspended coroutines in GDB. Not for Rust async: use idiomatic-rust.

54 stars
0 votes
0 copies
0 views
Added 9/20/2026
ai-agentsrustc++bashexpressdebugging

Security Analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned 9/20/2026

$npx -y skills add OutlineDriven/outline-driven-development --skill cpp-coroutines --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cpp Coroutines?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Cpp Coroutines
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/outlinedriven-cpp-coroutines-outline-driven-development/badge)](https://www.skillsdirectory.com/skills/outlinedriven-cpp-coroutines-outline-driven-development)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: cpp-coroutines
description: 'Use when working with C++20 co_await, co_yield, and co_return, implementing promise_type, sizing coroutine frames, or debugging suspended coroutines in GDB. Not for Rust async: use idiomatic-rust.'
---

# C++20 coroutines

A coroutine is a function whose execution suspends and resumes while its frame survives on the heap. The language provides the keywords; the library author provides `promise_type`, which decides what suspension and return mean.

## Contract

| Field | Bound contract |
|---|---|
| Trigger | The task writes or reviews `co_await`, `co_yield`, or `co_return` code, implements a `promise_type`, explains the coroutine frame, or debugs a suspended coroutine. |
| Authority | Read-only. The skill explains mechanics and drafts coroutine types; edits land through the normal coding path. No remote mutation. |
| Side effect | None. |
| Done | The drafted coroutine type compiles against the project standard, or the coroutine under debug is located and its promise state read. |

## Inputs

- The coroutine code or the behavior wanted: required.
- The toolchain and standard level: required. C++20 keywords with a C++23 library where `<generator>` is wanted; current GCC and Clang ship both.
- A debugger session: required only for the debugging path.

## Procedure

1. Recognize the three keywords and what they demand. A function containing any of them is a coroutine, and its return type must carry a `promise_type`. Done when: every coroutine in the source has a coroutine-shaped return type.

```cpp
co_return value;               // return and finish
co_yield value;                // produce a value, suspend
auto r = co_await awaitable;   // suspend until awaitable completes
```

2. Draft a lazy `Task` when only the final result matters. `initial_suspend` returns `suspend_always` so the body runs only on `resume()`, and `final_suspend` is `noexcept` by rule. The owner destroys the handle exactly once. Done when: the type owns its handle, destroys it in the destructor, and propagates the exception.

```cpp
#include <coroutine>
#include <exception>
#include <optional>
#include <utility>

template <typename T>
struct Task {
    struct promise_type {
        std::optional<T> value;
        std::exception_ptr exception;

        Task get_return_object() {
            return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        std::suspend_always initial_suspend() { return {}; }            // lazy start
        std::suspend_always final_suspend() noexcept { return {}; }     // must be noexcept
        void return_value(T v) { value = std::move(v); }
        void unhandled_exception() { exception = std::current_exception(); }
    };

    std::coroutine_handle<promise_type> handle;

    explicit Task(std::coroutine_handle<promise_type> h) : handle(h) {}
    Task(Task&&) = default;
    Task& operator=(Task&&) = default;
    ~Task() { if (handle) handle.destroy(); }

    T get() {
        handle.resume();
        if (handle.promise().exception)
            std::rethrow_exception(handle.promise().exception);
        return std::move(*handle.promise().value);
    }
};
```

3. Draft a `Generator` when values stream out. `yield_value` stores and suspends; the iterator resumes to advance. Done when: the range-for loop produces the sequence and destroys the coroutine at scope exit.

```cpp
template <typename T>
struct Generator {
    struct promise_type {
        T current_value;
        Generator get_return_object() {
            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }   // keep it simple, fail loud
        std::suspend_always yield_value(T v) {
            current_value = v;
            return {};
        }
    };

    std::coroutine_handle<promise_type> handle;
    explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); }

    bool advance() {                 // false when exhausted
        handle.resume();
        return !handle.done();
    }
    T current() const { return handle.promise().current_value; }
};
```

For a standard type instead of a hand-written one, C++23 ships `std::generator<T>` in `<generator>`; current GCC and Clang standard libraries provide it.

4. Write an awaitable by filling three members. `await_ready` returns true to skip suspension. `await_suspend` receives the handle and schedules its resumption. `await_resume` supplies the value of the `co_await` expression. Done when: the awaitable suspends once, resumes once, and never resumes a finished coroutine.

```cpp
struct TimerAwaitable {
    int delay_ms;
    bool await_ready() const noexcept { return delay_ms <= 0; }
    void await_suspend(std::coroutine_handle<> h);   // schedule h.resume() after the delay
    void await_resume() const noexcept {}
};
```

`std::suspend_always` and `std::suspend_never` are the built-in trivial awaitables.

5. Keep the frame small. The compiler heap-allocates one frame holding the promise, the resume state, and every local that crosses a suspension point. Check the generated code on Compiler Explorer with `-std=c++20 -O2 -S`, or `-emit-llvm` and the `coro.size` marker on Clang. Done when: no large object outlives a suspension without need.

```cpp
// large object alive across co_await: it lives in the frame
auto buf = get_data();
auto sz = buf.size();     // capture what is needed
buf.clear();              // release the rest before suspending
co_await next_event;
```

At `-O2` the compiler may apply heap allocation elision (HALO) and move the frame to the caller's stack, but elision is not guaranteed; treat the allocation as real.

6. Debug a suspended coroutine. The coroutine lives on the heap, not on the stack, until resumed. Break inside the coroutine body, then reach the promise through the handle variable in scope; the member layout is implementation specific, so print through the promise type you wrote. Done when: the promise fields are readable in the debugger.

```bash
g++ -std=c++20 -g -O0 -o app app.cpp
gdb ./app
(gdb) break my_coro
(gdb) run
(gdb) info locals          # the handle and promise live in this frame
(gdb) p my_task.handle.promise()   # toolchain exposes the promise through the handle
```

GDB carries no built-in `info coroutines` command in common builds, so trace suspended coroutines through the `coroutine_handle` objects your code stores.

7. Use a composed library for concurrency. Boost.Asio's `co_spawn` runs an `awaitable<>` on an executor, and every `co_await` chains a completion without callback nesting. Done when: the session runs to completion on one `io_context`.

```cpp
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>

boost::asio::awaitable<void> echo_session(boost::asio::ip::tcp::socket s) {
    char buf[1024];
    for (;;) {
        std::size_t n = co_await s.async_read_some(boost::asio::buffer(buf));
        co_await boost::asio::async_write(s, boost::asio::buffer(buf, n));
    }
}
```

Boost 1.92.0 is the current release this tree pins.

8. Audit the recurring defects. Done when: each defect class is checked in the code under review.

| Defect | Cause | Fix |
|-------|-------|-----|
| `co_await` rejected in this function | The return type is not a coroutine type | Return `Task` or `Generator`, or move the code into one |
| Handle used after finish | `resume()` on a done coroutine | Check `handle.done()` before every resume |
| Double resume | Two owners resumed one coroutine | One owner; the rest hold weak references |
| Frame leaked | No `destroy()` ever ran | Own the handle with a destructor, as in steps 2 and 3 |
| Frame too large | Big locals across suspension | Move data out before `co_await`, per step 5 |
| Deep recursive `co_await` chains | Each link allocates a frame | Bound the chain depth, or loop instead of recursing |

## Failure and recovery

| Failure class | Behavior |
|---|---|
| Compile error inside `promise_type` | One required member is missing or wrongly signed. Check `get_return_object`, both suspend hooks, `return_value` or `return_void` exactly one of them, and `unhandled_exception`. |
| `final_suspend` not `noexcept` | The rule requires it. Make it `noexcept` and recompile. |
| Crash on second `get()` | The coroutine finished and the first `get()` destroyed it. Make the type move-only and single-use. |
| Exception swallowed | `unhandled_exception` stored it but nobody rethrows. Route it through the consumer, as in step 2. |
| Header parse time explosion | Asio-level coroutine headers are heavy. Isolate them in `.cpp` files and measure with `-ftime-report` or `-ftime-trace`. |

## Output

A working coroutine type or a located suspension point, with the promise contract stated and the ownership rule for the handle written next to the type. The frame layout and GDB mechanics above are the complete procedure; nothing further is deferred.

Attribution

OutlineDrivenOutlineDriven
View sourceSee grades on GitHubMore from OutlineDriven →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698431 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →