// // Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_COBALT_OP_HPP #define BOOST_COBALT_OP_HPP #include #include #include #include #include namespace boost::cobalt { template struct op { virtual void ready(cobalt::handler) {}; virtual void initiate(cobalt::completion_handler complete) = 0 ; virtual ~op() = default; struct awaitable { op &op_; std::optional> result; awaitable(op * op_) : op_(*op_) {} awaitable(awaitable && lhs) : op_(lhs.op_) , result(std::move(lhs.result)) { } bool await_ready() { op_.ready(handler(result)); return result.has_value(); } char buffer[BOOST_COBALT_SBO_BUFFER_SIZE]; detail::sbo_resource resource{buffer, sizeof(buffer)}; detail::completed_immediately_t completed_immediately = detail::completed_immediately_t::no; std::exception_ptr init_ep; template bool await_suspend(std::coroutine_handle h #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) , const boost::source_location & loc = BOOST_CURRENT_LOCATION #endif ) noexcept { BOOST_TRY { completed_immediately = detail::completed_immediately_t::initiating; #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) op_.initiate(completion_handler{h, result, &resource, &completed_immediately, loc}); #else op_.initiate(completion_handler{h, result, &resource, &completed_immediately}); #endif if (completed_immediately == detail::completed_immediately_t::initiating) completed_immediately = detail::completed_immediately_t::no; return completed_immediately != detail::completed_immediately_t::yes; } BOOST_CATCH(...) { init_ep = std::current_exception(); return false; } BOOST_CATCH_END } auto await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION) { if (init_ep) std::rethrow_exception(init_ep); return await_resume(as_result_tag{}).value(loc); } auto await_resume(const struct as_tuple_tag &) { if (init_ep) std::rethrow_exception(init_ep); return *std::move(result); } auto await_resume(const struct as_result_tag &) { if (init_ep) std::rethrow_exception(init_ep); return interpret_as_result(*std::move(result)); } }; awaitable operator co_await() && { return awaitable{this}; } }; struct use_op_t { /// Default constructor. constexpr use_op_t() { } /// Adapts an executor to add the @c use_op_t completion token as the /// default. template struct executor_with_default : InnerExecutor { /// Specify @c use_op_t as the default completion token type. typedef use_op_t default_completion_token_type; executor_with_default(const InnerExecutor& ex) noexcept : InnerExecutor(ex) { } /// Construct the adapted executor from the inner executor type. template executor_with_default(const InnerExecutor1& ex, typename std::enable_if< std::conditional< !std::is_same::value, std::is_convertible, std::false_type >::type::value>::type = 0) noexcept : InnerExecutor(ex) { } }; /// Type alias to adapt an I/O object to use @c use_op_t as its /// default completion token type. template using as_default_on_t = typename T::template rebind_executor< executor_with_default >::other; /// Function helper to adapt an I/O object to use @c use_op_t as its /// default completion token type. template static typename std::decay_t::template rebind_executor< executor_with_default::executor_type> >::other as_default_on(T && object) { return typename std::decay_t::template rebind_executor< executor_with_default::executor_type> >::other(std::forward(object)); } }; constexpr use_op_t use_op{}; struct enable_await_deferred { template auto await_transform(asio::deferred_async_operation op_) { struct deferred_op : op { asio::deferred_async_operation op_; deferred_op(asio::deferred_async_operation op_) : op_(std::move(op_)) {} void initiate(cobalt::completion_handler complete) override { std::move(op_)(std::move(complete)); } }; return deferred_op{std::move(op_)}; } }; } namespace boost::asio { template struct async_result { using return_type = boost::cobalt::op; template struct op_impl final : boost::cobalt::op { Initiation initiation; std::tuple args; template op_impl(Initiation_ initiation, InitArgs_ && ... args) : initiation(std::forward(initiation)) , args(std::forward(args)...) {} void initiate(cobalt::completion_handler complete) final override { std::apply( [&](InitArgs && ... args) { std::move(initiation)(std::move(complete), std::move(args)...); }, std::move(args)); } }; template static auto initiate(Initiation && initiation, boost::cobalt::use_op_t, InitArgs &&... args) -> op_impl, std::decay_t...> { return op_impl, std::decay_t...>( std::forward(initiation), std::forward(args)...); } }; } #endif //BOOST_COBALT_OP_HPP