Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_EX_IMMEDIATE_HPP
11 : #define BOOST_CAPY_EX_IMMEDIATE_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/executor_ref.hpp>
15 : #include <boost/capy/io_result.hpp>
16 :
17 : #include <coroutine>
18 : #include <stop_token>
19 : #include <utility>
20 :
21 : namespace boost {
22 : namespace capy {
23 :
24 : /** An awaitable that completes immediately with a value.
25 :
26 : This awaitable wraps a synchronous result so it can be used in
27 : contexts that require an awaitable type. It never suspends -
28 : `await_ready()` always returns `true`, so the coroutine machinery
29 : is optimized away by the compiler.
30 :
31 : Use this to adapt synchronous operations to satisfy async concepts
32 : like @ref IoAwaitable without the overhead of a full coroutine frame.
33 :
34 : @tparam T The result type to wrap.
35 :
36 : @par Example
37 : @code
38 : // Wrap a sync operation as an awaitable
39 : immediate<int> get_value()
40 : {
41 : return {42};
42 : }
43 :
44 : task<void> example()
45 : {
46 : int x = co_await get_value(); // No suspension, returns 42
47 : }
48 : @endcode
49 :
50 : @par Satisfying WriteSink with sync operations
51 : @code
52 : struct my_sync_sink
53 : {
54 : template<ConstBufferSequence CB>
55 : immediate<io_result<std::size_t>>
56 : write(CB buffers)
57 : {
58 : auto n = process_sync(buffers);
59 : return {{{}, n}};
60 : }
61 :
62 : immediate<io_result<>>
63 : write_eof()
64 : {
65 : return {{}};
66 : }
67 : };
68 : @endcode
69 :
70 : @see ready, io_result
71 : */
72 : template<class T>
73 : struct immediate
74 : {
75 : /** The wrapped value. */
76 : T value_;
77 :
78 : /** Always returns true - this awaitable never suspends. */
79 : constexpr bool
80 21 : await_ready() const noexcept
81 : {
82 21 : return true;
83 : }
84 :
85 : /** Never called since await_ready() returns true. */
86 : constexpr void
87 : await_suspend(std::coroutine_handle<>) const noexcept
88 : {
89 : }
90 :
91 : /** IoAwaitable protocol overload.
92 :
93 : This overload allows `immediate` to satisfy the @ref IoAwaitable
94 : concept. Since the result is already available, the executor and
95 : stop token are unused.
96 :
97 : @param h The coroutine handle (unused).
98 : @param ex The executor (unused).
99 : @param token The stop token (unused).
100 :
101 : @return `std::noop_coroutine()` to indicate no suspension.
102 : */
103 : std::coroutine_handle<>
104 1 : await_suspend(
105 : std::coroutine_handle<> h,
106 : executor_ref ex,
107 : std::stop_token token = {}) const noexcept
108 : {
109 : (void)h;
110 : (void)ex;
111 : (void)token;
112 1 : return std::noop_coroutine();
113 : }
114 :
115 : /** Returns the wrapped value.
116 :
117 : @return The stored value, moved if non-const.
118 : */
119 : constexpr T
120 24 : await_resume() noexcept
121 : {
122 24 : return std::move(value_);
123 : }
124 :
125 : /** Returns the wrapped value (const overload). */
126 : constexpr T const&
127 : await_resume() const noexcept
128 : {
129 : return value_;
130 : }
131 : };
132 :
133 : //----------------------------------------------------------
134 :
135 : /** Create an immediate awaitable for a successful io_result.
136 :
137 : This helper creates an @ref immediate wrapping an @ref io_result
138 : with no error and the provided values.
139 :
140 : @par Example
141 : @code
142 : immediate<io_result<std::size_t>>
143 : write(const_buffer buf)
144 : {
145 : auto n = write_sync(buf);
146 : return ready(n); // success with n bytes
147 : }
148 :
149 : immediate<io_result<>>
150 : connect()
151 : {
152 : connect_sync();
153 : return ready(); // void success
154 : }
155 : @endcode
156 :
157 : @return An immediate awaitable containing a successful io_result.
158 :
159 : @see immediate, io_result
160 : */
161 : inline
162 : immediate<io_result<>>
163 3 : ready() noexcept
164 : {
165 3 : return {{}};
166 : }
167 :
168 : /** Create an immediate awaitable for a successful io_result with one value.
169 :
170 : @param t1 The result value.
171 :
172 : @return An immediate awaitable containing `io_result<T1>{{}, t1}`.
173 : */
174 : template<class T1>
175 : immediate<io_result<T1>>
176 4 : ready(T1 t1)
177 : {
178 4 : return {{{}, std::move(t1)}};
179 : }
180 :
181 : /** Create an immediate awaitable for a successful io_result with two values.
182 :
183 : @param t1 The first result value.
184 : @param t2 The second result value.
185 :
186 : @return An immediate awaitable containing `io_result<T1,T2>{{}, t1, t2}`.
187 : */
188 : template<class T1, class T2>
189 : immediate<io_result<T1, T2>>
190 2 : ready(T1 t1, T2 t2)
191 : {
192 2 : return {{{}, std::move(t1), std::move(t2)}};
193 : }
194 :
195 : /** Create an immediate awaitable for a successful io_result with three values.
196 :
197 : @param t1 The first result value.
198 : @param t2 The second result value.
199 : @param t3 The third result value.
200 :
201 : @return An immediate awaitable containing `io_result<T1,T2,T3>{{}, t1, t2, t3}`.
202 : */
203 : template<class T1, class T2, class T3>
204 : immediate<io_result<T1, T2, T3>>
205 2 : ready(T1 t1, T2 t2, T3 t3)
206 : {
207 2 : return {{{}, std::move(t1), std::move(t2), std::move(t3)}};
208 : }
209 :
210 : //----------------------------------------------------------
211 :
212 : /** Create an immediate awaitable for a failed io_result.
213 :
214 : This helper creates an @ref immediate wrapping an @ref io_result
215 : with an error code.
216 :
217 : @par Example
218 : @code
219 : immediate<io_result<std::size_t>>
220 : write(const_buffer buf)
221 : {
222 : auto ec = write_sync(buf);
223 : if(ec)
224 : return ready(ec, std::size_t{0});
225 : return ready(buffer_size(buf));
226 : }
227 : @endcode
228 :
229 : @param ec The error code.
230 :
231 : @return An immediate awaitable containing a failed io_result.
232 :
233 : @see immediate, io_result
234 : */
235 : inline
236 : immediate<io_result<>>
237 1 : ready(std::error_code ec) noexcept
238 : {
239 1 : return {{ec}};
240 : }
241 :
242 : /** Create an immediate awaitable for an io_result with error and one value.
243 :
244 : @param ec The error code.
245 : @param t1 The result value.
246 :
247 : @return An immediate awaitable containing `io_result<T1>{ec, t1}`.
248 : */
249 : template<class T1>
250 : immediate<io_result<T1>>
251 2 : ready(std::error_code ec, T1 t1)
252 : {
253 2 : return {{ec, std::move(t1)}};
254 : }
255 :
256 : /** Create an immediate awaitable for an io_result with error and two values.
257 :
258 : @param ec The error code.
259 : @param t1 The first result value.
260 : @param t2 The second result value.
261 :
262 : @return An immediate awaitable containing `io_result<T1,T2>{ec, t1, t2}`.
263 : */
264 : template<class T1, class T2>
265 : immediate<io_result<T1, T2>>
266 1 : ready(std::error_code ec, T1 t1, T2 t2)
267 : {
268 1 : return {{ec, std::move(t1), std::move(t2)}};
269 : }
270 :
271 : /** Create an immediate awaitable for an io_result with error and three values.
272 :
273 : @param ec The error code.
274 : @param t1 The first result value.
275 : @param t2 The second result value.
276 : @param t3 The third result value.
277 :
278 : @return An immediate awaitable containing `io_result<T1,T2,T3>{ec, t1, t2, t3}`.
279 : */
280 : template<class T1, class T2, class T3>
281 : immediate<io_result<T1, T2, T3>>
282 1 : ready(std::error_code ec, T1 t1, T2 t2, T3 t3)
283 : {
284 1 : return {{ec, std::move(t1), std::move(t2), std::move(t3)}};
285 : }
286 :
287 : } // namespace capy
288 : } // namespace boost
289 :
290 : #endif
|