// // 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_TASK_HPP #define BOOST_COBALT_TASK_HPP #include <boost/cobalt/detail/handler.hpp> #include <boost/cobalt/detail/task.hpp> #include <boost/cobalt/op.hpp> #include <boost/asio/append.hpp> #include <boost/asio/deferred.hpp> namespace boost::cobalt { // tag::outline[] template<typename Return> struct [[nodiscard]] task { task(task &&lhs) noexcept = default; task& operator=(task &&) noexcept = default; // enable `co_await` auto operator co_await (); // end::outline[] task(const task &) = delete; task& operator=(const task &) = delete; using promise_type = detail::task_promise<Return>; constexpr task(noop<Return> n) : receiver_(std::move(n)){} private: template<typename> friend struct detail::task_promise; task(detail::task_promise<Return> * task) : receiver_(task) { } detail::task_receiver<Return> receiver_; friend struct detail::async_initiate_spawn; // tag::outline[] }; // end::outline[] struct use_task_t { /// Default constructor. constexpr use_task_t() { } /// Adapts an executor to add the @c use_task_t completion token as the /// default. template <typename InnerExecutor> struct executor_with_default : InnerExecutor { /// Specify @c use_task_t as the default completion token type. typedef use_task_t default_completion_token_type; executor_with_default(const InnerExecutor& ex) noexcept : InnerExecutor(ex) { } /// Construct the adapted executor from the inner executor type. template <typename InnerExecutor1> executor_with_default(const InnerExecutor1& ex, typename std::enable_if< std::conditional< !std::is_same<InnerExecutor1, executor_with_default>::value, std::is_convertible<InnerExecutor1, InnerExecutor>, std::false_type >::type::value>::type = 0) noexcept : InnerExecutor(ex) { } }; /// Type alias to adapt an I/O object to use @c use_task_t as its /// default completion token type. template <typename T> using as_default_on_t = typename T::template rebind_executor< executor_with_default<typename T::executor_type> >::other; /// Function helper to adapt an I/O object to use @c use_task_t as its /// default completion token type. template <typename T> static typename std::decay_t<T>::template rebind_executor< executor_with_default<typename std::decay_t<T>::executor_type> >::other as_default_on(T && object) { return typename std::decay_t<T>::template rebind_executor< executor_with_default<typename std::decay_t<T>::executor_type> >::other(std::forward<T>(object)); } }; constexpr use_task_t use_task{}; template<typename T> inline auto task<T>::operator co_await () {return receiver_.get_awaitable();} } namespace boost::asio { template<typename ... Args> struct async_result<boost::cobalt::use_task_t, void(Args...)> { using return_type = cobalt::task< typename decltype(cobalt::interpret_as_result(std::declval<std::tuple<Args...>>()))::value_type>; template <typename Initiation, typename... InitArgs> static auto initiate(Initiation initiation, boost::cobalt::use_task_t, InitArgs ... args) -> return_type { co_return co_await async_initiate< const cobalt::use_op_t&, void(Args...)>( std::move(initiation), cobalt::use_op, std::move(args)...); } }; } #endif //BOOST_COBALT_COBALT_HPP