On Wed, Feb 4, 2026 at 2:26 PM Ruben Perez <rubenperez038@gmail.com> wrote:
When we say sans-io, it usually means that it can be used regardless of the I/O framework and completion paradigm you're using (think of OpenSSL). Can I implement an Asio universal async operation using Capy? I got the impression that I can't, however it should be possible if the library is truely sans-io.
What you are asking for, "implement an Asio universal async operation using Capy" is not possible, but for pedantic reasons which we don't need to get into here. What I think you are really asking for is a buffer-oriented, type-erased stream upon which you can implement algorithms which transact in buffers of inputs and outputs. Many protocols can be implemented with this primitive: HTTP, Websocket, MySQL, ZLib, JSON serialization and deserialization, and a lot more.
The reason I'm asking is because I'm writing a PostgreSQL library that targets C++20 and above, hence your proposed library is an option. I intend to expose a sans-io API. This API needs to power both a Corosio connection and an Asio connection (at the end of the day, I want users, and today users do Asio).
I will list some facts and then we can see where they go. * Capy's buffer sequences are compatible with Asio's. You can pass each where the other is expected. Yet Capy does not include asio headers. * capy::any_stream is a concrete type which can be constructed by value or by reference from any object that satisfies capy::Stream: https://github.com/cppalliance/capy/blob/18aa8a8b5fe8d0925e983907d057a1111c4... https://github.com/cppalliance/capy/blob/18aa8a8b5fe8d0925e983907d057a1111c4... * values of types which satisfy capy::Executor may be used to construct capy::executor_ref, a type-erasing reference wrapper which does not allocate and is two pointers in size: https://github.com/cppalliance/capy/blob/18aa8a8b5fe8d0925e983907d057a1111c4... https://github.com/cppalliance/capy/blob/18aa8a8b5fe8d0925e983907d057a1111c4... * asio_executor adapts asio::io_context::executor_type to satisfy capy::Executor: https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... * asio_context hides an asio::io_context in a pimpl, and offers a type-erased asio_executor (via get_executor): https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... * asio_socket adapts an asio::ip::tcp::socket to satisfy capy::Stream https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... Some notes on this adaptor: 1. It is coroutine-only (read_some and write_some return awaitables) 2. It operates on any BufferSequence (the sequence is unrolled up to 8 elements, which can be adjusted) 3. It connects the IoAwaitable protocol std::stop_token to an asio cancellation slot * The private function make_socket_pair returns two connected asio::ip::tcp::socket: https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... * The function make_stream_pair returns two connected capy::any_stream: https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... * The function main (in any_stream.cpp) creates an asio_context, two connected capy::any_stream, and launches two coroutines which transfer data: https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... https://github.com/vinniefalco/capy/blob/e89bbdd39d3de00d257b91d4c0264276bd5... It was a dense read, and I think it is worth it; this is the big reveal if you haven't figured it out already: * This runs on Asio, yet no Asio includes are publicly visible. This example shows that it is both possible and easy to wrap asio's objects in a way that satisfies Capy's requirements, to build stream abstractions which do not leak types yet remain entirely functional. Would this be something upon which you can implement most of the requirements of PostgresSQL? The promptogorov complexity of these wrappers is so low that variations can be produced on demand by frontier models. Also note, that it is not required to use the type-erasing wrappers. You can express algorithms as templates if you like (asio style). For example capy::read_until is expressed thusly: https://github.com/cppalliance/capy/blob/18aa8a8b5fe8d0925e983907d057a1111c4... You can pass capy::any_stream here, or you could pass asio_socket if you want. Both work. Thanks