100.00% Lines (8/8) 100.00% Functions (3/3)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_IO_RESULT_HPP 10   #ifndef BOOST_CAPY_IO_RESULT_HPP
11   #define BOOST_CAPY_IO_RESULT_HPP 11   #define BOOST_CAPY_IO_RESULT_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <system_error> 14   #include <system_error>
15   15  
16   #include <cstddef> 16   #include <cstddef>
  17 + #include <tuple>
17   #include <type_traits> 18   #include <type_traits>
18   #include <utility> 19   #include <utility>
19   20  
20   namespace boost { 21   namespace boost {
21   namespace capy { 22   namespace capy {
22   23  
23   /** Result type for asynchronous I/O operations. 24   /** Result type for asynchronous I/O operations.
24   25  
25   This template provides a unified result type for async operations, 26   This template provides a unified result type for async operations,
26   always containing a `std::error_code` plus optional additional 27   always containing a `std::error_code` plus optional additional
27 - values. It supports structured bindings. 28 + values. It supports structured bindings via the tuple protocol.
28 -  
29 - @tparam Args Additional value types beyond the error code.  
30 -  
31 - @par Usage  
32 - @code  
33 - auto [ec, n] = co_await s.read_some(buf);  
34 - if (ec) { ... }  
35 - @endcode  
36 - */  
37 - template<class... Args>  
38 - struct io_result  
39 - {  
40 - static_assert("io_result only supports up to 3 template arguments");  
41 - };  
42 -  
43 - /** Result type for void operations.  
44 -  
45 - Used by operations like `connect()` that don't return a value  
46 - beyond success/failure. This specialization is not an aggregate  
47 - to enable implicit conversion from `error_code`.  
48 -  
49 - @par Example  
50 - @code  
51 - auto [ec] = co_await s.connect(ep);  
52 - if (ec) { ... }  
53 - @endcode  
54 - */  
55 - template<>  
56 - struct [[nodiscard]] io_result<>  
57 - {  
58 - /** The error code from the operation. */  
59 - std::error_code ec;  
60 -  
61 - #ifdef _MSC_VER  
62 - // Tuple protocol (unconditional - io_result<> is not an aggregate)  
63 - template<std::size_t I>  
64 - auto& get() & noexcept  
65 - {  
66 - static_assert(I == 0, "index out of range");  
67 - return ec;  
68 - }  
69 -  
70 - template<std::size_t I>  
71 - auto const& get() const& noexcept  
72 - {  
73 - static_assert(I == 0, "index out of range");  
74 - return ec;  
75 - }  
76 -  
77 - template<std::size_t I>  
78 - auto&& get() && noexcept  
79 - {  
80 - static_assert(I == 0, "index out of range");  
81 - return std::move(ec);  
82 - }  
83 - #endif  
84 - };  
85 -  
86 - /** Result type for byte transfer operations.  
87 -  
88 - Used by operations like `read_some()` and `write_some()` that  
89 - return the number of bytes transferred.  
90   29  
91   @par Example 30   @par Example
92   @code 31   @code
93   auto [ec, n] = co_await s.read_some(buf); 32   auto [ec, n] = co_await s.read_some(buf);
94   if (ec) { ... } 33   if (ec) { ... }
95 - */  
96 - template<typename T1>  
97 - struct [[nodiscard]] io_result<T1>  
98 - {  
99 - std::error_code ec;  
100 - T1 t1{};  
101 -  
102 - #ifdef _MSC_VER  
103 - template<std::size_t I>  
104 - auto& get() & noexcept  
105 - {  
106 - static_assert(I < 2, "index out of range");  
107 - if constexpr (I == 0) return ec;  
108 - else return t1;  
109 - }  
110   @endcode 34   @endcode
111   35  
112 - template<std::size_t I> 36 + @note Payload members are only meaningful when
113 - auto const& get() const& noexcept 37 + `ec` does not indicate an error.
114 - {  
115 - static_assert(I < 2, "index out of range");  
116 - if constexpr (I == 0) return ec;  
117 - else return t1;  
118 - }  
119 -  
120 - template<std::size_t I>  
121 - auto&& get() && noexcept  
122 - {  
123 - static_assert(I < 2, "index out of range");  
124 - if constexpr (I == 0) return std::move(ec);  
125 - else return std::move(t1);  
126 - }  
127 - #endif  
128 - };  
129   38  
130 - template<typename T1, typename T2> 39 + @tparam Ts Ordered payload types following the leading
131 - struct [[nodiscard]] io_result<T1, T2> 40 + `std::error_code`.
  41 + */
  42 + template<class... Ts>
  43 + struct [[nodiscard]] io_result
132   { 44   {
  45 + /// The error code from the operation.
133 - T1 t1{};  
134 - T2 t2{};  
135   std::error_code ec; 46   std::error_code ec;
136   47  
137 - #ifdef _MSC_VER 48 + /// The payload values. Unspecified when `ec` is set.
138 - template<std::size_t I> 49 + std::tuple<Ts...> values;
139 - auto& get() & noexcept  
140 - {  
141 - static_assert(I < 3, "index out of range");  
142 - if constexpr (I == 0) return ec;  
143 - else if constexpr (I == 1) return t1;  
144 - else return t2;  
145 - }  
146   50  
147 - template<std::size_t I> 51 + /// Construct a default io_result.
HITGIC 148 - auto const& get() const& noexcept 52 + 750 io_result() = default;
149 - {  
150 - static_assert(I < 3, "index out of range");  
151 - if constexpr (I == 0) return ec;  
152 - else if constexpr (I == 1) return t1;  
153 - else return t2;  
154 - }  
155   53  
156 - template<std::size_t I> 54 + /// Construct from an error code and payload values.
HITGIC 157 - auto&& get() && noexcept 55 + 5879 io_result(std::error_code ec_, Ts... ts)
HITGNC   56 + 5879 : ec(ec_)
HITGNC   57 + 5542 , values(std::move(ts)...)
158 - static_assert(I < 3, "index out of range");  
159 - if constexpr (I == 0) return std::move(ec);  
160 - else if constexpr (I == 1) return std::move(t1);  
161 - else return std::move(t2);  
162   { 58   {
163 - #endif  
164 - };  
165 -  
166 - template<typename T1, typename T2, typename T3>  
167 - struct [[nodiscard]] io_result<T1, T2, T3>  
168 - {  
169 - std::error_code ec;  
170 - T1 t1{};  
171 - T2 t2{};  
172 - T3 t3{};  
HITGIC 173   } 59   5879 }
174   60  
175 - #ifdef _MSC_VER 61 + /// @cond
176   template<std::size_t I> 62   template<std::size_t I>
177 - auto& get() & noexcept 63 + decltype(auto) get() & noexcept
178   { 64   {
179 - static_assert(I < 4, "index out of range"); 65 + static_assert(I < 1 + sizeof...(Ts), "index out of range");
180 - if constexpr (I == 0) return ec; 66 + if constexpr (I == 0) return (ec);
181 - else if constexpr (I == 1) return t1; 67 + else return std::get<I - 1>(values);
182 - else if constexpr (I == 2) return t2;  
183 - else return t3;  
184   } 68   }
185   69  
186   template<std::size_t I> 70   template<std::size_t I>
187 - auto const& get() const& noexcept 71 + decltype(auto) get() const& noexcept
188   { 72   {
189 - static_assert(I < 4, "index out of range"); 73 + static_assert(I < 1 + sizeof...(Ts), "index out of range");
190 - if constexpr (I == 0) return ec; 74 + if constexpr (I == 0) return (ec);
191 - else if constexpr (I == 1) return t1; 75 + else return std::get<I - 1>(values);
192 - else if constexpr (I == 2) return t2;  
193 - else return t3;  
194   } 76   }
195   77  
196   template<std::size_t I> 78   template<std::size_t I>
HITGIC 197 - auto&& get() && noexcept 79 + 11511 decltype(auto) get() && noexcept
198   { 80   {
199 - static_assert(I < 4, "index out of range"); 81 + static_assert(I < 1 + sizeof...(Ts), "index out of range");
HITGIC 200   if constexpr (I == 0) return std::move(ec); 82   6208 if constexpr (I == 0) return std::move(ec);
HITGIC 201 - else if constexpr (I == 1) return std::move(t1); 83 + 5303 else return std::get<I - 1>(std::move(values));
202 - else if constexpr (I == 2) return std::move(t2);  
203 - else return std::move(t3);  
204   } 84   }
205 - #endif 85 + /// @endcond
206   }; 86   };
207   87  
208 - //------------------------------------------------------------------------------ 88 + /// @cond
209 - 89 + template<std::size_t I, class... Ts>
210 - #ifdef _MSC_VER 90 + decltype(auto) get(io_result<Ts...>& r) noexcept
211 -  
212 - // Free-standing get() overloads for ADL (MSVC workaround for aggregates)  
213 -  
214 - template<std::size_t I>  
215 - auto& get(io_result<>& r) noexcept  
216 - {  
217 - return r.template get<I>();  
218 - }  
219 -  
220 - template<std::size_t I>  
221 - auto const& get(io_result<> const& r) noexcept  
222 - {  
223 - return r.template get<I>();  
224 - }  
225 -  
226 - template<std::size_t I>  
227 - auto&& get(io_result<>&& r) noexcept  
228 - {  
229 - return std::move(r).template get<I>();  
230 - }  
231 -  
232 - template<std::size_t I, typename T1>  
233 - auto& get(io_result<T1>& r) noexcept  
234 - {  
235 - return r.template get<I>();  
236 - }  
237 -  
238 - template<std::size_t I, typename T1>  
239 - auto const& get(io_result<T1> const& r) noexcept  
240 - {  
241 - return r.template get<I>();  
242 - }  
243 -  
244 - template<std::size_t I, typename T1>  
245 - auto&& get(io_result<T1>&& r) noexcept  
246 - {  
247 - return std::move(r).template get<I>();  
248 - }  
249 -  
250 - template<std::size_t I, typename T1, typename T2>  
251 - auto& get(io_result<T1, T2>& r) noexcept  
252 - {  
253 - return r.template get<I>();  
254 - }  
255 -  
256 - template<std::size_t I, typename T1, typename T2>  
257 - auto const& get(io_result<T1, T2> const& r) noexcept  
258 - {  
259 - return r.template get<I>();  
260 - }  
261 -  
262 - template<std::size_t I, typename T1, typename T2>  
263 - auto&& get(io_result<T1, T2>&& r) noexcept  
264 - {  
265 - return std::move(r).template get<I>();  
266 - }  
267 -  
268 - template<std::size_t I, typename T1, typename T2, typename T3>  
269 - auto& get(io_result<T1, T2, T3>& r) noexcept  
270   { 91   {
271   return r.template get<I>(); 92   return r.template get<I>();
272   } 93   }
273   94  
274 - template<std::size_t I, typename T1, typename T2, typename T3> 95 + template<std::size_t I, class... Ts>
275 - auto const& get(io_result<T1, T2, T3> const& r) noexcept 96 + decltype(auto) get(io_result<Ts...> const& r) noexcept
276   { 97   {
277   return r.template get<I>(); 98   return r.template get<I>();
278   } 99   }
279   100  
280 - template<std::size_t I, typename T1, typename T2, typename T3> 101 + template<std::size_t I, class... Ts>
281 - auto&& get(io_result<T1, T2, T3>&& r) noexcept 102 + decltype(auto) get(io_result<Ts...>&& r) noexcept
282   { 103   {
283   return std::move(r).template get<I>(); 104   return std::move(r).template get<I>();
284   } 105   }
285 - 106 + /// @endcond
286 - #endif // _MSC_VER  
287   107  
288   } // namespace capy 108   } // namespace capy
289   } // namespace boost 109   } // namespace boost
290   110  
291 - //------------------------------------------------------------------------------ 111 + // Tuple protocol for structured bindings
292 -  
293 - #ifdef _MSC_VER  
294 -  
295 - // Tuple protocol for structured bindings (MSVC workaround)  
296 - // MSVC has a bug with aggregate decomposition in coroutines, so we use  
297 - // tuple protocol instead which forces the compiler to use get<>() functions.  
298 -  
299   namespace std { 112   namespace std {
300   113  
301 - template<> 114 + template<class... Ts>
302 - struct tuple_size<boost::capy::io_result<>> 115 + struct tuple_size<boost::capy::io_result<Ts...>>
303 - : std::integral_constant<std::size_t, 1> {}; 116 + : std::integral_constant<std::size_t, 1 + sizeof...(Ts)> {};
304 -  
305 - template<>  
306 - struct tuple_element<0, boost::capy::io_result<>>  
307 - {  
308 - using type = ::std::error_code;  
309 - };  
310 -  
311 - template<typename T1>  
312 - struct tuple_size<boost::capy::io_result<T1>>  
313 - : std::integral_constant<std::size_t, 2> {};  
314 -  
315 - template<typename T1, typename T2>  
316 - struct tuple_size<boost::capy::io_result<T1, T2>>  
317 - : std::integral_constant<std::size_t, 3> {};  
318 -  
319 - template<typename T1, typename T2, typename T3>  
320 - struct tuple_size<boost::capy::io_result<T1, T2, T3>>  
321 - : std::integral_constant<std::size_t, 4> {};  
322 -  
323 - // tuple_element specializations for io_result<T1>  
324 -  
325 - template<>  
326 - struct tuple_element<0, boost::capy::io_result<std::size_t>>  
327 - {  
328 - using type = ::std::error_code;  
329 - };  
330 -  
331 - template<>  
332 - struct tuple_element<1, boost::capy::io_result<std::size_t>>  
333 - {  
334 - using type = std::size_t;  
335 - };  
336 -  
337 - template<typename T1>  
338 - struct tuple_element<0, boost::capy::io_result<T1>>  
339 - {  
340 - using type = ::std::error_code;  
341 - };  
342 -  
343 - template<typename T1>  
344 - struct tuple_element<1, boost::capy::io_result<T1>>  
345 - {  
346 - using type = T1;  
347 - };  
348 -  
349 - // tuple_element specializations for io_result<T1, T2>  
350 -  
351 - template<typename T1, typename T2>  
352 - struct tuple_element<0, boost::capy::io_result<T1, T2>>  
353 - {  
354 - using type = ::std::error_code;  
355 - };  
356 -  
357 - template<typename T1, typename T2>  
358 - struct tuple_element<1, boost::capy::io_result<T1, T2>>  
359 - {  
360 - using type = T1;  
361 - };  
362 -  
363 - template<typename T1, typename T2>  
364 - struct tuple_element<2, boost::capy::io_result<T1, T2>>  
365 - {  
366 - using type = T2;  
367 - };  
368 -  
369 - // tuple_element specializations for io_result<T1, T2, T3>  
370 -  
371 - template<typename T1, typename T2, typename T3>  
372 - struct tuple_element<0, boost::capy::io_result<T1, T2, T3>>  
373 - {  
374 - using type = ::std::error_code;  
375 - };  
376 -  
377 - template<typename T1, typename T2, typename T3>  
378 - struct tuple_element<1, boost::capy::io_result<T1, T2, T3>>  
379 - {  
380 - using type = T1;  
381 - };  
382   117  
383 - template<typename T1, typename T2, typename T3> 118 + template<class... Ts>
384 - struct tuple_element<2, boost::capy::io_result<T1, T2, T3>> 119 + struct tuple_element<0, boost::capy::io_result<Ts...>>
385   { 120   {
386 - using type = T2; 121 + using type = std::error_code;
387   }; 122   };
388   123  
389 - template<typename T1, typename T2, typename T3> 124 + template<std::size_t I, class... Ts>
390 - struct tuple_element<3, boost::capy::io_result<T1, T2, T3>> 125 + struct tuple_element<I, boost::capy::io_result<Ts...>>
391   { 126   {
392 - using type = T3; 127 + using type = std::tuple_element_t<I - 1, std::tuple<Ts...>>;
393   }; 128   };
394   129  
395 -  
396 - #endif // _MSC_VER  
397   } // namespace std 130   } // namespace std
398   131  
399   #endif // BOOST_CAPY_IO_RESULT_HPP 132   #endif // BOOST_CAPY_IO_RESULT_HPP