Type-Erased Wrappers
any_read_stream is a movable, type-erased wrapper for any type modeling ReadStream.
It can be owning or non-owning: you decide upon construction. It models ReadStream itself.
It offers you a possibility to write an algorithm that works with any read stream and that is not a function template:
capy::task<> algo1(capy::ReadStream auto& stream); (1)
capy::task<> algo2(capy::any_read_stream& stream); (2)
| 1 | Taking any read stream via a template argument. |
| 2 | Taking any read stream via a type-erased wrapper. |
The type-erased wrapper comes at a slight run-time cost resulting from indirect calls and a possible allocator use. In return, you get:
-
Not having to ship the implementation of your functions in header files.
-
Much improved compile times.
-
Being able to select the read stream implementation at runtime.
Note that this runtime cost is dwarfed by the cost of I/O itself.
The allocation strategy used by the wrapper is very efficient. There is only one
when the wrapper is created. The same allocation is reused when an awaitable
(also type-erased) is created by the call to read_some.
You decide whether the wrapper will exclusively own the stream, or if it will only be a reference, by selecting the appropriate constructor
MyReadStream s = makeMyStream();
capy::any_read_stream ws1(&s); (1)
capy::any_read_stream ws2(makeMyStream()); (2)
| 1 | Pointer constructor — You have to make sure the stream outlives the wrapper. |
| 2 | Rvalue reference constructor — The wrapper will manage the stream’s lifetime. |
There are analogous wrappers for WriteStream and Stream concepts.
This is shown in the table.
| Concept | Type-erased wrapper |
|---|---|