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

source code of boost/libs/cobalt/include/boost/cobalt/op.hpp