Design

Capy’s public interface—​tasks, buffers, streams—​is intentionally small. Behind that interface are design decisions that determine how concepts compose, where responsibility boundaries fall, and what guarantees the library can make. This section documents those decisions.

Each page examines one concept or facility in depth: the rationale behind its design, the alternatives that were considered, and the tradeoffs that were made. Formal definitions live in the header reference; these pages link to cpp: symbols rather than repeat them. If you have ever wondered why a particular concept requires a specific primitive, or why certain abstractions exist as separate concepts, the answers are here. These documents are reference material for library contributors and advanced users. They assume familiarity with the tutorial sections and focus on design reasoning rather than usage.

What This Section Covers

  • Layered Abstractions — Why Capy offers templates, virtual dispatch, and type erasure as separate layers instead of one abstraction level.

  • Why Capy Is Separate — Why Capy and Corosio are two libraries instead of one.

  • ReadStream — Why read_some is the fundamental partial-read primitive.

  • WriteStream — Why write_some is the fundamental partial-write primitive, and when it outperforms write_now.

  • Type-Erasing Awaitables — How the any_* wrappers achieve zero steady-state allocation.

  • Executor — The Executor concept’s relationship to Asio, and why dispatch returns a coroutine handle.

  • Run API — Why run_async and run use a two-phase f(context)(task) call syntax.

  • Why Not Cobalt? — How Capy’s foundation differs from Boost.Cobalt’s Asio-based design.

  • Why Not Cobalt Concepts? — A side-by-side look at type-erased write-stream algorithms in both libraries.

  • Why Not TooManyCooks? — Choosing between Capy and TooManyCooks by workload: network I/O versus compute.