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_EX_IO_AWAITABLE_PROMISE_BASE_HPP
10  
#ifndef BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP
11  
#define BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP
11  
#define BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/ex/frame_allocator.hpp>
14  
#include <boost/capy/ex/frame_allocator.hpp>
15  
#include <boost/capy/ex/io_env.hpp>
15  
#include <boost/capy/ex/io_env.hpp>
16  
#include <boost/capy/ex/recycling_memory_resource.hpp>
16  
#include <boost/capy/ex/recycling_memory_resource.hpp>
17  
#include <boost/capy/ex/this_coro.hpp>
17  
#include <boost/capy/ex/this_coro.hpp>
18  

18  

19  
#include <coroutine>
19  
#include <coroutine>
20  
#include <cstddef>
20  
#include <cstddef>
21  
#include <cstring>
21  
#include <cstring>
22  
#include <memory_resource>
22  
#include <memory_resource>
23  
#include <stop_token>
23  
#include <stop_token>
24  
#include <type_traits>
24  
#include <type_traits>
25  

25  

26  
namespace boost {
26  
namespace boost {
27  
namespace capy {
27  
namespace capy {
28  

28  

29  
/** CRTP mixin that adds I/O awaitable support to a promise type.
29  
/** CRTP mixin that adds I/O awaitable support to a promise type.
30  

30  

31  
    Inherit from this class to enable these capabilities in your coroutine:
31  
    Inherit from this class to enable these capabilities in your coroutine:
32  

32  

33  
    1. **Frame allocation** — The mixin provides `operator new/delete` that
33  
    1. **Frame allocation** — The mixin provides `operator new/delete` that
34  
       use the thread-local frame allocator set by `run_async`.
34  
       use the thread-local frame allocator set by `run_async`.
35  

35  

36  
    2. **Environment storage** — The mixin stores a pointer to the `io_env`
36  
    2. **Environment storage** — The mixin stores a pointer to the `io_env`
37  
       containing the executor, stop token, and allocator for this coroutine.
37  
       containing the executor, stop token, and allocator for this coroutine.
38  

38  

39  
    3. **Environment access** — Coroutine code can retrieve the environment
39  
    3. **Environment access** — Coroutine code can retrieve the environment
40  
       via `co_await this_coro::environment`, or individual fields via
40  
       via `co_await this_coro::environment`, or individual fields via
41  
       `co_await this_coro::executor`, `co_await this_coro::stop_token`,
41  
       `co_await this_coro::executor`, `co_await this_coro::stop_token`,
42  
       and `co_await this_coro::allocator`.
42  
       and `co_await this_coro::allocator`.
43  

43  

44  
    @tparam Derived The derived promise type (CRTP pattern).
44  
    @tparam Derived The derived promise type (CRTP pattern).
45  

45  

46  
    @par Basic Usage
46  
    @par Basic Usage
47  

47  

48  
    For coroutines that need to access their execution environment:
48  
    For coroutines that need to access their execution environment:
49  

49  

50  
    @code
50  
    @code
51  
    struct my_task
51  
    struct my_task
52  
    {
52  
    {
53  
        struct promise_type : io_awaitable_promise_base<promise_type>
53  
        struct promise_type : io_awaitable_promise_base<promise_type>
54  
        {
54  
        {
55  
            my_task get_return_object();
55  
            my_task get_return_object();
56  
            std::suspend_always initial_suspend() noexcept;
56  
            std::suspend_always initial_suspend() noexcept;
57  
            std::suspend_always final_suspend() noexcept;
57  
            std::suspend_always final_suspend() noexcept;
58  
            void return_void();
58  
            void return_void();
59  
            void unhandled_exception();
59  
            void unhandled_exception();
60  
        };
60  
        };
61  

61  

62  
        // ... awaitable interface ...
62  
        // ... awaitable interface ...
63  
    };
63  
    };
64  

64  

65  
    my_task example()
65  
    my_task example()
66  
    {
66  
    {
67  
        auto env = co_await this_coro::environment;
67  
        auto env = co_await this_coro::environment;
68  
        // Access env->executor, env->stop_token, env->allocator
68  
        // Access env->executor, env->stop_token, env->allocator
69  

69  

70  
        // Or use fine-grained accessors:
70  
        // Or use fine-grained accessors:
71  
        auto ex = co_await this_coro::executor;
71  
        auto ex = co_await this_coro::executor;
72  
        auto token = co_await this_coro::stop_token;
72  
        auto token = co_await this_coro::stop_token;
73  
        auto* alloc = co_await this_coro::allocator;
73  
        auto* alloc = co_await this_coro::allocator;
74  
    }
74  
    }
75  
    @endcode
75  
    @endcode
76  

76  

77  
    @par Custom Awaitable Transformation
77  
    @par Custom Awaitable Transformation
78  

78  

79  
    If your promise needs to transform awaitables (e.g., for affinity or
79  
    If your promise needs to transform awaitables (e.g., for affinity or
80  
    logging), override `transform_awaitable` instead of `await_transform`:
80  
    logging), override `transform_awaitable` instead of `await_transform`:
81  

81  

82  
    @code
82  
    @code
83  
    struct promise_type : io_awaitable_promise_base<promise_type>
83  
    struct promise_type : io_awaitable_promise_base<promise_type>
84  
    {
84  
    {
85  
        template<typename A>
85  
        template<typename A>
86  
        auto transform_awaitable(A&& a)
86  
        auto transform_awaitable(A&& a)
87  
        {
87  
        {
88  
            // Your custom transformation logic
88  
            // Your custom transformation logic
89  
            return std::forward<A>(a);
89  
            return std::forward<A>(a);
90  
        }
90  
        }
91  
    };
91  
    };
92  
    @endcode
92  
    @endcode
93  

93  

94  
    The mixin's `await_transform` intercepts @ref this_coro::environment_tag
94  
    The mixin's `await_transform` intercepts @ref this_coro::environment_tag
95  
    and the fine-grained tag types (@ref this_coro::executor_tag,
95  
    and the fine-grained tag types (@ref this_coro::executor_tag,
96  
    @ref this_coro::stop_token_tag, @ref this_coro::allocator_tag),
96  
    @ref this_coro::stop_token_tag, @ref this_coro::allocator_tag),
97  
    then delegates all other awaitables to your `transform_awaitable`.
97  
    then delegates all other awaitables to your `transform_awaitable`.
98  

98  

99  
    @par Making Your Coroutine an IoAwaitable
99  
    @par Making Your Coroutine an IoAwaitable
100  

100  

101  
    The mixin handles the "inside the coroutine" part—accessing the
101  
    The mixin handles the "inside the coroutine" part—accessing the
102  
    environment. To receive the environment when your coroutine is awaited
102  
    environment. To receive the environment when your coroutine is awaited
103  
    (satisfying @ref IoAwaitable), implement the `await_suspend` overload
103  
    (satisfying @ref IoAwaitable), implement the `await_suspend` overload
104  
    on your coroutine return type:
104  
    on your coroutine return type:
105  

105  

106  
    @code
106  
    @code
107  
    struct my_task
107  
    struct my_task
108  
    {
108  
    {
109  
        struct promise_type : io_awaitable_promise_base<promise_type> { ... };
109  
        struct promise_type : io_awaitable_promise_base<promise_type> { ... };
110  

110  

111  
        std::coroutine_handle<promise_type> h_;
111  
        std::coroutine_handle<promise_type> h_;
112  

112  

113  
        // IoAwaitable await_suspend receives and stores the environment
113  
        // IoAwaitable await_suspend receives and stores the environment
114  
        std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
114  
        std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
115  
        {
115  
        {
116  
            h_.promise().set_environment(env);
116  
            h_.promise().set_environment(env);
117  
            // ... rest of suspend logic ...
117  
            // ... rest of suspend logic ...
118  
        }
118  
        }
119  
    };
119  
    };
120  
    @endcode
120  
    @endcode
121  

121  

122  
    @par Thread Safety
122  
    @par Thread Safety
123  
    The environment is stored during `await_suspend` and read during
123  
    The environment is stored during `await_suspend` and read during
124  
    `co_await this_coro::environment`. These occur on the same logical
124  
    `co_await this_coro::environment`. These occur on the same logical
125  
    thread of execution, so no synchronization is required.
125  
    thread of execution, so no synchronization is required.
126  

126  

127  
    @see this_coro::environment, this_coro::executor,
127  
    @see this_coro::environment, this_coro::executor,
128  
         this_coro::stop_token, this_coro::allocator
128  
         this_coro::stop_token, this_coro::allocator
129  
    @see io_env
129  
    @see io_env
130  
    @see IoAwaitable
130  
    @see IoAwaitable
131  
*/
131  
*/
132  
template<typename Derived>
132  
template<typename Derived>
133  
class io_awaitable_promise_base
133  
class io_awaitable_promise_base
134  
{
134  
{
135  
    io_env const* env_ = nullptr;
135  
    io_env const* env_ = nullptr;
136  
    mutable std::coroutine_handle<> cont_{std::noop_coroutine()};
136  
    mutable std::coroutine_handle<> cont_{std::noop_coroutine()};
137  

137  

138  
public:
138  
public:
139  
    /** Allocate a coroutine frame.
139  
    /** Allocate a coroutine frame.
140  

140  

141  
        Uses the thread-local frame allocator set by run_async.
141  
        Uses the thread-local frame allocator set by run_async.
142  
        Falls back to default memory resource if not set.
142  
        Falls back to default memory resource if not set.
143  
        Stores the allocator pointer at the end of each frame for
143  
        Stores the allocator pointer at the end of each frame for
144  
        correct deallocation even when TLS changes. Uses memcpy
144  
        correct deallocation even when TLS changes. Uses memcpy
145  
        to avoid alignment requirements on the trailing pointer.
145  
        to avoid alignment requirements on the trailing pointer.
146  
        Bypasses virtual dispatch for the recycling allocator.
146  
        Bypasses virtual dispatch for the recycling allocator.
147  
    */
147  
    */
148  
    static void* operator new(std::size_t size)
148  
    static void* operator new(std::size_t size)
149  
    {
149  
    {
150  
        static auto* const rmr = get_recycling_memory_resource();
150  
        static auto* const rmr = get_recycling_memory_resource();
151  

151  

152  
        auto* mr = get_current_frame_allocator();
152  
        auto* mr = get_current_frame_allocator();
153  
        if(!mr)
153  
        if(!mr)
154  
            mr = std::pmr::get_default_resource();
154  
            mr = std::pmr::get_default_resource();
155  

155  

156  
        auto total = size + sizeof(std::pmr::memory_resource*);
156  
        auto total = size + sizeof(std::pmr::memory_resource*);
157  
        void* raw;
157  
        void* raw;
158  
        if(mr == rmr)
158  
        if(mr == rmr)
159  
            raw = static_cast<recycling_memory_resource*>(mr)
159  
            raw = static_cast<recycling_memory_resource*>(mr)
160  
                ->allocate_fast(total, alignof(std::max_align_t));
160  
                ->allocate_fast(total, alignof(std::max_align_t));
161  
        else
161  
        else
162  
            raw = mr->allocate(total, alignof(std::max_align_t));
162  
            raw = mr->allocate(total, alignof(std::max_align_t));
163  
        std::memcpy(static_cast<char*>(raw) + size, &mr, sizeof(mr));
163  
        std::memcpy(static_cast<char*>(raw) + size, &mr, sizeof(mr));
164  
        return raw;
164  
        return raw;
165  
    }
165  
    }
166  

166  

167  
    /** Deallocate a coroutine frame.
167  
    /** Deallocate a coroutine frame.
168  

168  

169  
        Reads the allocator pointer stored at the end of the frame
169  
        Reads the allocator pointer stored at the end of the frame
170  
        to ensure correct deallocation regardless of current TLS.
170  
        to ensure correct deallocation regardless of current TLS.
171  
        Bypasses virtual dispatch for the recycling allocator.
171  
        Bypasses virtual dispatch for the recycling allocator.
172  
    */
172  
    */
173  
    static void operator delete(void* ptr, std::size_t size) noexcept
173  
    static void operator delete(void* ptr, std::size_t size) noexcept
174  
    {
174  
    {
175  
        static auto* const rmr = get_recycling_memory_resource();
175  
        static auto* const rmr = get_recycling_memory_resource();
176  

176  

177  
        std::pmr::memory_resource* mr;
177  
        std::pmr::memory_resource* mr;
178  
        std::memcpy(&mr, static_cast<char*>(ptr) + size, sizeof(mr));
178  
        std::memcpy(&mr, static_cast<char*>(ptr) + size, sizeof(mr));
179  
        auto total = size + sizeof(std::pmr::memory_resource*);
179  
        auto total = size + sizeof(std::pmr::memory_resource*);
180  
        if(mr == rmr)
180  
        if(mr == rmr)
181  
            static_cast<recycling_memory_resource*>(mr)
181  
            static_cast<recycling_memory_resource*>(mr)
182  
                ->deallocate_fast(ptr, total, alignof(std::max_align_t));
182  
                ->deallocate_fast(ptr, total, alignof(std::max_align_t));
183  
        else
183  
        else
184  
            mr->deallocate(ptr, total, alignof(std::max_align_t));
184  
            mr->deallocate(ptr, total, alignof(std::max_align_t));
185  
    }
185  
    }
186  

186  

187  
    ~io_awaitable_promise_base()
187  
    ~io_awaitable_promise_base()
188  
    {
188  
    {
189  
        // Abnormal teardown: destroy orphaned continuation
189  
        // Abnormal teardown: destroy orphaned continuation
190  
        if(cont_ != std::noop_coroutine())
190  
        if(cont_ != std::noop_coroutine())
191  
            cont_.destroy();
191  
            cont_.destroy();
192  
    }
192  
    }
193  

193  

194  
    //----------------------------------------------------------
194  
    //----------------------------------------------------------
195  
    // Continuation support
195  
    // Continuation support
196  
    //----------------------------------------------------------
196  
    //----------------------------------------------------------
197  

197  

198  
    /** Store the continuation to resume on completion.
198  
    /** Store the continuation to resume on completion.
199  

199  

200  
        Call this from your coroutine type's `await_suspend` overload
200  
        Call this from your coroutine type's `await_suspend` overload
201  
        to set up the completion path. The `final_suspend` awaiter
201  
        to set up the completion path. The `final_suspend` awaiter
202  
        returns this handle via unconditional symmetric transfer.
202  
        returns this handle via unconditional symmetric transfer.
203  

203  

204  
        @param cont The continuation to resume on completion.
204  
        @param cont The continuation to resume on completion.
205  
    */
205  
    */
206  
    void set_continuation(std::coroutine_handle<> cont) noexcept
206  
    void set_continuation(std::coroutine_handle<> cont) noexcept
207  
    {
207  
    {
208  
        cont_ = cont;
208  
        cont_ = cont;
209  
    }
209  
    }
210  

210  

211  
    /** Return and consume the stored continuation handle.
211  
    /** Return and consume the stored continuation handle.
212  

212  

213  
        Resets the stored handle to `noop_coroutine()` so the
213  
        Resets the stored handle to `noop_coroutine()` so the
214  
        destructor will not double-destroy it.
214  
        destructor will not double-destroy it.
215  

215  

216  
        @return The continuation for symmetric transfer.
216  
        @return The continuation for symmetric transfer.
217  
    */
217  
    */
218  
    std::coroutine_handle<> continuation() const noexcept
218  
    std::coroutine_handle<> continuation() const noexcept
219  
    {
219  
    {
220  
        return std::exchange(cont_, std::noop_coroutine());
220  
        return std::exchange(cont_, std::noop_coroutine());
221  
    }
221  
    }
222  

222  

223  
    //----------------------------------------------------------
223  
    //----------------------------------------------------------
224  
    // Environment support
224  
    // Environment support
225  
    //----------------------------------------------------------
225  
    //----------------------------------------------------------
226  

226  

227  
    /** Store a pointer to the execution environment.
227  
    /** Store a pointer to the execution environment.
228  

228  

229  
        Call this from your coroutine type's `await_suspend`
229  
        Call this from your coroutine type's `await_suspend`
230  
        overload to make the environment available via
230  
        overload to make the environment available via
231  
        `co_await this_coro::environment`. The pointed-to
231  
        `co_await this_coro::environment`. The pointed-to
232  
        `io_env` must outlive this coroutine.
232  
        `io_env` must outlive this coroutine.
233  

233  

234  
        @param env The environment to store.
234  
        @param env The environment to store.
235  
    */
235  
    */
236  
    void set_environment(io_env const* env) noexcept
236  
    void set_environment(io_env const* env) noexcept
237  
    {
237  
    {
238  
        env_ = env;
238  
        env_ = env;
239  
    }
239  
    }
240  

240  

241  
    /** Return the stored execution environment.
241  
    /** Return the stored execution environment.
242  

242  

243  
        @return The environment.
243  
        @return The environment.
244  
    */
244  
    */
245  
    io_env const* environment() const noexcept
245  
    io_env const* environment() const noexcept
246  
    {
246  
    {
247  
        BOOST_CAPY_ASSERT(env_);
247  
        BOOST_CAPY_ASSERT(env_);
248  
        return env_;
248  
        return env_;
249  
    }
249  
    }
250  

250  

251  
    /** Transform an awaitable before co_await.
251  
    /** Transform an awaitable before co_await.
252  

252  

253  
        Override this in your derived promise type to customize how
253  
        Override this in your derived promise type to customize how
254  
        awaitables are transformed. The default implementation passes
254  
        awaitables are transformed. The default implementation passes
255  
        the awaitable through unchanged.
255  
        the awaitable through unchanged.
256  

256  

257  
        @param a The awaitable expression from `co_await a`.
257  
        @param a The awaitable expression from `co_await a`.
258  

258  

259  
        @return The transformed awaitable.
259  
        @return The transformed awaitable.
260  
    */
260  
    */
261  
    template<typename A>
261  
    template<typename A>
262  
    decltype(auto) transform_awaitable(A&& a)
262  
    decltype(auto) transform_awaitable(A&& a)
263  
    {
263  
    {
264  
        return std::forward<A>(a);
264  
        return std::forward<A>(a);
265  
    }
265  
    }
266  

266  

267  
    /** Intercept co_await expressions.
267  
    /** Intercept co_await expressions.
268  

268  

269  
        This function handles @ref this_coro::environment_tag and
269  
        This function handles @ref this_coro::environment_tag and
270  
        the fine-grained tags (@ref this_coro::executor_tag,
270  
        the fine-grained tags (@ref this_coro::executor_tag,
271  
        @ref this_coro::stop_token_tag, @ref this_coro::allocator_tag)
271  
        @ref this_coro::stop_token_tag, @ref this_coro::allocator_tag)
272  
        specially, returning an awaiter that yields the stored value.
272  
        specially, returning an awaiter that yields the stored value.
273  
        All other awaitables are delegated to @ref transform_awaitable.
273  
        All other awaitables are delegated to @ref transform_awaitable.
274  

274  

275  
        @param t The awaited expression.
275  
        @param t The awaited expression.
276  

276  

277  
        @return An awaiter for the expression.
277  
        @return An awaiter for the expression.
278  
    */
278  
    */
279  
    template<typename T>
279  
    template<typename T>
280  
    auto await_transform(T&& t)
280  
    auto await_transform(T&& t)
281  
    {
281  
    {
282  
        using Tag = std::decay_t<T>;
282  
        using Tag = std::decay_t<T>;
283  

283  

284  
        if constexpr (std::is_same_v<Tag, this_coro::environment_tag>)
284  
        if constexpr (std::is_same_v<Tag, this_coro::environment_tag>)
285  
        {
285  
        {
286  
            BOOST_CAPY_ASSERT(env_);
286  
            BOOST_CAPY_ASSERT(env_);
287  
            struct awaiter
287  
            struct awaiter
288  
            {
288  
            {
289  
                io_env const* env_;
289  
                io_env const* env_;
290  
                bool await_ready() const noexcept { return true; }
290  
                bool await_ready() const noexcept { return true; }
291  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
291  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
292  
                io_env const* await_resume() const noexcept { return env_; }
292  
                io_env const* await_resume() const noexcept { return env_; }
293  
            };
293  
            };
294  
            return awaiter{env_};
294  
            return awaiter{env_};
295  
        }
295  
        }
296  
        else if constexpr (std::is_same_v<Tag, this_coro::executor_tag>)
296  
        else if constexpr (std::is_same_v<Tag, this_coro::executor_tag>)
297  
        {
297  
        {
298  
            BOOST_CAPY_ASSERT(env_);
298  
            BOOST_CAPY_ASSERT(env_);
299  
            struct awaiter
299  
            struct awaiter
300  
            {
300  
            {
301  
                executor_ref executor_;
301  
                executor_ref executor_;
302  
                bool await_ready() const noexcept { return true; }
302  
                bool await_ready() const noexcept { return true; }
303  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
303  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
304  
                executor_ref await_resume() const noexcept { return executor_; }
304  
                executor_ref await_resume() const noexcept { return executor_; }
305  
            };
305  
            };
306  
            return awaiter{env_->executor};
306  
            return awaiter{env_->executor};
307  
        }
307  
        }
308  
        else if constexpr (std::is_same_v<Tag, this_coro::stop_token_tag>)
308  
        else if constexpr (std::is_same_v<Tag, this_coro::stop_token_tag>)
309  
        {
309  
        {
310  
            BOOST_CAPY_ASSERT(env_);
310  
            BOOST_CAPY_ASSERT(env_);
311  
            struct awaiter
311  
            struct awaiter
312  
            {
312  
            {
313  
                std::stop_token token_;
313  
                std::stop_token token_;
314  
                bool await_ready() const noexcept { return true; }
314  
                bool await_ready() const noexcept { return true; }
315  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
315  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
316  
                std::stop_token await_resume() const noexcept { return token_; }
316  
                std::stop_token await_resume() const noexcept { return token_; }
317  
            };
317  
            };
318  
            return awaiter{env_->stop_token};
318  
            return awaiter{env_->stop_token};
319  
        }
319  
        }
320  
        else if constexpr (std::is_same_v<Tag, this_coro::allocator_tag>)
320  
        else if constexpr (std::is_same_v<Tag, this_coro::allocator_tag>)
321  
        {
321  
        {
322  
            BOOST_CAPY_ASSERT(env_);
322  
            BOOST_CAPY_ASSERT(env_);
323  
            struct awaiter
323  
            struct awaiter
324  
            {
324  
            {
325  
                std::pmr::memory_resource* allocator_;
325  
                std::pmr::memory_resource* allocator_;
326  
                bool await_ready() const noexcept { return true; }
326  
                bool await_ready() const noexcept { return true; }
327  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
327  
                void await_suspend(std::coroutine_handle<>) const noexcept { }
328  
                std::pmr::memory_resource* await_resume() const noexcept { return allocator_; }
328  
                std::pmr::memory_resource* await_resume() const noexcept { return allocator_; }
329  
            };
329  
            };
330  
            return awaiter{env_->allocator};
330  
            return awaiter{env_->allocator};
331  
        }
331  
        }
332  
        else
332  
        else
333  
        {
333  
        {
334  
            return static_cast<Derived*>(this)->transform_awaitable(
334  
            return static_cast<Derived*>(this)->transform_awaitable(
335  
                std::forward<T>(t));
335  
                std::forward<T>(t));
336  
        }
336  
        }
337  
    }
337  
    }
338  
};
338  
};
339  

339  

340  
} // namespace capy
340  
} // namespace capy
341  
} // namespace boost
341  
} // namespace boost
342  

342  

343  
#endif
343  
#endif