78.21% Lines (122/156)
79.59% Functions (39/49)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | + | // | ||||||
| 2 | + | // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.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_ASIO_BOOST_HPP | ||||||
| 11 | + | #define BOOST_CAPY_ASIO_BOOST_HPP | ||||||
| 12 | + | |||||||
| 13 | + | /** @file boost.hpp | ||||||
| 14 | + | * @brief Boost.Asio integration for capy coroutines. | ||||||
| 15 | + | * | ||||||
| 16 | + | * This header provides complete integration between capy's coroutine framework | ||||||
| 17 | + | * and Boost.Asio. Include this header when using capy with Boost.Asio. | ||||||
| 18 | + | * | ||||||
| 19 | + | * @par Features | ||||||
| 20 | + | * - Property query/require support for `asio_executor_adapter` | ||||||
| 21 | + | * - `asio_boost_standard_executor` wrapper for Boost.Asio executors | ||||||
| 22 | + | * - `async_result` specialization for `as_io_awaitable_t` | ||||||
| 23 | + | * - Three-argument `asio_spawn` overloads with completion tokens | ||||||
| 24 | + | * | ||||||
| 25 | + | * @par Example | ||||||
| 26 | + | * @code | ||||||
| 27 | + | * #include <boost/capy/asio/boost.hpp> | ||||||
| 28 | + | * #include <boost/asio.hpp> | ||||||
| 29 | + | * | ||||||
| 30 | + | * capy::io_task<void> my_coro(boost::asio::ip::tcp::socket& sock) { | ||||||
| 31 | + | * char buf[1024]; | ||||||
| 32 | + | * auto [n] = co_await sock.async_read_some( | ||||||
| 33 | + | * boost::asio::buffer(buf), capy::as_io_awaitable); | ||||||
| 34 | + | * // ... | ||||||
| 35 | + | * } | ||||||
| 36 | + | * | ||||||
| 37 | + | * int main() { | ||||||
| 38 | + | * boost::asio::io_context io; | ||||||
| 39 | + | * auto exec = capy::wrap_asio_executor(io.get_executor()); | ||||||
| 40 | + | * capy::asio_spawn(exec, my_coro(socket))(boost::asio::detached); | ||||||
| 41 | + | * io.run(); | ||||||
| 42 | + | * } | ||||||
| 43 | + | * @endcode | ||||||
| 44 | + | * | ||||||
| 45 | + | * @see standalone.hpp For standalone Asio support | ||||||
| 46 | + | * @ingroup asio | ||||||
| 47 | + | */ | ||||||
| 48 | + | |||||||
| 49 | + | #include <boost/capy/concept/io_awaitable.hpp> | ||||||
| 50 | + | #include <boost/capy/concept/decomposes_to.hpp> | ||||||
| 51 | + | |||||||
| 52 | + | #include <boost/capy/asio/as_io_awaitable.hpp> | ||||||
| 53 | + | #include <boost/capy/asio/buffers.hpp> | ||||||
| 54 | + | #include <boost/capy/asio/detail/completion_handler.hpp> | ||||||
| 55 | + | #include <boost/capy/asio/executor_adapter.hpp> | ||||||
| 56 | + | #include <boost/capy/asio/spawn.hpp> | ||||||
| 57 | + | #include <boost/capy/asio/executor_from_asio.hpp> | ||||||
| 58 | + | |||||||
| 59 | + | |||||||
| 60 | + | #include <boost/asio/append.hpp> | ||||||
| 61 | + | #include <boost/asio/associated_allocator.hpp> | ||||||
| 62 | + | #include <boost/asio/associated_cancellation_slot.hpp> | ||||||
| 63 | + | #include <boost/asio/associated_immediate_executor.hpp> | ||||||
| 64 | + | #include <boost/asio/buffer.hpp> | ||||||
| 65 | + | #include <boost/asio/dispatch.hpp> | ||||||
| 66 | + | #include <boost/asio/cancellation_signal.hpp> | ||||||
| 67 | + | #include <boost/asio/cancellation_type.hpp> | ||||||
| 68 | + | #include <boost/asio/execution_context.hpp> | ||||||
| 69 | + | #include <boost/asio/execution/allocator.hpp> | ||||||
| 70 | + | #include <boost/asio/execution/blocking.hpp> | ||||||
| 71 | + | #include <boost/asio/execution/context.hpp> | ||||||
| 72 | + | #include <boost/asio/execution/outstanding_work.hpp> | ||||||
| 73 | + | #include <boost/asio/execution/relationship.hpp> | ||||||
| 74 | + | #include <tuple> | ||||||
| 75 | + | #include <type_traits> | ||||||
| 76 | + | |||||||
| 77 | + | namespace boost::capy | ||||||
| 78 | + | { | ||||||
| 79 | + | |||||||
| 80 | + | /** @addtogroup asio | ||||||
| 81 | + | * @{ | ||||||
| 82 | + | */ | ||||||
| 83 | + | |||||||
| 84 | + | /// @name Execution Property Queries for asio_executor_adapter | ||||||
| 85 | + | /// @{ | ||||||
| 86 | + | |||||||
| 87 | + | /** @brief Queries the execution context from an asio_executor_adapter. | ||||||
| 88 | + | * | ||||||
| 89 | + | * Returns the Boost.Asio execution context associated with the adapter. | ||||||
| 90 | + | * The context is provided via an adapter service that bridges capy's | ||||||
| 91 | + | * execution_context with Asio's. | ||||||
| 92 | + | * | ||||||
| 93 | + | * @param exec The executor adapter to query | ||||||
| 94 | + | * @return Reference to the associated `boost::asio::execution_context` | ||||||
| 95 | + | */ | ||||||
| 96 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 97 | + | boost::asio::execution_context& | ||||||
| HITGNC | 98 | + | 1 | query(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||
| 99 | + | boost::asio::execution::context_t) noexcept | ||||||
| 100 | + | { | ||||||
| 101 | + | using service = detail::asio_adapter_context_service< | ||||||
| 102 | + | boost::asio::execution_context>; | ||||||
| HITGNC | 103 | + | 1 | return exec.context(). | ||||
| HITGNC | 104 | + | 1 | template use_service<service>(); | ||||
| 105 | + | } | ||||||
| 106 | + | |||||||
| 107 | + | /** @brief Queries the blocking property from an asio_executor_adapter. | ||||||
| 108 | + | * @param exec The executor adapter to query | ||||||
| 109 | + | * @return The current blocking property value | ||||||
| 110 | + | */ | ||||||
| 111 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 112 | + | constexpr boost::asio::execution::blocking_t | ||||||
| HITGNC | 113 | + | 4 | query(const asio_executor_adapter<Executor, Allocator, Bits> &, | ||||
| 114 | + | boost::asio::execution::blocking_t) noexcept | ||||||
| 115 | + | { | ||||||
| 116 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| 117 | + | |||||||
| 118 | + | switch (Bits & ex::blocking_mask) | ||||||
| 119 | + | { | ||||||
| 120 | + | case ex::blocking_never: | ||||||
| MISUNC | 121 | + | ✗ | return boost::asio::execution::blocking.never; | ||||
| 122 | + | case ex::blocking_always: | ||||||
| 123 | + | return boost::asio::execution::blocking.always; | ||||||
| 124 | + | case ex::blocking_possibly: | ||||||
| HITGNC | 125 | + | 4 | return boost::asio::execution::blocking.possibly; | ||||
| 126 | + | default: return {}; | ||||||
| 127 | + | } | ||||||
| 128 | + | } | ||||||
| 129 | + | |||||||
| 130 | + | /// @} | ||||||
| 131 | + | |||||||
| 132 | + | /// @name Execution Property Requirements for asio_executor_adapter | ||||||
| 133 | + | /// @{ | ||||||
| 134 | + | |||||||
| 135 | + | /** @brief Requires blocking.possibly property. | ||||||
| 136 | + | * @return New adapter with blocking.possibly set | ||||||
| 137 | + | */ | ||||||
| 138 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 139 | + | constexpr auto | ||||||
| MISUNC | 140 | + | ✗ | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||
| 141 | + | boost::asio::execution::blocking_t::possibly_t) | ||||||
| 142 | + | { | ||||||
| 143 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| MISUNC | 144 | + | ✗ | constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_possibly; | ||||
| MISUNC | 145 | + | ✗ | return asio_executor_adapter<Executor, Allocator, new_bits>(exec); | ||||
| 146 | + | } | ||||||
| 147 | + | |||||||
| 148 | + | /** @brief Requires blocking.never property. | ||||||
| 149 | + | * @return New adapter that never blocks | ||||||
| 150 | + | */ | ||||||
| 151 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 152 | + | constexpr auto | ||||||
| HITGNC | 153 | + | 6 | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||
| 154 | + | boost::asio::execution::blocking_t::never_t) | ||||||
| 155 | + | { | ||||||
| 156 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| HITGNC | 157 | + | 6 | constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_never; | ||||
| HITGNC | 158 | + | 6 | return asio_executor_adapter<Executor, Allocator, new_bits>(exec); | ||||
| 159 | + | } | ||||||
| 160 | + | |||||||
| 161 | + | /** @brief Requires blocking.always property. | ||||||
| 162 | + | * @return New adapter that always blocks until execution completes | ||||||
| 163 | + | */ | ||||||
| 164 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 165 | + | constexpr auto | ||||||
| 166 | + | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||||
| 167 | + | boost::asio::execution::blocking_t::always_t) | ||||||
| 168 | + | { | ||||||
| 169 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| 170 | + | constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_always; | ||||||
| 171 | + | return asio_executor_adapter<Executor, Allocator, new_bits>(exec); | ||||||
| 172 | + | } | ||||||
| 173 | + | |||||||
| 174 | + | /** @brief Queries the outstanding_work property. | ||||||
| 175 | + | * @param exec The executor adapter to query | ||||||
| 176 | + | * @return The current work tracking setting | ||||||
| 177 | + | */ | ||||||
| 178 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| MISUNC | 179 | + | ✗ | static constexpr boost::asio::execution::outstanding_work_t query( | ||||
| 180 | + | const asio_executor_adapter<Executor, Allocator, Bits> &, | ||||||
| 181 | + | boost::asio::execution::outstanding_work_t) noexcept | ||||||
| 182 | + | { | ||||||
| 183 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| 184 | + | switch (Bits & ex::work_mask) | ||||||
| 185 | + | { | ||||||
| 186 | + | case ex::work_tracked: | ||||||
| MISUNC | 187 | + | ✗ | return boost::asio::execution::outstanding_work.tracked; | ||||
| 188 | + | case ex::work_untracked: | ||||||
| MISUNC | 189 | + | ✗ | return boost::asio::execution::outstanding_work.untracked; | ||||
| 190 | + | default: return {}; | ||||||
| 191 | + | } | ||||||
| 192 | + | } | ||||||
| 193 | + | |||||||
| 194 | + | /** @brief Requires outstanding_work.tracked property. | ||||||
| 195 | + | * @return New adapter that tracks outstanding work | ||||||
| 196 | + | */ | ||||||
| 197 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 198 | + | constexpr auto | ||||||
| HITGNC | 199 | + | 8 | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||
| 200 | + | boost::asio::execution::outstanding_work_t::tracked_t) | ||||||
| 201 | + | { | ||||||
| 202 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| HITGNC | 203 | + | 8 | constexpr int new_bits = (Bits & ~ex::work_mask) | ex::work_tracked; | ||||
| HITGNC | 204 | + | 8 | return asio_executor_adapter<Executor, Allocator, new_bits>(exec); | ||||
| 205 | + | } | ||||||
| 206 | + | |||||||
| 207 | + | /** @brief Requires outstanding_work.untracked property. | ||||||
| 208 | + | * @return New adapter that does not track outstanding work | ||||||
| 209 | + | */ | ||||||
| 210 | + | template<typename Executor, typename Allocator, int Bits> | ||||||
| 211 | + | constexpr auto | ||||||
| MISUNC | 212 | + | ✗ | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||
| 213 | + | boost::asio::execution::outstanding_work_t::untracked_t) | ||||||
| 214 | + | { | ||||||
| 215 | + | using ex = asio_executor_adapter<Executor, Allocator, Bits>; | ||||||
| MISUNC | 216 | + | ✗ | constexpr int new_bits = (Bits & ~ex::work_mask) | ex::work_untracked; | ||||
| MISUNC | 217 | + | ✗ | return asio_executor_adapter<Executor, Allocator, new_bits>(exec); | ||||
| 218 | + | } | ||||||
| 219 | + | |||||||
| 220 | + | /** @brief Queries the allocator property. | ||||||
| 221 | + | * @return The adapter's current allocator | ||||||
| 222 | + | */ | ||||||
| 223 | + | template <typename Executor, typename Allocator, int Bits, typename OtherAllocator> | ||||||
| 224 | + | constexpr Allocator query( | ||||||
| 225 | + | const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||||
| 226 | + | boost::asio::execution::allocator_t<OtherAllocator>) noexcept | ||||||
| 227 | + | { | ||||||
| 228 | + | return exec.get_allocator(); | ||||||
| 229 | + | } | ||||||
| 230 | + | |||||||
| 231 | + | /** @brief Requires a specific allocator. | ||||||
| 232 | + | * @param a The allocator property containing the new allocator | ||||||
| 233 | + | * @return New adapter using the specified allocator | ||||||
| 234 | + | */ | ||||||
| 235 | + | template <typename Executor, typename Allocator, int Bits, typename OtherAllocator> | ||||||
| 236 | + | constexpr auto | ||||||
| HITGNC | 237 | + | 10 | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||
| 238 | + | boost::asio::execution::allocator_t<OtherAllocator> a) | ||||||
| 239 | + | { | ||||||
| 240 | + | return asio_executor_adapter<Executor, OtherAllocator, Bits>( | ||||||
| HITGNC | 241 | + | 10 | exec, a.value() | ||||
| HITGNC | 242 | + | 10 | ); | ||||
| 243 | + | } | ||||||
| 244 | + | |||||||
| 245 | + | /** @brief Requires the default allocator (uses frame allocator). | ||||||
| 246 | + | * @return New adapter using the frame allocator from the context | ||||||
| 247 | + | */ | ||||||
| 248 | + | template <typename Executor, typename Allocator, int Bits> | ||||||
| 249 | + | constexpr auto | ||||||
| 250 | + | require(const asio_executor_adapter<Executor, Allocator, Bits> & exec, | ||||||
| 251 | + | boost::asio::execution::allocator_t<void>) | ||||||
| 252 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| 253 | + | { | ||||||
| 254 | + | return asio_executor_adapter< | ||||||
| 255 | + | Executor, | ||||||
| 256 | + | std::pmr::polymorphic_allocator<void>, | ||||||
| 257 | + | Bits> | ||||||
| 258 | + | ( | ||||||
| 259 | + | exec, | ||||||
| 260 | + | exec.context().get_frame_allocator() | ||||||
| 261 | + | ); | ||||||
| 262 | + | } | ||||||
| 263 | + | |||||||
| 264 | + | /// @} | ||||||
| 265 | + | |||||||
| 266 | + | namespace detail | ||||||
| 267 | + | { | ||||||
| 268 | + | |||||||
| 269 | + | template<typename Executor> | ||||||
| 270 | + | struct asio_work_tracker_service : boost::asio::execution_context::service | ||||||
| 271 | + | { | ||||||
| 272 | + | static boost::asio::execution_context::id id; | ||||||
| 273 | + | |||||||
| HITGNC | 274 | + | 1 | asio_work_tracker_service(boost::asio::execution_context & ctx) | ||||
| HITGNC | 275 | + | 1 | : boost::asio::execution_context::service(ctx) {} | ||||
| 276 | + | |||||||
| 277 | + | using tracked_executor = | ||||||
| 278 | + | typename boost::asio::prefer_result< | ||||||
| 279 | + | Executor, | ||||||
| 280 | + | boost::asio::execution::outstanding_work_t::tracked_t | ||||||
| 281 | + | >::type; | ||||||
| 282 | + | |||||||
| 283 | + | alignas(tracked_executor) char buffer[sizeof(tracked_executor) ]; | ||||||
| 284 | + | |||||||
| 285 | + | |||||||
| 286 | + | std::mutex mutex; | ||||||
| 287 | + | std::size_t work = 0u; | ||||||
| 288 | + | |||||||
| HITGNC | 289 | + | 2 | void shutdown() | ||||
| 290 | + | { | ||||||
| HITGNC | 291 | + | 2 | std::lock_guard _(mutex); | ||||
| HITGNC | 292 | + | 2 | if (std::exchange(work, 0) > 0u) | ||||
| MISUNC | 293 | + | ✗ | reinterpret_cast<tracked_executor*>(buffer)->~tracked_executor(); | ||||
| HITGNC | 294 | + | 2 | } | ||||
| 295 | + | |||||||
| 296 | + | |||||||
| HITGNC | 297 | + | 1 | void work_started(const Executor & exec) | ||||
| 298 | + | { | ||||||
| HITGNC | 299 | + | 1 | std::lock_guard _(mutex); | ||||
| HITGNC | 300 | + | 1 | if (work ++ == 0u) | ||||
| HITGNC | 301 | + | 1 | new (buffer) tracked_executor( | ||||
| 302 | + | boost::asio::prefer(exec, | ||||||
| 303 | + | boost::asio::execution::outstanding_work.tracked)); | ||||||
| HITGNC | 304 | + | 1 | } | ||||
| 305 | + | |||||||
| HITGNC | 306 | + | 1 | void work_finished() | ||||
| 307 | + | { | ||||||
| HITGNC | 308 | + | 1 | std::lock_guard _(mutex); | ||||
| HITGNC | 309 | + | 1 | if (--work == 0u) | ||||
| HITGNC | 310 | + | 1 | reinterpret_cast<tracked_executor*>(buffer)->~tracked_executor(); | ||||
| HITGNC | 311 | + | 1 | } | ||||
| 312 | + | |||||||
| 313 | + | }; | ||||||
| 314 | + | |||||||
| 315 | + | |||||||
| 316 | + | template<typename Executor> | ||||||
| 317 | + | boost::asio::execution_context::id asio_work_tracker_service<Executor>::id; | ||||||
| 318 | + | |||||||
| 319 | + | |||||||
| 320 | + | } | ||||||
| 321 | + | |||||||
| 322 | + | /** @brief Wraps a Boost.Asio standard executor for use with capy. | ||||||
| 323 | + | * | ||||||
| 324 | + | * This class adapts Boost.Asio executors that follow the P0443/P2300 | ||||||
| 325 | + | * standard executor model to be usable as capy executors. It provides | ||||||
| 326 | + | * work tracking through an `asio_work_tracker_service` and integrates | ||||||
| 327 | + | * with capy's execution context system. | ||||||
| 328 | + | * | ||||||
| 329 | + | * @tparam Executor A Boost.Asio executor satisfying `AsioBoostStandardExecutor` | ||||||
| 330 | + | * | ||||||
| 331 | + | * @par Example | ||||||
| 332 | + | * @code | ||||||
| 333 | + | * boost::asio::io_context io; | ||||||
| 334 | + | * auto wrapped = asio_boost_standard_executor(io.get_executor()); | ||||||
| 335 | + | * | ||||||
| 336 | + | * // Use with capy coroutines | ||||||
| 337 | + | * capy::run(wrapped, my_io_task()); | ||||||
| 338 | + | * @endcode | ||||||
| 339 | + | * | ||||||
| 340 | + | * @see wrap_asio_executor For automatic executor type detection | ||||||
| 341 | + | * @see asio_net_ts_executor For legacy Networking TS executors | ||||||
| 342 | + | */ | ||||||
| 343 | + | template<detail::AsioBoostStandardExecutor Executor> | ||||||
| 344 | + | struct asio_boost_standard_executor | ||||||
| 345 | + | { | ||||||
| 346 | + | /** @brief Constructs from a Boost.Asio executor. | ||||||
| 347 | + | * @param executor The Boost.Asio executor to wrap | ||||||
| 348 | + | */ | ||||||
| HITGNC | 349 | + | 1 | asio_boost_standard_executor(Executor executor) | ||||
| 350 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| HITGNC | 351 | + | 1 | : executor_(std::move(executor)) | ||||
| 352 | + | { | ||||||
| HITGNC | 353 | + | 1 | } | ||||
| 354 | + | |||||||
| 355 | + | /** @brief Move constructor. */ | ||||||
| HITGNC | 356 | + | 5 | asio_boost_standard_executor(asio_boost_standard_executor && rhs) | ||||
| 357 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| HITGNC | 358 | + | 5 | : executor_(std::move(rhs.executor_)) | ||||
| 359 | + | { | ||||||
| HITGNC | 360 | + | 5 | } | ||||
| 361 | + | |||||||
| 362 | + | /** @brief Copy constructor. */ | ||||||
| HITGNC | 363 | + | 1 | asio_boost_standard_executor(const asio_boost_standard_executor & rhs) | ||||
| 364 | + | noexcept(std::is_nothrow_copy_constructible_v<Executor>) | ||||||
| HITGNC | 365 | + | 1 | : executor_(rhs.executor_) | ||||
| 366 | + | { | ||||||
| HITGNC | 367 | + | 1 | } | ||||
| 368 | + | |||||||
| 369 | + | /** @brief Returns the associated capy execution context. | ||||||
| 370 | + | * @return Reference to the capy execution_context | ||||||
| 371 | + | */ | ||||||
| HITGNC | 372 | + | 2 | execution_context& context() const noexcept | ||||
| 373 | + | { | ||||||
| HITGNC | 374 | + | 2 | auto & ec = boost::asio::query(executor_, boost::asio::execution::context); | ||||
| 375 | + | return boost::asio::use_service< | ||||||
| 376 | + | detail::asio_context_service<boost::asio::execution_context> | ||||||
| HITGNC | 377 | + | 2 | >(ec); | ||||
| 378 | + | } | ||||||
| 379 | + | |||||||
| 380 | + | /** @brief Notifies that work has started. | ||||||
| 381 | + | * | ||||||
| 382 | + | * Delegates to the work tracker service to maintain a tracked executor | ||||||
| 383 | + | * while work is outstanding. | ||||||
| 384 | + | */ | ||||||
| HITGNC | 385 | + | 1 | void on_work_started() const noexcept | ||||
| 386 | + | { | ||||||
| HITGNC | 387 | + | 1 | auto & ec = boost::asio::query(executor_, boost::asio::execution::context); | ||||
| 388 | + | boost::asio::use_service< | ||||||
| 389 | + | detail::asio_work_tracker_service<Executor> | ||||||
| HITGNC | 390 | + | 1 | >(ec).work_started(executor_); | ||||
| HITGNC | 391 | + | 1 | } | ||||
| 392 | + | |||||||
| 393 | + | /** @brief Notifies that work has finished. | ||||||
| 394 | + | * | ||||||
| 395 | + | * Decrements the work counter in the tracker service. | ||||||
| 396 | + | */ | ||||||
| HITGNC | 397 | + | 1 | void on_work_finished() const noexcept | ||||
| 398 | + | { | ||||||
| HITGNC | 399 | + | 1 | auto & ec = boost::asio::query(executor_, boost::asio::execution::context); | ||||
| 400 | + | boost::asio::use_service< | ||||||
| 401 | + | detail::asio_work_tracker_service<Executor> | ||||||
| HITGNC | 402 | + | 1 | >(ec).work_finished(); | ||||
| HITGNC | 403 | + | 1 | } | ||||
| 404 | + | |||||||
| 405 | + | /** @brief Dispatches a continuation for execution. | ||||||
| 406 | + | * | ||||||
| 407 | + | * May execute inline if the executor allows, otherwise posts. | ||||||
| 408 | + | * Uses the context's frame allocator for handler allocation. | ||||||
| 409 | + | * | ||||||
| 410 | + | * @param c The continuation to dispatch | ||||||
| 411 | + | * @return A noop coroutine handle | ||||||
| 412 | + | */ | ||||||
| HITGNC | 413 | + | 1 | std::coroutine_handle<> dispatch(continuation & c) const | ||||
| 414 | + | { | ||||||
| HITGNC | 415 | + | 2 | boost::asio::prefer( | ||||
| HITGNC | 416 | + | 1 | executor_, | ||||
| HITGNC | 417 | + | 1 | boost::asio::execution::allocator( | ||||
| HITGNC | 418 | + | 1 | std::pmr::polymorphic_allocator<void>( | ||||
| HITGNC | 419 | + | 1 | context().get_frame_allocator() | ||||
| 420 | + | ) | ||||||
| 421 | + | ) | ||||||
| HITGNC | 422 | + | 1 | ).execute(detail::asio_coroutine_unique_handle(c.h)); | ||||
| HITGNC | 423 | + | 1 | return std::noop_coroutine(); | ||||
| 424 | + | } | ||||||
| 425 | + | |||||||
| 426 | + | /** @brief Posts a continuation for deferred execution. | ||||||
| 427 | + | * | ||||||
| 428 | + | * The continuation will never be executed inline. Uses blocking.never | ||||||
| 429 | + | * and relationship.fork properties for proper async behavior. | ||||||
| 430 | + | * | ||||||
| 431 | + | * @param c The continuation to post | ||||||
| 432 | + | */ | ||||||
| MISUNC | 433 | + | ✗ | void post(continuation & c) const | ||||
| 434 | + | { | ||||||
| 435 | + | boost::asio::prefer( | ||||||
| MISUNC | 436 | + | ✗ | boost::asio::require(executor_, boost::asio::execution::blocking.never), | ||||
| 437 | + | boost::asio::execution::relationship.fork, | ||||||
| MISUNC | 438 | + | ✗ | boost::asio::execution::allocator( | ||||
| MISUNC | 439 | + | ✗ | std::pmr::polymorphic_allocator<void>( | ||||
| MISUNC | 440 | + | ✗ | context().get_frame_allocator() | ||||
| 441 | + | ) | ||||||
| 442 | + | ) | ||||||
| MISUNC | 443 | + | ✗ | ).execute(detail::asio_coroutine_unique_handle(c.h)); | ||||
| MISUNC | 444 | + | ✗ | } | ||||
| 445 | + | |||||||
| 446 | + | /** @brief Equality comparison. */ | ||||||
| MISUNC | 447 | + | ✗ | bool operator==(const asio_boost_standard_executor & rhs) const noexcept | ||||
| 448 | + | { | ||||||
| MISUNC | 449 | + | ✗ | return executor_ == rhs.executor_; | ||||
| 450 | + | } | ||||||
| 451 | + | |||||||
| 452 | + | /** @brief Inequality comparison. */ | ||||||
| 453 | + | bool operator!=(const asio_boost_standard_executor & rhs) const noexcept | ||||||
| 454 | + | { | ||||||
| 455 | + | return executor_ != rhs.executor_; | ||||||
| 456 | + | } | ||||||
| 457 | + | |||||||
| 458 | + | private: | ||||||
| 459 | + | Executor executor_; | ||||||
| 460 | + | }; | ||||||
| 461 | + | |||||||
| 462 | + | |||||||
| 463 | + | /** @brief Spawns a capy coroutine with a Boost.Asio completion token (executor overload). | ||||||
| 464 | + | * | ||||||
| 465 | + | * Convenience overload that combines the two-step spawn process into one call. | ||||||
| 466 | + | * Equivalent to `asio_spawn(exec, awaitable)(token)`. | ||||||
| 467 | + | * | ||||||
| 468 | + | * @tparam ExecutorType The executor type | ||||||
| 469 | + | * @tparam Awaitable The coroutine type | ||||||
| 470 | + | * @tparam Token A Boost.Asio completion token | ||||||
| 471 | + | * @param exec The executor to run on | ||||||
| 472 | + | * @param awaitable The coroutine to spawn | ||||||
| 473 | + | * @param token The completion token | ||||||
| 474 | + | * @return Depends on the token type | ||||||
| 475 | + | */ | ||||||
| 476 | + | template<Executor ExecutorType, | ||||||
| 477 | + | IoAwaitable Awaitable, | ||||||
| 478 | + | boost::asio::completion_token_for< | ||||||
| 479 | + | detail::completion_signature_for_io_awaitable<Awaitable> | ||||||
| 480 | + | > Token> | ||||||
| HITGNC | 481 | + | 4 | auto asio_spawn(ExecutorType exec, Awaitable && awaitable, Token token) | ||||
| 482 | + | { | ||||||
| HITGNC | 483 | + | 4 | return asio_spawn(exec, std::forward<Awaitable>(awaitable))(std::move(token)); | ||||
| 484 | + | } | ||||||
| 485 | + | |||||||
| 486 | + | /** @brief Spawns a capy coroutine with a Boost.Asio completion token (context overload). | ||||||
| 487 | + | * | ||||||
| 488 | + | * Convenience overload that extracts the executor from a context. | ||||||
| 489 | + | * | ||||||
| 490 | + | * @tparam Context The execution context type | ||||||
| 491 | + | * @tparam Awaitable The coroutine type | ||||||
| 492 | + | * @tparam Token A Boost.Asio completion token | ||||||
| 493 | + | * @param ctx The execution context | ||||||
| 494 | + | * @param awaitable The coroutine to spawn | ||||||
| 495 | + | * @param token The completion token | ||||||
| 496 | + | * @return Depends on the token type | ||||||
| 497 | + | */ | ||||||
| 498 | + | template<ExecutionContext Context, | ||||||
| 499 | + | IoAwaitable Awaitable, | ||||||
| 500 | + | boost::asio::completion_token_for< | ||||||
| 501 | + | detail::completion_signature_for_io_awaitable<Awaitable> | ||||||
| 502 | + | > Token> | ||||||
| 503 | + | auto asio_spawn(Context & ctx, Awaitable && awaitable, Token token) | ||||||
| 504 | + | { | ||||||
| 505 | + | return asio_spawn(ctx.get_executor(), std::forward<Awaitable>(awaitable))(std::move(token)); | ||||||
| 506 | + | } | ||||||
| 507 | + | |||||||
| 508 | + | /** @} */ // end of asio group | ||||||
| 509 | + | |||||||
| 510 | + | } | ||||||
| 511 | + | |||||||
| 512 | + | template<typename ... Ts> | ||||||
| 513 | + | struct boost::asio::async_result<boost::capy::as_io_awaitable_t, void(Ts...)> | ||||||
| 514 | + | : boost::capy::detail::async_result_impl< | ||||||
| 515 | + | boost::asio::cancellation_signal, | ||||||
| 516 | + | boost::asio::cancellation_type, | ||||||
| 517 | + | Ts...> | ||||||
| 518 | + | { | ||||||
| 519 | + | }; | ||||||
| 520 | + | |||||||
| 521 | + | |||||||
| 522 | + | namespace boost::capy::detail | ||||||
| 523 | + | { | ||||||
| 524 | + | |||||||
| 525 | + | |||||||
| 526 | + | struct boost_asio_init; | ||||||
| 527 | + | |||||||
| 528 | + | |||||||
| 529 | + | template<typename Allocator> | ||||||
| 530 | + | struct boost_asio_promise_type_allocator_base | ||||||
| 531 | + | { | ||||||
| 532 | + | template<typename Handler, Executor Ex, IoAwaitable Awaitable> | ||||||
| 533 | + | void * operator new (std::size_t n, boost_asio_init &, | ||||||
| 534 | + | Handler & handler, | ||||||
| 535 | + | Ex &, Awaitable &) | ||||||
| 536 | + | { | ||||||
| 537 | + | using allocator_type = std::allocator_traits<Allocator> | ||||||
| 538 | + | ::template rebind_alloc<char>; | ||||||
| 539 | + | allocator_type allocator(boost::asio::get_associated_allocator(handler)); | ||||||
| 540 | + | |||||||
| 541 | + | // round n up to max_align | ||||||
| 542 | + | if (const auto d = n % sizeof(std::max_align_t); d > 0u) | ||||||
| 543 | + | n += (sizeof(std::max_align_t) - d); | ||||||
| 544 | + | |||||||
| 545 | + | auto mem = std::allocator_traits<allocator_type>:: | ||||||
| 546 | + | template rebind_traits<char>:: | ||||||
| 547 | + | allocate(allocator, n + sizeof(allocator_type)); | ||||||
| 548 | + | |||||||
| 549 | + | void* p = static_cast<char*>(mem) + n; | ||||||
| 550 | + | new (p) allocator_type(std::move(allocator)); | ||||||
| 551 | + | |||||||
| 552 | + | return mem; | ||||||
| 553 | + | } | ||||||
| 554 | + | void operator delete(void * ptr, std::size_t n) | ||||||
| 555 | + | { | ||||||
| 556 | + | if (const auto d = n % sizeof(std::max_align_t); d > 0u) | ||||||
| 557 | + | n += (sizeof(std::max_align_t) - d); | ||||||
| 558 | + | |||||||
| 559 | + | using allocator_type = std::allocator_traits<Allocator> | ||||||
| 560 | + | ::template rebind_alloc<char>; | ||||||
| 561 | + | auto allocator_p = reinterpret_cast<allocator_type*>( | ||||||
| 562 | + | static_cast<char*>(ptr) + n); | ||||||
| 563 | + | auto allocator = std::move(*allocator_p); | ||||||
| 564 | + | |||||||
| 565 | + | allocator_p->~allocator_type(); | ||||||
| 566 | + | allocator.deallocate(static_cast<char*>(ptr), n + sizeof(allocator_type)); | ||||||
| 567 | + | } | ||||||
| 568 | + | }; | ||||||
| 569 | + | |||||||
| 570 | + | template<> | ||||||
| 571 | + | struct boost_asio_promise_type_allocator_base<std::allocator<void>> | ||||||
| 572 | + | { | ||||||
| 573 | + | }; | ||||||
| 574 | + | |||||||
| 575 | + | template<typename Handler, Executor Ex, IoAwaitable Awaitable> | ||||||
| 576 | + | struct boost_asio_init_promise_type | ||||||
| 577 | + | : boost_asio_promise_type_allocator_base< | ||||||
| 578 | + | boost::asio::associated_allocator_t<Handler>> | ||||||
| 579 | + | { | ||||||
| 580 | + | using args_type = completion_tuple_for_io_awaitable<Awaitable>; | ||||||
| 581 | + | |||||||
| HITGNC | 582 | + | 4 | boost_asio_init_promise_type( | ||||
| 583 | + | boost_asio_init &, | ||||||
| 584 | + | Handler & h, | ||||||
| 585 | + | Ex & exec, | ||||||
| 586 | + | Awaitable &) | ||||||
| HITGNC | 587 | + | 4 | : handler(h), ex(exec) | ||||
| 588 | + | { | ||||||
| HITGNC | 589 | + | 4 | } | ||||
| 590 | + | |||||||
| 591 | + | Handler & handler; | ||||||
| 592 | + | Ex &ex; | ||||||
| 593 | + | |||||||
| HITGNC | 594 | + | 4 | void get_return_object() {} | ||||
| MISUNC | 595 | + | ✗ | void unhandled_exception() {throw;} | ||||
| MISUNC | 596 | + | ✗ | void return_void() {} | ||||
| 597 | + | |||||||
| HITGNC | 598 | + | 4 | std::suspend_never initial_suspend() noexcept {return {};} | ||||
| MISUNC | 599 | + | ✗ | std::suspend_never final_suspend() noexcept {return {};} | ||||
| 600 | + | |||||||
| 601 | + | struct completer | ||||||
| 602 | + | { | ||||||
| 603 | + | Handler handler; | ||||||
| 604 | + | Ex ex; | ||||||
| 605 | + | args_type args; | ||||||
| 606 | + | |||||||
| 607 | + | |||||||
| HITGNC | 608 | + | 4 | bool await_ready() const {return false;} | ||||
| 609 | + | |||||||
| HITGNC | 610 | + | 4 | auto await_suspend( | ||||
| 611 | + | std::coroutine_handle<boost_asio_init_promise_type> h) | ||||||
| 612 | + | { | ||||||
| HITGNC | 613 | + | 4 | auto handler_ = | ||||
| 614 | + | std::apply( | ||||||
| HITGNC | 615 | + | 8 | [&](auto ... args) | ||||
| 616 | + | { | ||||||
| HITGNC | 617 | + | 4 | return boost::asio::append(std::move(handler), | ||||
| HITGNC | 618 | + | 8 | std::move(args)...); | ||||
| 619 | + | }, | ||||||
| HITGNC | 620 | + | 4 | detail::decomposed_types(std::move(args))); | ||||
| 621 | + | |||||||
| HITGNC | 622 | + | 4 | auto exec = boost::asio::get_associated_immediate_executor( | ||||
| 623 | + | handler_, | ||||||
| HITGNC | 624 | + | 8 | asio_executor_adapter(std::move(ex))); | ||||
| HITGNC | 625 | + | 4 | h.destroy(); | ||||
| HITGNC | 626 | + | 4 | boost::asio::dispatch(exec, std::move(handler_)); | ||||
| HITGNC | 627 | + | 4 | } | ||||
| MISUNC | 628 | + | ✗ | void await_resume() const {} | ||||
| 629 | + | }; | ||||||
| 630 | + | |||||||
| HITGNC | 631 | + | 4 | completer yield_value(args_type value) | ||||
| 632 | + | { | ||||||
| HITGNC | 633 | + | 8 | return {std::move(handler), std::move(ex), std::move(value)}; | ||||
| HITGNC | 634 | + | 1 | } | ||||
| 635 | + | |||||||
| 636 | + | struct wrapper | ||||||
| 637 | + | { | ||||||
| 638 | + | Awaitable r; | ||||||
| 639 | + | const Ex &ex; | ||||||
| 640 | + | io_env env; | ||||||
| 641 | + | std::stop_source stop_src; | ||||||
| 642 | + | boost::asio::cancellation_slot cancel_slot; | ||||||
| 643 | + | |||||||
| 644 | + | continuation c; | ||||||
| 645 | + | |||||||
| HITGNC | 646 | + | 4 | wrapper(Awaitable && r, const Ex &ex) | ||||
| HITGNC | 647 | + | 4 | : r(std::move(r)), ex(ex) | ||||
| 648 | + | { | ||||||
| HITGNC | 649 | + | 4 | } | ||||
| 650 | + | |||||||
| HITGNC | 651 | + | 4 | bool await_ready() {return r.await_ready(); } | ||||
| 652 | + | |||||||
| HITGNC | 653 | + | 1 | auto await_suspend( | ||||
| 654 | + | std::coroutine_handle<boost_asio_init_promise_type> tr) | ||||||
| 655 | + | { | ||||||
| HITGNC | 656 | + | 1 | env.executor = ex; | ||||
| HITGNC | 657 | + | 1 | env.stop_token = stop_src.get_token(); | ||||
| 658 | + | cancel_slot = | ||||||
| HITGNC | 659 | + | 1 | boost::asio::get_associated_cancellation_slot(tr.promise().handler); | ||||
| 660 | + | |||||||
| HITGNC | 661 | + | 1 | if (cancel_slot.is_connected()) | ||||
| MISUNC | 662 | + | ✗ | cancel_slot.assign( | ||||
| MISUNC | 663 | + | ✗ | [this](boost::asio::cancellation_type ct) | ||||
| 664 | + | { | ||||||
| MISUNC | 665 | + | ✗ | if ((ct & boost::asio::cancellation_type::terminal) | ||||
| MISUNC | 666 | + | ✗ | != boost::asio::cancellation_type::none) | ||||
| MISUNC | 667 | + | ✗ | stop_src.request_stop(); | ||||
| 668 | + | }); | ||||||
| HITGNC | 669 | + | 1 | env.frame_allocator = get_current_frame_allocator(); | ||||
| 670 | + | |||||||
| 671 | + | using suspend_kind = decltype(r.await_suspend(tr, &env)); | ||||||
| 672 | + | if constexpr (std::is_void_v<suspend_kind>) | ||||||
| MISUNC | 673 | + | ✗ | r.await_suspend(tr, &env); | ||||
| 674 | + | else if constexpr (std::same_as<suspend_kind, bool>) | ||||||
| 675 | + | return r.await_suspend(tr, &env); | ||||||
| 676 | + | else | ||||||
| 677 | + | { | ||||||
| HITGNC | 678 | + | 1 | c.h = r.await_suspend(tr, &env); | ||||
| HITGNC | 679 | + | 1 | return ex.dispatch(c); | ||||
| 680 | + | } | ||||||
| MISUNC | 681 | + | ✗ | } | ||||
| 682 | + | |||||||
| HITGNC | 683 | + | 4 | completion_tuple_for_io_awaitable<Awaitable> await_resume() | ||||
| 684 | + | { | ||||||
| HITGNC | 685 | + | 4 | if (cancel_slot.is_connected()) | ||||
| MISUNC | 686 | + | ✗ | cancel_slot.clear(); | ||||
| 687 | + | |||||||
| 688 | + | using type = decltype(r.await_resume()); | ||||||
| 689 | + | if constexpr (!noexcept(r.await_resume())) | ||||||
| 690 | + | { | ||||||
| 691 | + | if constexpr (std::is_void_v<type>) | ||||||
| 692 | + | try | ||||||
| 693 | + | { | ||||||
| HITGNC | 694 | + | 1 | r.await_resume(); | ||||
| HITGNC | 695 | + | 1 | return {std::exception_ptr()}; | ||||
| 696 | + | } | ||||||
| MISUNC | 697 | + | ✗ | catch (...) | ||||
| 698 | + | { | ||||||
| MISUNC | 699 | + | ✗ | return std::current_exception(); | ||||
| 700 | + | } | ||||||
| 701 | + | else | ||||||
| 702 | + | try | ||||||
| 703 | + | { | ||||||
| 704 | + | return {std::current_exception(), r.await_resume()}; | ||||||
| 705 | + | } | ||||||
| 706 | + | catch (...) | ||||||
| 707 | + | { | ||||||
| 708 | + | return {std::current_exception(), type()}; | ||||||
| 709 | + | } | ||||||
| 710 | + | } | ||||||
| 711 | + | else | ||||||
| 712 | + | { | ||||||
| 713 | + | if constexpr (std::is_void_v<type>) | ||||||
| 714 | + | { | ||||||
| 715 | + | r.await_resume(); | ||||||
| 716 | + | return {}; | ||||||
| 717 | + | } | ||||||
| 718 | + | else | ||||||
| HITGNC | 719 | + | 3 | return {r.await_resume()}; | ||||
| 720 | + | } | ||||||
| 721 | + | } | ||||||
| 722 | + | }; | ||||||
| 723 | + | |||||||
| HITGNC | 724 | + | 4 | wrapper await_transform(Awaitable & r) | ||||
| 725 | + | { | ||||||
| HITGNC | 726 | + | 4 | return wrapper{std::move(r), ex}; | ||||
| 727 | + | } | ||||||
| 728 | + | }; | ||||||
| 729 | + | |||||||
| 730 | + | |||||||
| 731 | + | struct boost_asio_init | ||||||
| 732 | + | { | ||||||
| 733 | + | template<typename Handler, Executor Ex, IoAwaitable Awaitable> | ||||||
| HITGNC | 734 | + | 4 | void operator()( | ||||
| 735 | + | Handler , | ||||||
| 736 | + | Ex, | ||||||
| 737 | + | Awaitable awaitable) | ||||||
| 738 | + | { | ||||||
| 739 | + | auto res = co_await awaitable; | ||||||
| 740 | + | co_yield std::move(res); | ||||||
| HITGNC | 741 | + | 8 | } | ||||
| 742 | + | }; | ||||||
| 743 | + | |||||||
| 744 | + | template<typename Awaitable, typename Token> | ||||||
| 745 | + | requires | ||||||
| 746 | + | boost::asio::completion_token_for< | ||||||
| 747 | + | Token, | ||||||
| 748 | + | completion_signature_for_io_awaitable<Awaitable> | ||||||
| 749 | + | > | ||||||
| 750 | + | struct initialize_asio_spawn_helper<Awaitable, Token> | ||||||
| 751 | + | { | ||||||
| 752 | + | template<typename Executor> | ||||||
| HITGNC | 753 | + | 4 | static auto init(Executor ex, Awaitable r, Token && tk) | ||||
| 754 | + | -> decltype( boost::asio::async_initiate< | ||||||
| 755 | + | Token, | ||||||
| 756 | + | completion_signature_for_io_awaitable<Awaitable>>( | ||||||
| 757 | + | boost_asio_init{}, | ||||||
| 758 | + | tk, std::move(ex), std::move(r) | ||||||
| 759 | + | )) | ||||||
| 760 | + | { | ||||||
| 761 | + | return boost::asio::async_initiate< | ||||||
| 762 | + | Token, | ||||||
| HITGNC | 763 | + | 3 | completion_signature_for_io_awaitable<Awaitable>>( | ||||
| 764 | + | boost_asio_init{}, | ||||||
| HITGNC | 765 | + | 4 | tk, std::move(ex), std::move(r) | ||||
| HITGNC | 766 | + | 4 | ); | ||||
| 767 | + | } | ||||||
| 768 | + | }; | ||||||
| 769 | + | |||||||
| 770 | + | |||||||
| 771 | + | } | ||||||
| 772 | + | |||||||
| 773 | + | |||||||
| 774 | + | template<typename Handler, typename Executor, typename Awaitable> | ||||||
| 775 | + | struct std::coroutine_traits<void, | ||||||
| 776 | + | boost::capy::detail::boost_asio_init&, | ||||||
| 777 | + | Handler, | ||||||
| 778 | + | Executor, | ||||||
| 779 | + | Awaitable> | ||||||
| 780 | + | { | ||||||
| 781 | + | using promise_type | ||||||
| 782 | + | = boost::capy::detail::boost_asio_init_promise_type< | ||||||
| 783 | + | Handler, | ||||||
| 784 | + | Executor, | ||||||
| 785 | + | Awaitable>; | ||||||
| 786 | + | }; | ||||||
| 787 | + | |||||||
| 788 | + | |||||||
| 789 | + | |||||||
| 790 | + | namespace boost::capy | ||||||
| 791 | + | { | ||||||
| 792 | + | |||||||
| 793 | + | |||||||
| 794 | + | namespace detail | ||||||
| 795 | + | { | ||||||
| 796 | + | |||||||
| 797 | + | template<typename Sequence> | ||||||
| 798 | + | class asio_boost_buffer_range | ||||||
| 799 | + | { | ||||||
| 800 | + | public: | ||||||
| 801 | + | using sequence_type = Sequence; | ||||||
| 802 | + | using iterator = asio_buffer_iterator< | ||||||
| 803 | + | decltype(boost::asio::buffer_sequence_begin(std::declval<const Sequence&>()))>; | ||||||
| 804 | + | using const_iterator = iterator; | ||||||
| 805 | + | |||||||
| 806 | + | asio_boost_buffer_range() = default; | ||||||
| 807 | + | |||||||
| HITGNC | 808 | + | 3 | explicit asio_boost_buffer_range(Sequence seq) | ||||
| 809 | + | noexcept(std::is_nothrow_move_constructible_v<Sequence>) | ||||||
| HITGNC | 810 | + | 3 | : seq_(std::move(seq)) | ||||
| 811 | + | { | ||||||
| HITGNC | 812 | + | 3 | } | ||||
| 813 | + | |||||||
| 814 | + | asio_boost_buffer_range(const asio_boost_buffer_range&) = default; | ||||||
| 815 | + | asio_boost_buffer_range(asio_boost_buffer_range&&) = default; | ||||||
| 816 | + | asio_boost_buffer_range& operator=(const asio_boost_buffer_range&) = default; | ||||||
| 817 | + | asio_boost_buffer_range& operator=(asio_boost_buffer_range&&) = default; | ||||||
| 818 | + | |||||||
| HITGNC | 819 | + | 6 | iterator begin() const | ||||
| 820 | + | noexcept(noexcept(boost::asio::buffer_sequence_begin(std::declval<const Sequence&>()))) | ||||||
| 821 | + | { | ||||||
| HITGNC | 822 | + | 6 | return iterator(boost::asio::buffer_sequence_begin(seq_)); | ||||
| 823 | + | } | ||||||
| 824 | + | |||||||
| HITGNC | 825 | + | 6 | iterator end() const | ||||
| 826 | + | noexcept(noexcept(boost::asio::buffer_sequence_end(std::declval<const Sequence&>()))) | ||||||
| 827 | + | { | ||||||
| HITGNC | 828 | + | 6 | return iterator(boost::asio::buffer_sequence_end(seq_)); | ||||
| 829 | + | } | ||||||
| 830 | + | |||||||
| 831 | + | /// Returns the underlying sequence. | ||||||
| 832 | + | const Sequence& base() const noexcept { return seq_; } | ||||||
| 833 | + | |||||||
| 834 | + | private: | ||||||
| 835 | + | Sequence seq_{}; | ||||||
| 836 | + | }; | ||||||
| 837 | + | |||||||
| 838 | + | // Deduction guide | ||||||
| 839 | + | template<typename Sequence> | ||||||
| 840 | + | asio_boost_buffer_range(Sequence) -> asio_boost_buffer_range<Sequence>; | ||||||
| 841 | + | |||||||
| 842 | + | |||||||
| HITGNC | 843 | + | 3 | asio_mutable_buffer asio_buffer_transformer_t:: | ||||
| 844 | + | operator()(const boost::asio::mutable_buffer &mb) const noexcept | ||||||
| 845 | + | { | ||||||
| HITGNC | 846 | + | 3 | return {mb.data(), mb.size()}; | ||||
| 847 | + | } | ||||||
| 848 | + | |||||||
| HITGNC | 849 | + | 3 | asio_const_buffer asio_buffer_transformer_t:: | ||||
| 850 | + | operator()(const boost::asio::const_buffer &cb) const noexcept | ||||||
| 851 | + | { | ||||||
| HITGNC | 852 | + | 3 | return {cb.data(), cb.size()}; | ||||
| 853 | + | } | ||||||
| 854 | + | |||||||
| 855 | + | } | ||||||
| 856 | + | |||||||
| 857 | + | /** Convert a Boost.Asio buffer sequence for bidirectional Asio/capy compatibility. | ||||||
| 858 | + | |||||||
| 859 | + | Wraps a Boost.Asio buffer sequence in a transforming range that converts | ||||||
| 860 | + | each buffer element to asio_const_buffer or asio_mutable_buffer. | ||||||
| 861 | + | The returned range satisfies both Boost.Asio's buffer sequence requirements | ||||||
| 862 | + | and capy's buffer sequence concepts. | ||||||
| 863 | + | |||||||
| 864 | + | This overload uses `boost::asio::buffer_sequence_begin` and | ||||||
| 865 | + | `boost::asio::buffer_sequence_end` for iteration. | ||||||
| 866 | + | |||||||
| 867 | + | @par Example: Asio to Capy | ||||||
| 868 | + | @code | ||||||
| 869 | + | std::vector<boost::asio::mutable_buffer> asio_bufs = ...; | ||||||
| 870 | + | auto seq = capy::as_asio_buffer_sequence(asio_bufs); | ||||||
| 871 | + | std::size_t total = capy::buffer_size(seq); // Use capy algorithms | ||||||
| 872 | + | @endcode | ||||||
| 873 | + | |||||||
| 874 | + | @param seq The Boost.Asio buffer sequence to convert | ||||||
| 875 | + | @return A transforming range over the buffer sequence | ||||||
| 876 | + | |||||||
| 877 | + | @see as_asio_buffer_sequence in buffers.hpp for capy buffer sequences | ||||||
| 878 | + | */ | ||||||
| 879 | + | template<typename T> | ||||||
| 880 | + | requires requires (const T & seq) | ||||||
| 881 | + | { | ||||||
| 882 | + | {boost::asio::buffer_sequence_begin(seq)} -> std::bidirectional_iterator; | ||||||
| 883 | + | {boost::asio::buffer_sequence_end(seq)} -> std::bidirectional_iterator; | ||||||
| 884 | + | {*boost::asio::buffer_sequence_begin(seq)} -> std::convertible_to<boost::asio::const_buffer>; | ||||||
| 885 | + | {*boost::asio::buffer_sequence_end(seq)} -> std::convertible_to<boost::asio::const_buffer>; | ||||||
| 886 | + | } | ||||||
| HITGNC | 887 | + | 3 | auto as_asio_buffer_sequence(const T & seq) | ||||
| 888 | + | { | ||||||
| HITGNC | 889 | + | 3 | return detail::asio_boost_buffer_range(seq); | ||||
| 890 | + | } | ||||||
| 891 | + | |||||||
| HITGNC | 892 | + | 2 | asio_const_buffer::operator boost::asio::const_buffer() const | ||||
| 893 | + | { | ||||||
| HITGNC | 894 | + | 2 | return {data(), size()}; | ||||
| 895 | + | } | ||||||
| 896 | + | |||||||
| 897 | + | asio_mutable_buffer::operator boost::asio::const_buffer() const | ||||||
| 898 | + | { | ||||||
| 899 | + | return {data(), size()}; | ||||||
| 900 | + | } | ||||||
| 901 | + | |||||||
| HITGNC | 902 | + | 2 | asio_mutable_buffer::operator boost::asio::mutable_buffer() const | ||||
| 903 | + | { | ||||||
| HITGNC | 904 | + | 2 | return {data(), size()}; | ||||
| 905 | + | } | ||||||
| 906 | + | |||||||
| 907 | + | } | ||||||
| 908 | + | |||||||
| 909 | + | #endif //BOOST_CAPY_ASIO_BOOST_HPP | ||||||
| 910 | + | |||||||