op.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //
  2. // Copyright (c) 2022 Klemens Morgenstern ([email protected])
  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. #ifndef BOOST_COBALT_OP_HPP
  8. #define BOOST_COBALT_OP_HPP
  9. #include <boost/cobalt/detail/handler.hpp>
  10. #include <boost/cobalt/detail/sbo_resource.hpp>
  11. #include <boost/cobalt/result.hpp>
  12. namespace boost::cobalt
  13. {
  14. template<typename ... Args>
  15. struct op
  16. {
  17. virtual void ready(cobalt::handler<Args...>) {};
  18. virtual void initiate(cobalt::completion_handler<Args...> complete) = 0 ;
  19. virtual ~op() = default;
  20. struct awaitable
  21. {
  22. op<Args...> &op_;
  23. std::optional<std::tuple<Args...>> result;
  24. awaitable(op<Args...> * op_) : op_(*op_) {}
  25. awaitable(awaitable && lhs)
  26. : op_(lhs.op_)
  27. , result(std::move(lhs.result))
  28. {
  29. }
  30. bool await_ready()
  31. {
  32. op_.ready(handler<Args...>(result));
  33. return result.has_value();
  34. }
  35. char buffer[BOOST_COBALT_SBO_BUFFER_SIZE];
  36. detail::sbo_resource resource{buffer, sizeof(buffer)};
  37. detail::completed_immediately_t completed_immediately = detail::completed_immediately_t::no;
  38. std::exception_ptr init_ep;
  39. template<typename Promise>
  40. bool await_suspend(std::coroutine_handle<Promise> h
  41. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  42. , const boost::source_location & loc = BOOST_CURRENT_LOCATION
  43. #endif
  44. ) noexcept
  45. {
  46. try
  47. {
  48. completed_immediately = detail::completed_immediately_t::initiating;
  49. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  50. op_.initiate(completion_handler<Args...>{h, result, &resource, &completed_immediately, loc});
  51. #else
  52. op_.initiate(completion_handler<Args...>{h, result, &resource, &completed_immediately});
  53. #endif
  54. if (completed_immediately == detail::completed_immediately_t::initiating)
  55. completed_immediately = detail::completed_immediately_t::no;
  56. return completed_immediately != detail::completed_immediately_t::yes;
  57. }
  58. catch(...)
  59. {
  60. init_ep = std::current_exception();
  61. return false;
  62. }
  63. }
  64. auto await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION)
  65. {
  66. if (init_ep)
  67. std::rethrow_exception(init_ep);
  68. return await_resume(as_result_tag{}).value(loc);
  69. }
  70. auto await_resume(const struct as_tuple_tag &)
  71. {
  72. if (init_ep)
  73. std::rethrow_exception(init_ep);
  74. return *std::move(result);
  75. }
  76. auto await_resume(const struct as_result_tag &)
  77. {
  78. if (init_ep)
  79. std::rethrow_exception(init_ep);
  80. return interpret_as_result(*std::move(result));
  81. }
  82. };
  83. awaitable operator co_await() &&
  84. {
  85. return awaitable{this};
  86. }
  87. };
  88. struct use_op_t
  89. {
  90. /// Default constructor.
  91. constexpr use_op_t()
  92. {
  93. }
  94. /// Adapts an executor to add the @c use_op_t completion token as the
  95. /// default.
  96. template <typename InnerExecutor>
  97. struct executor_with_default : InnerExecutor
  98. {
  99. /// Specify @c use_op_t as the default completion token type.
  100. typedef use_op_t default_completion_token_type;
  101. executor_with_default(const InnerExecutor& ex) noexcept
  102. : InnerExecutor(ex)
  103. {
  104. }
  105. /// Construct the adapted executor from the inner executor type.
  106. template <typename InnerExecutor1>
  107. executor_with_default(const InnerExecutor1& ex,
  108. typename std::enable_if<
  109. std::conditional<
  110. !std::is_same<InnerExecutor1, executor_with_default>::value,
  111. std::is_convertible<InnerExecutor1, InnerExecutor>,
  112. std::false_type
  113. >::type::value>::type = 0) noexcept
  114. : InnerExecutor(ex)
  115. {
  116. }
  117. };
  118. /// Type alias to adapt an I/O object to use @c use_op_t as its
  119. /// default completion token type.
  120. template <typename T>
  121. using as_default_on_t = typename T::template rebind_executor<
  122. executor_with_default<typename T::executor_type> >::other;
  123. /// Function helper to adapt an I/O object to use @c use_op_t as its
  124. /// default completion token type.
  125. template <typename T>
  126. static typename std::decay_t<T>::template rebind_executor<
  127. executor_with_default<typename std::decay_t<T>::executor_type>
  128. >::other
  129. as_default_on(T && object)
  130. {
  131. return typename std::decay_t<T>::template rebind_executor<
  132. executor_with_default<typename std::decay_t<T>::executor_type>
  133. >::other(std::forward<T>(object));
  134. }
  135. };
  136. constexpr use_op_t use_op{};
  137. }
  138. namespace boost::asio
  139. {
  140. template<typename ... Args>
  141. struct async_result<boost::cobalt::use_op_t, void(Args...)>
  142. {
  143. using return_type = boost::cobalt::op<Args...>;
  144. template <typename Initiation, typename... InitArgs>
  145. struct op_impl final : boost::cobalt::op<Args...>
  146. {
  147. Initiation initiation;
  148. std::tuple<InitArgs...> args;
  149. template<typename Initiation_, typename ...InitArgs_>
  150. op_impl(Initiation_ initiation,
  151. InitArgs_ && ... args)
  152. : initiation(std::forward<Initiation_>(initiation))
  153. , args(std::forward<InitArgs_>(args)...) {}
  154. void initiate(cobalt::completion_handler<Args...> complete) final override
  155. {
  156. std::apply(
  157. [&](InitArgs && ... args)
  158. {
  159. std::move(initiation)(std::move(complete),
  160. std::move(args)...);
  161. }, std::move(args));
  162. }
  163. };
  164. template <typename Initiation, typename... InitArgs>
  165. static auto initiate(Initiation && initiation,
  166. boost::cobalt::use_op_t,
  167. InitArgs &&... args)
  168. -> op_impl<std::decay_t<Initiation>, std::decay_t<InitArgs>...>
  169. {
  170. return op_impl<std::decay_t<Initiation>, std::decay_t<InitArgs>...>(
  171. std::forward<Initiation>(initiation),
  172. std::forward<InitArgs>(args)...);
  173. }
  174. };
  175. }
  176. #endif //BOOST_COBALT_OP_HPP