Skip to content

Commit 799d131

Browse files
authored
Merge pull request #1894 from ericniebler/get-await-completion-adaptor
add the `get_await_completion_adaptor` query and use it in `as_awaitable`
2 parents e81bceb + ca0ac08 commit 799d131

3 files changed

Lines changed: 80 additions & 30 deletions

File tree

include/stdexec/__detail/__as_awaitable.hpp

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
#include "__concepts.hpp"
2323
#include "__connect.hpp"
2424
#include "__meta.hpp"
25+
#include "__queries.hpp"
2526
#include "__tag_invoke.hpp"
2627
#include "__type_traits.hpp"
2728

2829
#include <exception>
30+
#include <functional> // for std::identity
2931
#include <system_error>
3032
#include <variant>
3133

@@ -49,6 +51,23 @@ namespace STDEXEC
4951
template <class _Sender, class _Promise>
5052
using __value_t = __decay_t<
5153
__value_types_of_t<_Sender, env_of_t<_Promise&>, __q<__single_value>, __msingle_or<void>>>;
54+
55+
inline constexpr auto __get_await_completion_adaptor =
56+
__with_default{get_await_completion_adaptor, std::identity{}};
57+
58+
template <class _Sender>
59+
using __adapt_completion_t = __result_of<__get_await_completion_adaptor, env_of_t<_Sender>>;
60+
61+
template <class _Sender>
62+
constexpr auto __adapt_sender_for_await(_Sender&& __sndr)
63+
noexcept(__nothrow_callable<__adapt_completion_t<_Sender>, _Sender>) -> decltype(auto)
64+
{
65+
return __get_await_completion_adaptor(get_env(__sndr))(static_cast<_Sender&&>(__sndr));
66+
}
67+
68+
template <class _Sender>
69+
using __adapted_sender_t =
70+
__remove_rvalue_reference_t<__call_result_t<__adapt_completion_t<_Sender>, _Sender>>;
5271
} // namespace __detail
5372

5473
/////////////////////////////////////////////////////////////////////////////
@@ -185,14 +204,23 @@ namespace STDEXEC
185204
};
186205

187206
template <class _Sender, class _Promise>
188-
concept __awaitable_sender = sender_in<_Sender, env_of_t<_Promise&>>
189-
&& __minvocable_q<__detail::__value_t, _Sender, _Promise>
190-
&& sender_to<_Sender, __receiver_t<_Sender, _Promise>>
191-
&& requires(_Promise& __promise) {
192-
{
193-
__promise.unhandled_stopped()
194-
} -> __std::convertible_to<__std::coroutine_handle<>>;
195-
};
207+
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
208+
__sender_awaitable(_Sender&&, __std::coroutine_handle<_Promise>)
209+
-> __sender_awaitable<_Promise, _Sender>;
210+
211+
template <class _Sender, class _Promise>
212+
concept __awaitable_adapted_sender = sender_in<_Sender, env_of_t<_Promise&>>
213+
&& __minvocable_q<__detail::__value_t, _Sender, _Promise>
214+
&& sender_to<_Sender, __receiver_t<_Sender, _Promise>>
215+
&& requires(_Promise& __promise) {
216+
{
217+
__promise.unhandled_stopped()
218+
} -> __std::convertible_to<__std::coroutine_handle<>>;
219+
};
220+
221+
template <class _Sender, class _Promise>
222+
concept __awaitable_sender =
223+
__awaitable_adapted_sender<__detail::__adapted_sender_t<_Sender>, _Promise>;
196224

