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_someis the fundamental partial-read primitive. -
WriteStream — Why
write_someis the fundamental partial-write primitive, and when it outperformswrite_now. -
Type-Erasing Awaitables — How the
any_*wrappers achieve zero steady-state allocation. -
Executor — The
Executorconcept’s relationship to Asio, and whydispatchreturns a coroutine handle. -
Run API — Why
run_asyncandrunuse a two-phasef(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.