Skip to content

Commit 3ede965

Browse files
committed
add the ability to co_await sender adaptor closure objects
1 parent 50efa1a commit 3ede965

11 files changed

Lines changed: 139 additions & 39 deletions

File tree

include/exec/task.hpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ namespace exec {
8888
__sticky
8989
};
9090

91-
struct __parent_promise_t { };
92-
9391
template <__scheduler_affinity _SchedulerAffinity = __scheduler_affinity::__sticky>
9492
class __default_task_context_impl {
9593
template <class _ParentPromise>
@@ -103,7 +101,7 @@ namespace exec {
103101

104102
public:
105103
template <class _ParentPromise>
106-
explicit __default_task_context_impl(__parent_promise_t, _ParentPromise& __parent) noexcept {
104+
explicit __default_task_context_impl(_ParentPromise& __parent) noexcept {
107105
if constexpr (_SchedulerAffinity == __scheduler_affinity::__sticky) {
108106
if constexpr (__check_parent_promise_has_scheduler<_ParentPromise>()) {
109107
__scheduler_ = get_scheduler(get_env(__parent));
@@ -281,6 +279,25 @@ namespace exec {
281279
__variant_for<__void, std::exception_ptr> __data_{};
282280
};
283281

282+
template <class _Sch>
283+
struct __just_void {
284+
using sender_concept = sender_t;
285+
using completion_signatures = completion_signatures<set_value_t()>;
286+
287+
template <class _Rcvr>
288+
[[nodiscard]]
289+
static constexpr auto connect(_Rcvr __rcvr) noexcept {
290+
return stdexec::connect(just(), static_cast<_Rcvr&&>(__rcvr));
291+
}
292+
293+
[[nodiscard]]
294+
constexpr auto get_env() const noexcept {
295+
return prop{get_completion_scheduler<set_value_t>, __sch_};
296+
}
297+
298+
_Sch __sch_;
299+
};
300+
284301
enum class disposition : unsigned {
285302
stopped,
286303
succeeded,
@@ -319,6 +336,8 @@ namespace exec {
319336
}
320337

321338
private:
339+
using __scheduler_t = __query_result_or_t<get_scheduler_t, _Context, inline_scheduler>;
340+
322341
struct __final_awaitable {
323342
static constexpr auto await_ready() noexcept -> bool {
324343
return false;
@@ -399,6 +418,11 @@ namespace exec {
399418
}
400419
#endif
401420

421+
template <__sender_adaptor_closure_for<__just_void<__scheduler_t>> _Closure>
422+
auto await_transform(_Closure&& __closure) noexcept -> decltype(auto) {
423+
return await_transform(static_cast<_Closure&&>(__closure)(__just_void<__scheduler_t>()));
424+
}
425+
402426
template <class _Awaitable>
403427
auto await_transform(_Awaitable&& __awaitable) noexcept -> decltype(auto) {
404428
return with_awaitable_senders<__promise>::await_transform(
@@ -431,7 +455,7 @@ namespace exec {
431455
auto await_suspend(__coro::coroutine_handle<_ParentPromise2> __parent) noexcept
432456
-> __coro::coroutine_handle<> {
433457
static_assert(__one_of<_ParentPromise, _ParentPromise2, void>);
434-
__coro_.promise().__context_.emplace(__parent_promise_t(), __parent.promise());
458+
__coro_.promise().__context_.emplace(__parent.promise());
435459
__context_.emplace(*__coro_.promise().__context_, __parent.promise());
436460
__coro_.promise().set_continuation(__parent);
437461
if constexpr (requires { __coro_.promise().stop_requested() ? 0 : 1; }) {

include/stdexec/__detail/__config.hpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -473,25 +473,7 @@ namespace stdexec {
473473
# define STDEXEC_EXPLICIT_THIS(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
474474
#endif
475475

476-
// Configure extra type checking
477-
#define STDEXEC_TYPE_CHECKING_ZERO() 0
478-
#define STDEXEC_TYPE_CHECKING_ONE() 1
479-
#define STDEXEC_TYPE_CHECKING_TWO() 2
480-
481-
#define STDEXEC_PROBE_TYPE_CHECKING_ STDEXEC_TYPE_CHECKING_ONE
482-
#define STDEXEC_PROBE_TYPE_CHECKING_0 STDEXEC_TYPE_CHECKING_ZERO
483-
#define STDEXEC_PROBE_TYPE_CHECKING_1 STDEXEC_TYPE_CHECKING_ONE
484-
#define STDEXEC_PROBE_TYPE_CHECKING_STDEXEC_ENABLE_EXTRA_TYPE_CHECKING STDEXEC_TYPE_CHECKING_TWO
485-
486-
#define STDEXEC_TYPE_CHECKING_WHICH3(...) STDEXEC_PROBE_TYPE_CHECKING_##__VA_ARGS__
487-
#define STDEXEC_TYPE_CHECKING_WHICH2(...) STDEXEC_TYPE_CHECKING_WHICH3(__VA_ARGS__)
488-
#define STDEXEC_TYPE_CHECKING_WHICH STDEXEC_TYPE_CHECKING_WHICH2(STDEXEC_ENABLE_EXTRA_TYPE_CHECKING)
489-
490-
#ifndef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
491-
# define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 0
492-
#elif STDEXEC_TYPE_CHECKING_WHICH() == 2
493-
// do nothing
494-
#elif STDEXEC_TYPE_CHECKING_WHICH() == 0
476+
#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING == 0
495477
# undef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
496478
# define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 0
497479
#else

include/stdexec/__detail/__env.hpp

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,13 @@ namespace stdexec {
246246

247247
struct __is_scheduler_affine_t {
248248
template <class _Env>
249-
constexpr auto operator()(const _Env&) const noexcept {
250-
if constexpr (tag_invocable<__is_scheduler_affine_t, const _Env&>) {
251-
using _Result = __decay_t<tag_invoke_result_t<__is_scheduler_affine_t, const _Env&>>;
252-
static_assert(__same_as<decltype(__v<_Result>), const bool>);
253-
return _Result();
249+
constexpr auto operator()(const _Env& __env) const noexcept {
250+
if constexpr (requires { _Env::query(*this); }) {
251+
return _Env::query(*this);
252+
} else if constexpr (requires { __env.query(*this); }) {
253+
return __env.query(*this);
254254
} else {
255-
return std::false_type();
255+
return false;
256256
}
257257
}
258258

@@ -410,6 +410,18 @@ namespace stdexec {
410410
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
411411
prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;
412412

413+
template <class _Query, auto _Value>
414+
struct cprop {
415+
using __t = cprop;
416+
using __id = cprop;
417+
418+
STDEXEC_ATTRIBUTE(nodiscard)
419+
420+
static constexpr auto query(_Query) noexcept {
421+
return _Value;
422+
}
423+
};
424+
413425
// utility for joining multiple environments
414426
template <class... _Envs>
415427
struct env {
@@ -432,6 +444,23 @@ namespace stdexec {
432444
template <class _Query, class... _Args>
433445
using __1st_env_t = decltype(env::__get_1st<_Query, _Args...>(__declval<const env&>()));
434446

447+
// NOT TO SPEC: a static query memfn for those envs that have a static query memfn.
448+
// This is useful for constexpr evaluation of queries.
449+
template <class _Query, class... _Args>
450+
requires(__queryable<_Envs, _Query, _Args...> || ...)
451+
STDEXEC_ATTRIBUTE(always_inline)
452+
static constexpr auto query(_Query __q, _Args&&... __args)
453+
noexcept(__nothrow_queryable<__1st_env_t<_Query, _Args...>, _Query, _Args...>)
454+
-> decltype(auto)
455+
requires requires {
456+
std::remove_reference_t<__1st_env_t<_Query, _Args...>>::query(
457+
__q, static_cast<_Args &&>(__args)...);
458+
}
459+
{
460+
return std::remove_reference_t<__1st_env_t<_Query, _Args...>>::query(
461+
__q, static_cast<_Args&&>(__args)...);
462+
}
463+
435464
template <class _Query, class... _Args>
436465
requires(__queryable<_Envs, _Query, _Args...> || ...)
437466
STDEXEC_ATTRIBUTE(always_inline)
@@ -467,6 +496,24 @@ namespace stdexec {
467496
template <class _Query, class... _Args>
468497
using __1st_env_t = decltype(env::__get_1st<_Query, _Args...>(__declval<const env&>()));
469498

499+
// NOT TO SPEC: a static query memfn for those envs that have a static query memfn.
500+
// This is useful for constexpr evaluation of queries.
501+
template <class _Query, class... _Args>
502+
requires __queryable<_Env0, _Query, _Args...>
503+
|| __queryable<_Env1, _Query, _Args...>
504+
STDEXEC_ATTRIBUTE(always_inline)
505+
static constexpr auto query(_Query __q, _Args&&... __args)
506+
noexcept(__nothrow_queryable<__1st_env_t<_Query, _Args...>, _Query, _Args...>)
507+
-> decltype(auto)
508+
requires requires {
509+
std::remove_reference_t<__1st_env_t<_Query, _Args...>>::query(
510+
__q, static_cast<_Args &&>(__args)...);
511+
}
512+
{
513+
return std::remove_reference_t<__1st_env_t<_Query, _Args...>>::query(
514+
__q, static_cast<_Args&&>(__args)...);
515+
}
516+
470517
template <class _Query, class... _Args>
471518
requires __queryable<_Env0, _Query, _Args...> || __queryable<_Env1, _Query, _Args...>
472519
STDEXEC_ATTRIBUTE(always_inline)
@@ -765,7 +812,7 @@ namespace stdexec {
765812

766813
template <class _Sender>
767814
concept __is_scheduler_affine = requires {
768-
requires __v<__call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>;
815+
requires env_of_t<_Sender>::query(__is_scheduler_affine_t{});
769816
};
770817
} // namespace stdexec
771818

include/stdexec/__detail/__execution_fwd.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,15 @@ namespace stdexec {
5454
template <class _Query, class _Value>
5555
struct prop;
5656

57+
template <class _Query, auto _Value>
58+
struct cprop;
59+
5760
template <class... _Envs>
5861
struct env;
5962
} // namespace __env
6063

6164
using __env::prop;
65+
using __env::cprop;
6266
using __env::env;
6367
using empty_env [[deprecated("stdexec::empty_env is now spelled stdexec::env<>")]] = env<>;
6468

include/stdexec/__detail/__just.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ namespace stdexec {
3434
struct __impl : __sexpr_defaults {
3535
using __tag_t = typename _JustTag::__tag_t;
3636

37-
static constexpr auto get_attrs = [](__ignore) noexcept {
38-
return prop{__is_scheduler_affine_t{}, std::true_type{}};
37+
static constexpr auto get_attrs =
38+
[](__ignore) noexcept -> cprop<__is_scheduler_affine_t, true> {
39+
return {};
3940
};
4041

4142
static constexpr auto get_completion_signatures =

include/stdexec/__detail/__let.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace stdexec {
4141
// A dummy scheduler that is used by the metaprogramming below when the input sender doesn't
4242
// have a completion scheduler.
4343
struct __unknown_scheduler {
44-
struct __env {
44+
struct __attrs {
4545
static constexpr auto query(__is_scheduler_affine_t) noexcept -> bool {
4646
return true;
4747
}
@@ -56,7 +56,7 @@ namespace stdexec {
5656
using sender_concept = sender_t;
5757

5858
[[nodiscard]]
59-
constexpr auto get_env() const noexcept -> __env {
59+
constexpr auto get_env() const noexcept -> __attrs {
6060
return {};
6161
}
6262
};

include/stdexec/__detail/__read_env.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ namespace stdexec {
8282
using __completions_t =
8383
__minvoke<__mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>, _Tag, _Env>;
8484

85-
static constexpr auto get_attrs = [](__ignore) noexcept {
86-
return prop{__is_scheduler_affine_t{}, std::true_type{}};
85+
static constexpr auto get_attrs =
86+
[](__ignore) noexcept -> cprop<__is_scheduler_affine_t, true> {
87+
return {};
8788
};
8889

8990
static constexpr auto get_completion_signatures =

include/stdexec/__detail/__tag_invoke.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ namespace stdexec {
4242
template <class _Tag, class _Env>
4343
requires true // so this overload is preferred over the one below
4444
STDEXEC_ATTRIBUTE(always_inline) constexpr auto tag_invoke(_Tag, const _Env&) noexcept
45-
-> __mconstant<_Env::query(_Tag())> {
46-
return {};
45+
-> decltype(_Env::query(_Tag())) {
46+
return _Env::query(_Tag());
4747
}
4848

4949
// For handling queryables with a query member function:

include/stdexec/__detail/__then.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace stdexec {
6262
struct __then_impl : __sexpr_defaults {
6363
static constexpr auto get_attrs = []<class _Child>(__ignore, const _Child& __child) noexcept {
6464
return __env::__join(
65-
prop{__is_scheduler_affine_t{}, __mbool<__is_scheduler_affine<_Child>>{}},
65+
cprop<__is_scheduler_affine_t, __is_scheduler_affine<_Child>>{},
6666
stdexec::get_env(__child));
6767
};
6868

test/exec/test_task.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,15 @@
2222
# include <exec/single_thread_context.hpp>
2323
# include <exec/async_scope.hpp>
2424

25+
# include <test_common/schedulers.hpp>
26+
2527
# include <catch2/catch.hpp>
2628

29+
# include <string>
30+
2731
using namespace exec;
2832
using namespace stdexec;
33+
using namespace std::string_literals;
2934

3035
namespace {
3136

@@ -252,6 +257,39 @@ namespace {
252257
CHECK(count == 3);
253258
}
254259

260+
struct test_domain {
261+
template <sender_expr_for<then_t> _Sender>
262+
static constexpr auto transform_sender(_Sender&& __sndr) noexcept {
263+
return just("goodbye"s);
264+
}
265+
};
266+
267+
struct test_task_context {
268+
constexpr test_task_context(auto&&...) noexcept {
269+
}
270+
271+
template <class _ThisPromise>
272+
using promise_context_t = test_task_context;
273+
274+
template <class, class>
275+
using awaiter_context_t = test_task_context;
276+
277+
static constexpr auto query(get_scheduler_t) noexcept {
278+
return basic_inline_scheduler<test_domain>{};
279+
}
280+
};
281+
282+
template <class T>
283+
using test_task = exec::basic_task<T, test_task_context>;
284+
285+
TEST_CASE("task - can co_await a sender adaptor closure object", "[types][task]") {
286+
auto salutation = []() -> test_task<std::string> {
287+
co_return co_await then([] { return "hello"s; });
288+
}();
289+
auto [msg] = stdexec::sync_wait(std::move(salutation)).value();
290+
CHECK(msg == "goodbye"s);
291+
}
292+
255293
# if !STDEXEC_STD_NO_EXCEPTIONS()
256294
TEST_CASE("task - can error early", "[types][task]") {
257295
int count = 0;

0 commit comments

Comments
 (0)