Capy
Capy abstracts away sockets, files, and asynchrony with type-erased streams and buffer sequences�code compiles fast because the implementation is hidden. It provides the framework for concurrent algorithms that transact in buffers of memory: networking, serial ports, console, timers, and any platform I/O. This is only possible because Capy is coroutine-only, enabling optimizations and ergonomics that hybrid approaches must sacrifice.
What This Library Does
-
Lazy coroutine tasks �
task<T>with forward-propagating stop tokens and automatic cancellation -
Buffer sequences � taken straight from Asio and improved
-
Stream concepts �
ReadStream,WriteStream,ReadSource,WriteSink,BufferSource,BufferSink -
Type-erased streams �
any_stream,any_read_stream,any_write_streamfor fast compilation -
Concurrency facilities � executors, strands, thread pools,
when_all,when_any -
Test utilities � mock streams, mock sources/sinks, error injection
What This Library Does Not Do
-
Networking � no sockets, acceptors, or DNS; that’s what Corosio provides
-
Protocols � no HTTP, WebSocket, or TLS; see the Http and Beast2 libraries
-
Platform event loops � no io_uring, IOCP, epoll, or kqueue; Capy is the layer above
-
Callbacks or futures � coroutine-only means no other continuation styles
-
Sender/receiver � Capy uses the IoAwaitable protocol, not
std::execution
Target Audience
-
Users of Corosio � portable coroutine networking
-
Users of Http � sans-I/O HTTP/1.1 clients and servers
-
Users of Websocket � sans-I/O WebSocket
-
Users of Beast2 � high-level HTTP/WebSocket servers
-
Users of Burl � high-level HTTP client
All of these are built on Capy. Understanding its concepts�tasks, buffer sequences, streams, executors�unlocks the full power of the stack.
Design Philosophy
-
Use case first. Buffer sequences, stream concepts, executor affinity�these exist because I/O code needs them, not because they’re theoretically elegant.
-
Coroutines-only. No callbacks, futures, or sender/receiver. Hybrid support forces compromises; full commitment unlocks optimizations that adapted models cannot achieve.
-
Address the complaints of C++. Type erasure at boundaries, minimal dependencies, and hidden implementations keep builds fast and templates manageable.
Code Convention
|
Unless otherwise specified, all code examples in this documentation assume the following:
|
Quick Example
This example demonstrates a minimal coroutine that reads from a stream and echoes the data back:
#include <boost/capy.hpp>
using namespace boost::capy;
task<> echo(any_stream& stream)
{
char buf[1024];
for(;;)
{
auto [ec, n] = co_await stream.read_some(mutable_buffer(buf));
if(ec.failed())
co_return;
auto [wec, wn] = co_await write(stream, const_buffer(buf, n));
if(wec.failed())
co_return;
}
}
int main()
{
thread_pool pool;
// In a real application, you would obtain a stream from Corosio
// and call: run_async(pool.get_executor())(echo(stream));
return 0;
}
The echo function accepts an any_stream&�a type-erased wrapper that works with any concrete stream implementation. The function reads data into a buffer, then writes it back. Both operations use co_await to suspend until the I/O completes.
The task<> return type (equivalent to task<void>) creates a lazy coroutine that does not start executing until awaited or launched with run_async.
Next Steps
-
Quick Start � Set up your first Capy project
-
C++20 Coroutines Tutorial � Learn coroutines from the ground up
-
Concurrency Tutorial � Understand threads, mutexes, and synchronization
-
Coroutines in Capy � Deep dive into
task<T>and the IoAwaitable protocol -
Buffer Sequences � Master the concept-driven buffer model
-
Stream Concepts � Understand the six stream concepts