include/boost/capy/io/any_stream.hpp

100.0% Lines (46/3/49) 77.8% List of functions (14/0/18)
any_stream.hpp
f(x) Functions (18)
Function Calls Lines Blocks
boost::capy::any_stream::~any_stream() :74 39x 100.0% 100.0% boost::capy::any_stream::any_stream(boost::capy::any_stream&&) :115 1x 100.0% 100.0% boost::capy::any_stream::operator=(boost::capy::any_stream&&) :133 2x 100.0% 100.0% boost::capy::any_stream::any_stream<(anonymous namespace)::ex_1::instant_stream>((anonymous namespace)::ex_1::instant_stream) :164 0 0.0% 0.0% boost::capy::any_stream::any_stream<boost::capy::(anonymous namespace)::mock_stream>(boost::capy::(anonymous namespace)::mock_stream) :164 3x 100.0% 67.0% boost::capy::any_stream::any_stream<boost::capy::test::stream>(boost::capy::test::stream) :164 1x 100.0% 67.0% boost::capy::any_stream::any_stream<(anonymous namespace)::ex_1::instant_stream>((anonymous namespace)::ex_1::instant_stream)::guard::~guard() :170 0 0.0% 0.0% boost::capy::any_stream::any_stream<boost::capy::(anonymous namespace)::mock_stream>(boost::capy::(anonymous namespace)::mock_stream)::guard::~guard() :170 3x 100.0% 33.0% boost::capy::any_stream::any_stream<boost::capy::test::stream>(boost::capy::test::stream)::guard::~guard() :170 1x 100.0% 33.0% boost::capy::any_stream::any_stream<(anonymous namespace)::ex_1::instant_stream>((anonymous namespace)::ex_1::instant_stream)::{lambda(void*)#1}::operator()(void*) const :183 0 0.0% 0.0% boost::capy::any_stream::any_stream<boost::capy::(anonymous namespace)::mock_stream>(boost::capy::(anonymous namespace)::mock_stream)::{lambda(void*)#1}::operator()(void*) const :183 3x 100.0% 100.0% boost::capy::any_stream::any_stream<boost::capy::test::stream>(boost::capy::test::stream)::{lambda(void*)#1}::operator()(void*) const :183 1x 100.0% 100.0% boost::capy::any_stream::any_stream<(anonymous namespace)::ex_1::instant_stream>((anonymous namespace)::ex_1::instant_stream*) :202 0 0.0% 0.0% boost::capy::any_stream::any_stream<(anonymous namespace)::tcp::socket>((anonymous namespace)::tcp::socket*) :202 3x 100.0% 80.0% boost::capy::any_stream::any_stream<boost::capy::(anonymous namespace)::mock_stream>(boost::capy::(anonymous namespace)::mock_stream*) :202 22x 100.0% 80.0% boost::capy::any_stream::any_stream<boost::capy::test::stream>(boost::capy::test::stream*) :202 7x 100.0% 80.0% boost::capy::any_stream::has_value() const :217 12x 100.0% 100.0% boost::capy::any_stream::operator bool() const :231 2x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/capy
9 //
10
11 #ifndef BOOST_CAPY_IO_ANY_STREAM_HPP
12 #define BOOST_CAPY_IO_ANY_STREAM_HPP
13
14 #include <boost/capy/detail/config.hpp>
15 #include <boost/capy/concept/read_stream.hpp>
16 #include <boost/capy/concept/write_stream.hpp>
17 #include <boost/capy/io/any_read_stream.hpp>
18 #include <boost/capy/io/any_write_stream.hpp>
19
20 #include <concepts>
21
22 namespace boost {
23 namespace capy {
24
25 /** Dispatches `read_some` and `write_some` through independent type-erased vtables.
26
27 This class provides type erasure for any type satisfying both
28 the @ref ReadStream and @ref WriteStream concepts, enabling
29 runtime polymorphism for bidirectional I/O operations.
30
31 Inherits from both @ref any_read_stream and @ref any_write_stream,
32 providing `read_some` and `write_some` operations. Each base
33 maintains its own cached awaitable storage, allowing concurrent
34 read and write operations.
35
36 The wrapper supports two construction modes:
37 - **Owning**: Pass by value to transfer ownership. The wrapper
38 allocates storage and owns the stream.
39 - **Reference**: Pass a pointer to wrap without ownership. The
40 pointed-to stream must outlive this wrapper.
41
42 @par Implicit Conversion
43 This class implicitly converts to `any_read_stream&` or
44 `any_write_stream&`, allowing it to be passed to functions
45 that accept only one capability. However, do not move through
46 a base reference as this would leave the other base in an
47 invalid state.
48
49 @par Thread Safety
50 Not thread-safe. Concurrent operations of the same type
51 (two reads or two writes) are undefined behavior. One read
52 and one write may be in flight simultaneously.
53
54 @par Example
55 @par !example example
56
57
58 @see any_read_stream, any_write_stream, ReadStream, WriteStream
59 */
60 class any_stream
61 : public any_read_stream
62 , public any_write_stream
63 {
64 void* storage_ = nullptr;
65 void* stream_ptr_ = nullptr;
66 void (*destroy_)(void*) noexcept = nullptr;
67
68 public:
69 /** Destructor.
70
71 Destroys the owned stream (if any). Base class destructors
72 handle their cached awaitable storage.
73 */
74 39x ~any_stream()
75 {
76 39x if(storage_)
77 {
78 3x destroy_(stream_ptr_);
79 3x ::operator delete(storage_);
80 }
81 39x }
82
83 /** Construct a default instance.
84
85 Constructs an empty wrapper. @ref has_value and `operator bool`
86 report the empty state; calling `read_some` or `write_some`
87 before the wrapper holds a stream is undefined behavior.
88 */
89 any_stream() = default;
90
91 /** Non-copyable.
92
93 The awaitable caches are per-instance and cannot be shared.
94
95 @param other The wrapper that would be copied.
96 */
97 any_stream(any_stream const& other) = delete;
98
99 /** Copy assignment is disabled.
100
101 The awaitable caches are per-instance and cannot be shared.
102
103 @param other The wrapper that would be assigned from.
104
105 @return A reference to `*this`.
106 */
107 any_stream& operator=(any_stream const& other) = delete;
108
109 /** Construct by moving.
110
111 Transfers ownership from both bases and the owned stream (if any).
112
113 @param other The wrapper to move from.
114 */
115 1x any_stream(any_stream&& other) noexcept
116 1x : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
117 1x , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
118 1x , storage_(std::exchange(other.storage_, nullptr))
119 1x , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
120 2x , destroy_(std::exchange(other.destroy_, nullptr))
121 {
122 1x }
123
124 /** Assign by moving.
125
126 Destroys any owned stream and releases existing resources,
127 then transfers ownership from `other`.
128
129 @param other The wrapper to move from.
130 @return Reference to this wrapper.
131 */
132 any_stream&
133 2x operator=(any_stream&& other) noexcept
134 {
135 2x if(this != &other)
136 {
137 2x if(storage_)
138 {
139 1x destroy_(stream_ptr_);
140 1x ::operator delete(storage_);
141 }
142 static_cast<any_read_stream&>(*this) =
143 2x std::move(static_cast<any_read_stream&>(other));
144 static_cast<any_write_stream&>(*this) =
145 2x std::move(static_cast<any_write_stream&>(other));
146 2x storage_ = std::exchange(other.storage_, nullptr);
147 2x stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
148 2x destroy_ = std::exchange(other.destroy_, nullptr);
149 }
150 2x return *this;
151 }
152
153 /** Construct by taking ownership of a bidirectional stream.
154
155 Allocates storage and moves the stream into this wrapper.
156 The wrapper owns the stream and destroys it.
157
158 @param s The stream to take ownership of. Must satisfy both
159 ReadStream and WriteStream concepts.
160 */
161 template<class S>
162 requires ReadStream<S> && WriteStream<S> &&
163 (!std::same_as<std::decay_t<S>, any_stream>)
164 4x any_stream(S s)
165 4x {
166 struct guard {
167 any_stream* self;
168 void* ptr = nullptr;
169 bool committed = false;
170 4x ~guard() {
171 4x if(!committed && ptr) {
172 static_cast<S*>(ptr)->~S(); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
173 ::operator delete(self->storage_); // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
174 self->storage_ = nullptr; // LCOV_EXCL_LINE OOM rollback: only when the cached-awaitable allocation throws
175 }
176 4x }
177 4x } g{this};
178
179 4x storage_ = ::operator new(sizeof(S));
180 4x S* ptr = ::new(storage_) S(std::move(s));
181 4x g.ptr = ptr;
182 4x stream_ptr_ = ptr;
183 8x destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); };
184
185 // Initialize bases with pointer (reference semantics)
186 4x static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
187 4x static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
188
189 4x g.committed = true;
190 4x }
191
192 /** Construct by wrapping a bidirectional stream without ownership.
193
194 Wraps the given stream by pointer. The stream must remain
195 valid for the lifetime of this wrapper.
196
197 @param s Pointer to the stream to wrap. Must satisfy both
198 ReadStream and WriteStream concepts.
199 */
200 template<class S>
201 requires ReadStream<S> && WriteStream<S>
202 32x any_stream(S* s)
203 : any_read_stream(s)
204 32x , any_write_stream(s)
205 {
206 // storage_ remains nullptr - no ownership
207 32x }
208
209 /** Check if the wrapper contains a valid stream.
210
211 Both bases must be valid for the wrapper to be valid.
212
213 @return `true` if wrapping a stream, `false` if default-constructed
214 or moved-from.
215 */
216 bool
217 12x has_value() const noexcept
218 {
219 19x return any_read_stream::has_value() &&
220 19x any_write_stream::has_value();
221 }
222
223 /** Check if the wrapper contains a valid stream.
224
225 Both bases must be valid for the wrapper to be valid.
226
227 @return `true` if wrapping a stream, `false` if default-constructed
228 or moved-from.
229 */
230 explicit
231 2x operator bool() const noexcept
232 {
233 2x return has_value();
234 }
235 };
236
237 } // namespace capy
238 } // namespace boost
239
240 #endif
241