197225
struct __unspecified
198226
{
@@ -214,32 +242,33 @@ namespace STDEXEC
214242
template <class _Tp, class _Promise>
215243
static consteval auto __get_declfn() noexcept
216244
{
217-
using __as_awaitable::__unspecified;
245+
using namespace __as_awaitable;
218246
if constexpr (__connect_await::__has_as_awaitable_member<_Tp, _Promise>)
219247
{
220248
using __result_t = decltype(__declval<_Tp>().as_awaitable(__declval<_Promise&>()));
221249
constexpr bool __is_nothrow = noexcept(
222250
__declval<_Tp>().as_awaitable(__declval<_Promise&>()));
223251
return __declfn<__result_t, __is_nothrow>();
224-
// NOLINTNEXTLINE(bugprone-branch-clone)
225252
}
226-
else if constexpr (__awaitable<_Tp, __unspecified>)
227-
{ // NOT __awaitable<_Tp, _Promise> !!
253+
else if constexpr (__awaitable<_Tp, __unspecified>) // NOT __awaitable<_Tp, _Promise> !!
254+
{ // NOLINT(bugprone-branch-clone)
228255
return __declfn<_Tp&&>();
229256
}
230-
else if constexpr (__as_awaitable::__awaitable_sender<_Tp, _Promise>)
257+
else if constexpr (__awaitable_sender<_Tp, _Promise>)
231258
{
232-
using __result_t = __as_awaitable::__sender_awaitable<_Promise, _Tp>;
233-
constexpr bool __is_nothrow =
234-
__nothrow_constructible_from<__result_t, _Tp, __std::coroutine_handle<_Promise>>;
259+
using __result_t = decltype( //
260+
__sender_awaitable{__detail::__adapt_sender_for_await(__declval<_Tp>()),
261+
__std::coroutine_handle<_Promise>()});
262+
constexpr bool __is_nothrow = noexcept(
263+
__sender_awaitable{__detail::__adapt_sender_for_await(__declval<_Tp>()),
264+
__std::coroutine_handle<_Promise>()});
235265
return __declfn<__result_t, __is_nothrow>();
236-
// NOT TO SPEC
237266
}
238-
else if constexpr (__as_awaitable::__incompatible_sender<_Tp, _Promise>)
267+
else if constexpr (__incompatible_sender<_Tp, _Promise>)
239268
{
240-
// It's a sender, but it isn't a sender in the current promise's environment, so
241-
// we can return the error type that results from trying to compute the sender's
242-
// value type:
269+
// NOT TO SPEC: It's a sender, but it isn't a sender in the current promise's
270+
// environment, so we can return the error type that results from trying to
271+
// compute the sender's value type:
243272
return __declfn<__detail::__value_t<_Tp, _Promise>>();
244273
}
245274
else
@@ -253,24 +282,24 @@ namespace STDEXEC
253282
auto operator()(_Tp&& __t, _Promise& __promise) const noexcept(noexcept(_DeclFn()))
254283
-> decltype(_DeclFn())
255284
{
256-
using __as_awaitable::__unspecified;
285+
using namespace __as_awaitable;
257286
if constexpr (__connect_await::__has_as_awaitable_member<_Tp, _Promise>)
258287
{
259288
using __result_t = decltype(static_cast<_Tp&&>(__t).as_awaitable(__promise));
260289
static_assert(__awaitable<__result_t, _Promise>);
261290
return static_cast<_Tp&&>(__t).as_awaitable(__promise);
262-
// NOLINTNEXTLINE(bugprone-branch-clone)
263291
}
264-
else if constexpr (__awaitable<_Tp, __unspecified>)
265-
{ // NOT __awaitable<_Tp, _Promise> !!
292+
else if constexpr (__awaitable<_Tp, __unspecified>) // NOT __awaitable<_Tp, _Promise> !!
293+
{ // NOLINT(bugprone-branch-clone)
266294
return static_cast<_Tp&&>(__t);
267295
}
268-
else if constexpr (__as_awaitable::__awaitable_sender<_Tp, _Promise>)
296+
else if constexpr (__awaitable_sender<_Tp, _Promise>)
269297
{
270298
auto __hcoro = __std::coroutine_handle<_Promise>::from_promise(__promise);
271-
return __as_awaitable::__sender_awaitable<_Promise, _Tp>{static_cast<_Tp&&>(__t), __hcoro};
299+
return __sender_awaitable{__detail::__adapt_sender_for_await(static_cast<_Tp&&>(__t)),
300+
__hcoro};
272301
}
273-
else if constexpr (__as_awaitable::__incompatible_sender<_Tp, _Promise>)
302+
else if constexpr (__incompatible_sender<_Tp, _Promise>)
274303
{
275304
return __detail::__value_t<_Tp, _Promise>();
276305
}

include/stdexec/__detail/__execution_fwd.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ namespace STDEXEC
117117
template <__completion_tag _CPO>
118118
struct get_completion_behavior_t;
119119
struct get_domain_t;
120+
struct get_await_completion_adaptor_t;
120121

121122
struct __debug_env_t;
122123

@@ -127,8 +128,9 @@ namespace STDEXEC
127128
template <__completion_tag _CPO>
128129
extern get_completion_scheduler_t<_CPO> const get_completion_scheduler;
129130
template <class _CPO = void>
130-
extern get_completion_domain_t<_CPO> const get_completion_domain;
131-
extern get_domain_t const get_domain;
131+
extern get_completion_domain_t<_CPO> const get_completion_domain;
132+
extern get_domain_t const get_domain;
133+
extern get_await_completion_adaptor_t const get_await_completion_adaptor;
132134

133135
template <class _Env>
134136
concept __is_debug_env = __callable<__debug_env_t, _Env>;

include/stdexec/__detail/__queries.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ namespace STDEXEC
2828
//////////////////////////////////////////////////////////////////////////////////////////////////
2929
// [exec.queries]
3030

31+
// [exec.get.await.adapt], see https://eel.is/c++draft/exec#get.await.adapt
32+
struct get_await_completion_adaptor_t : __query<get_await_completion_adaptor_t>
33+
{
34+
template <class _Env>
35+
STDEXEC_ATTRIBUTE(always_inline, host, device)
36+
static constexpr void __validate() noexcept
37+
{
38+
static_assert(STDEXEC::__nothrow_callable<get_await_completion_adaptor_t, _Env const &>);
39+
}
40+
41+
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
42+
static consteval auto query(forwarding_query_t) noexcept -> bool
43+
{
44+
return true;
45+
}
46+
};
47+
48+
inline constexpr get_await_completion_adaptor_t get_await_completion_adaptor{};
49+
3150
// NOT TO SPEC:
3251
struct __is_scheduler_affine_t
3352
{

0 commit comments

Comments
 (0)