Skip to content

Commit 0363f10

Browse files
committed
add __start_sequential
1 parent d06fdef commit 0363f10

File tree

2 files changed

+141
-86
lines changed

2 files changed

+141
-86
lines changed

include/exec/tail_sender.hpp

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ namespace exec {
209209
};
210210

211211
template<tail_sender _TailSender>
212+
requires (!same_as<__null_tail_sender, _TailSender>)
212213
struct maybe_tail_sender {
213214
maybe_tail_sender() noexcept = default;
214215
maybe_tail_sender(__null_tail_sender) noexcept {}
@@ -313,96 +314,124 @@ namespace exec {
313314
bool valid_;
314315
};
315316

316-
template<tail_sender _TailSender, tail_receiver _TailReceiver>
317-
auto __start_until_nullable(_TailSender __t, _TailReceiver __r) {
318-
if constexpr (__nullable_tail_sender_to<_TailSender, _TailReceiver>) {
319-
return __t;
320-
} else if constexpr (__terminal_tail_sender_to<_TailSender, _TailReceiver>) {
321-
// restrict scope of op
322-
{
323-
auto op = stdexec::connect(std::move(__t), std::move(__r));
324-
stdexec::start(op);
317+
namespace __start_until_nullable_ {
318+
319+
struct __start_until_nullable_t;
320+
321+
template<tail_sender _TailSender, tail_receiver _TailReceiver>
322+
struct __start_until_nullable_result;
323+
324+
template<class _TailSender, class _TailReceiver>
325+
using __start_until_nullable_result_t = typename __start_until_nullable_result<_TailSender, _TailReceiver>::type;
326+
327+
template<tail_sender _TailSender, tail_receiver _TailReceiver>
328+
struct __start_until_nullable_result {
329+
using type =
330+
__if<
331+
__bool<__nullable_tail_sender_to<_TailSender, _TailReceiver>>, _TailSender,
332+
__if<
333+
__bool<__terminal_tail_sender_to<_TailSender, _TailReceiver>>, __null_tail_sender,
334+
__minvoke<__with_default<__q<__start_until_nullable_result_t>, __null_tail_sender>,
335+
__next_tail_from_sender_to_t<_TailSender, _TailReceiver>,
336+
_TailReceiver>
337+
>
338+
>;
339+
};
340+
341+
struct __start_until_nullable_t {
342+
template<tail_sender _TailSender, tail_receiver _TailReceiver>
343+
auto operator()(_TailSender __t, _TailReceiver __r) const noexcept
344+
-> __start_until_nullable_result_t<_TailSender, _TailReceiver> {
345+
if constexpr (__nullable_tail_sender_to<_TailSender, _TailReceiver>) {
346+
return __t;
347+
} else if constexpr (__terminal_tail_sender_to<_TailSender, _TailReceiver>) {
348+
// restrict scope of op
349+
{
350+
auto op = stdexec::connect(std::move(__t), std::move(__r));
351+
stdexec::start(op);
352+
}
353+
return __null_tail_sender{};
354+
} else {
355+
auto op = stdexec::connect(std::move(__t), __r);
356+
return __start_until_nullable_t{}(stdexec::start(op), std::move(__r));
357+
}
325358
}
326-
return __null_tail_sender{};
327-
} else {
328-
auto op = stdexec::connect(std::move(__t), __r);
329-
return __start_until_nullable(stdexec::start(op), std::move(__r));
330-
}
331-
}
359+
};
360+
361+
} // namespace __start_until_nullable_
362+
using __start_until_nullable_::__start_until_nullable_t;
363+
inline constexpr __start_until_nullable_t __start_until_nullable{};
332364

333-
#if 0
334-
template <class _Next, class _TailSender, class _TailReceiver, class... _Prev>
335-
auto __start_next(_Next next, _TailReceiver r) {
336-
if constexpr (__one_of<_Next, _TailSender, _Prev...>) {
365+
template <tail_sender _NextTailSender, tail_sender _TailSender, tail_receiver _TailReceiver, tail_sender... _PrevTailSenders>
366+
auto __start_next(_NextTailSender __next, _TailReceiver __r) {
367+
if constexpr (__one_of<_NextTailSender, _TailSender, _PrevTailSenders...>) {
337368
static_assert(
338-
(nullable_tail_sender_to<_TailSender, _TailReceiver> ||
339-
(nullable_tail_sender_to<_Prev, _TailReceiver> || ...)),
369+
(__nullable_tail_sender_to<_TailSender, _TailReceiver> ||
370+
(__nullable_tail_sender_to<_PrevTailSenders, _TailReceiver> || ...)),
340371
"At least one tail_sender in a cycle must be nullable to avoid "
341372
"entering an infinite loop");
342-
return __start_until_nullable(next, std::move(r));
373+
return __start_until_nullable(__next, std::move(__r));
343374
} else {
344375
using result_type =
345-
decltype(__start_sequential(next, r, type_list<_TailSender, _Prev...>{}));
346-
if constexpr (same_as<result_type, _Next>) {
376+
decltype(__start_sequential<_NextTailSender, _TailReceiver, _TailSender, _PrevTailSenders...>(__next, __r));
377+
if constexpr (same_as<result_type, _NextTailSender>) {
347378
// Let the loop in resume_tail_sender() handle checking the boolean.
348-
return next;
379+
return __next;
349380
} else {
350-
return __start_sequential(next, std::move(r), type_list<_TailSender, _Prev...>{});
381+
return __start_sequential<_NextTailSender, _TailReceiver, _TailSender, _PrevTailSenders...>(__next, std::move(__r));
351382
}
352383
}
353384
}
354385

355-
template<tail_sender _TailSender, tail_receiver _TailReceiver, class... _Prev>
356-
auto _start_sequential(_TailSender c, _TailReceiver r, type_list<_Prev...>) {
357-
static_assert(
358-
_tail_sender<_TailSender>, "_start_sequential: must be called with a tail_sender");
359-
if constexpr (_terminal_tail_sender_to<_TailSender, _TailReceiver>) {
360-
if constexpr (nullable_tail_sender_to<_TailSender, _TailReceiver>) {
386+
template<tail_sender _TailSender, tail_receiver _TailReceiver, class... _PrevTailSenders>
387+
auto __start_sequential(_TailSender c, _TailReceiver r) {
388+
if constexpr (__terminal_tail_sender_to<_TailSender, _TailReceiver>) {
389+
if constexpr (__nullable_tail_sender_to<_TailSender, _TailReceiver>) {
361390
return c;
362391
} else {
363392
// restrict scope of op
364393
{
365-
auto op = unifex::connect(std::move(c), std::move(r));
366-
unifex::start(op);
394+
auto op = stdexec::connect(std::move(c), std::move(r));
395+
stdexec::start(op);
367396
}
368-
return null_tail_sender{};
397+
return __null_tail_sender{};
369398
}
370399
} else {
371-
using next_t = next_tail_sender_to_t<_TailSender, _TailReceiver>;
372-
using result_type = decltype(_start_next<next_t, _TailSender, _TailReceiver, _Prev...>(
400+
using next_t = __next_tail_from_sender_to_t<_TailSender, _TailReceiver>;
401+
using result_type = decltype(__start_next<next_t, _TailSender, _TailReceiver, _PrevTailSenders...>(
373402
std::declval<next_t>(), r));
374-
if constexpr (std::is_void_v<next_t>) {
375-
// restrict scope of op
376-
{
377-
auto op = unifex::connect(std::move(c), std::move(r));
378-
unifex::start(op);
403+
if constexpr (same_as<result_type, next_t>) {
404+
static_assert(__nullable_tail_sender_to<_TailSender, _TailReceiver>, "recursing tail_sender must be nullable");
405+
auto op = stdexec::connect(std::move(c), std::move(r));
406+
// using result_type = variant_tail_sender<
407+
// __null_tail_sender,
408+
// next_t>;
409+
if (!op) {
410+
return //result_type{
411+
next_t{};//__null_tail_sender{}};
379412
}
380-
return null_tail_sender{};
381-
} else if constexpr (same_as<result_type, next_t>) {
382-
auto op = unifex::connect(std::move(c), std::move(r));
383-
return unifex::start(op);
384-
} else if constexpr (nullable_tail_sender_to<_TailSender, _TailReceiver>) {
385-
auto op = unifex::connect(std::move(c), r);
386-
using result_type = variant_tail_sender<
387-
null_tail_sender,
388-
decltype(_start_next<next_t, _TailSender, _TailReceiver, _Prev...>(
389-
unifex::start(op), r))>;
413+
return //result_type{
414+
stdexec::start(op);//};
415+
} else if constexpr (__nullable_tail_sender_to<_TailSender, _TailReceiver>) {
416+
auto op = stdexec::connect(std::move(c), r);
417+
// using result_type = variant_tail_sender<
418+
// __null_tail_sender,
419+
// decltype(_start_next<next_t, _TailSender, _TailReceiver, _PrevTailSenders...>(
420+
// stdexec::start(op), r))>;
421+
// if (!op) {
422+
// return result_type{__null_tail_sender{}};
423+
// }
390424
if (!op) {
391-
return result_type{null_tail_sender{}};
425+
return //result_type{
426+
__null_tail_sender{};//};
392427
}
393-
return result_type{
394-
_start_next<next_t, _TailSender, _TailReceiver, _Prev...>(unifex::start(op), r)};
428+
return //result_type{
429+
__start_next<next_t, _TailSender, _TailReceiver, _PrevTailSenders...>(stdexec::start(op), r);//};
395430
} else {
396-
auto op = unifex::connect(std::move(c), r);
397-
return _start_next<next_t, _TailSender, _TailReceiver, _Prev...>(unifex::start(op), r);
431+
auto op = stdexec::connect(std::move(c), r);
432+
return __start_next<next_t, _TailSender, _TailReceiver, _PrevTailSenders...>(stdexec::start(op), r);
398433
}
399434
}
400435
}
401436

402-
template<class _TailSender, class _TailReceiver> //
403-
auto _start_sequential(_TailSender c, _TailReceiver r) {
404-
return _start_sequential(c, r, type_list<>{});
405-
}
406-
#endif
407-
408437
} // namespace exec

test/exec/test_tail_sender.cpp

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,10 @@ TEST_CASE("Test ATailSender is a tail_sender", "[tail_sender]") {
9898
TEST_CASE("Test __null_tail_sender is a tail_sender", "[tail_sender]") {
9999
static_assert(exec::tail_sender<exec::__null_tail_sender>);
100100
static_assert(exec::__terminal_tail_sender_to<exec::__null_tail_sender, exec::__null_tail_receiver>);
101+
static_assert(exec::__nullable_tail_sender_to<exec::__null_tail_sender, exec::__null_tail_receiver>);
101102
CHECK(exec::tail_sender<exec::__null_tail_sender>);
102103
CHECK(exec::__terminal_tail_sender_to<exec::__null_tail_sender, exec::__null_tail_receiver>);
104+
CHECK(exec::__nullable_tail_sender_to<exec::__null_tail_sender, exec::__null_tail_receiver>);
103105
}
104106

105107
TEST_CASE("Test maybe_tail_sender is a tail_sender", "[tail_sender]") {
@@ -119,9 +121,9 @@ TEST_CASE("Test scoped_tail_sender", "[tail_sender]") {
119121
}
120122

121123
TEST_CASE("Test __start_until_nullable()", "[tail_sender]") {
122-
static_assert(exec::__terminal_tail_sender_to<exec::maybe_tail_sender<ATailSender>, ATailReceiver>);
123-
CHECK(exec::__terminal_tail_sender_to<exec::maybe_tail_sender<ATailSender>, ATailReceiver>);
124124

125+
// return sender arg when it is nullable
126+
// an empty maybe_tail_sender arg is empty when returned
125127
int called = 0;
126128
CHECK(called == 0);
127129
exec::maybe_tail_sender<ATailSender> maybe =
@@ -131,6 +133,8 @@ TEST_CASE("Test __start_until_nullable()", "[tail_sender]") {
131133
CHECK(called == 0);
132134
CHECK(!op0);
133135

136+
// return sender arg when it is nullable
137+
// a valid maybe_tail_sender arg is valid when returned
134138
called = 0;
135139
maybe =
136140
exec::__start_until_nullable(exec::maybe_tail_sender<ATailSender>{ATailSender{}}, ATailReceiver{&called});
@@ -141,6 +145,7 @@ TEST_CASE("Test __start_until_nullable()", "[tail_sender]") {
141145
ex::start(op1);
142146
CHECK(called == 1);
143147

148+
// return the nullable sender that was passed through set_value and start
144149
called = 0;
145150
maybe =
146151
exec::__start_until_nullable(
@@ -154,37 +159,58 @@ TEST_CASE("Test __start_until_nullable()", "[tail_sender]") {
154159
CHECK(called == 2);
155160
}
156161

157-
#if 0
158-
TEST_CASE("Test __resume_until_nullable()", "[tail_sender]") {
162+
TEST_CASE("Test __start_next()", "[tail_sender]") {
159163
int called = 0;
160164
CHECK(called == 0);
161-
exec::maybe_tail_sender<ATailSender> maybe =
162-
exec::__resume_until_nullable(exec::maybe_tail_sender<ATailSender>{}, ATailReceiver{&called});
163-
CHECK(called == 0);
164-
auto op0 = ex::connect(std::move(maybe), ATailReceiver{&called});
165-
CHECK(called == 0);
166-
CHECK(!op0);
167-
168-
called = 0;
169-
maybe =
170-
exec::__resume_until_nullable(exec::maybe_tail_sender<ATailSender>{ATailSender{}}, ATailReceiver{&called});
165+
auto maybe = exec::__start_next<exec::maybe_tail_sender<ATailSender>, ATailSender>(
166+
exec::maybe_tail_sender<ATailSender>{ATailSender{}},
167+
ANestTailReceiver<exec::maybe_tail_sender<ATailSender>>{ATailSender{}, &called});
171168
CHECK(called == 0);
172169
auto op1 = ex::connect(std::move(maybe), ATailReceiver{&called});
173170
CHECK(called == 0);
174171
CHECK(!!op1);
175172
ex::start(op1);
176173
CHECK(called == 1);
177174

178-
called = 0;
179-
maybe =
180-
exec::__resume_until_nullable(
181-
ATailSender{},
182-
ANestTailReceiver<exec::maybe_tail_sender<ATailSender>>{ATailSender{}, &called});
175+
// static_assert that this is infinite..
176+
// exec::__start_next<ATailSender, ATailSender>(ATailSender{ATailSender{}},
177+
// ANestTailReceiver<ATailSender>{ATailSender{}, &called});
178+
}
179+
180+
TEST_CASE("Test __start_sequential()", "[tail_sender]") {
181+
int called = 0;
182+
CHECK(called == 0);
183+
exec::maybe_tail_sender<ATailSender> maybe = exec::__start_sequential(
184+
exec::maybe_tail_sender<ATailSender>{ATailSender{}},
185+
ANestTailReceiver<exec::maybe_tail_sender<ATailSender>>{ATailSender{}, &called});
183186
CHECK(called == 1);
184-
auto op2 = ex::connect(std::move(maybe), ATailReceiver{&called});
187+
auto op1 = ex::connect(std::move(maybe), ATailReceiver{&called});
185188
CHECK(called == 1);
186-
CHECK(!!op2);
187-
ex::start(op2);
189+
CHECK(!!op1);
190+
ex::start(op1);
188191
CHECK(called == 2);
192+
193+
called = 0;
194+
CHECK(called == 0);
195+
maybe = exec::__start_sequential(
196+
exec::maybe_tail_sender<ATailSender>{},
197+
ANestTailReceiver<exec::maybe_tail_sender<ATailSender>>{ATailSender{}, &called});
198+
CHECK(called == 0);
199+
auto op2 = ex::connect(std::move(maybe), ATailReceiver{&called});
200+
CHECK(called == 0);
201+
CHECK(!op2);
202+
203+
called = 0;
204+
CHECK(called == 0);
205+
exec::__null_tail_sender empty = exec::__start_sequential(
206+
exec::__null_tail_sender{},
207+
ANestTailReceiver<exec::__null_tail_sender>{exec::__null_tail_sender{}, &called});
208+
CHECK(called == 0);
209+
auto op3 = ex::connect(std::move(empty), ATailReceiver{&called});
210+
CHECK(called == 0);
211+
CHECK(!op3);
212+
213+
// static_assert that this is infinite..
214+
// exec::__start_next<ATailSender, ATailSender>(ATailSender{ATailSender{}},
215+
// ANestTailReceiver<ATailSender>{ATailSender{}, &called});
189216
}
190-
#endif

0 commit comments

Comments
 (0)