-
Notifications
You must be signed in to change notification settings - Fork 185
when_all() should return just() #482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The idea doesn't sound bad to me. But, currently this is prevented by the spec (see 10.8.5.12./2). We need to change the spec first. |
|
Rebased (and resolved conflicts). |
Rebased and resolved conflicts. I'm not sure what to do with my suggested change to the specification. Here it is, for the record: diff --git a/std_execution.bs b/std_execution.bs
index ee8a16a..61f1507 100644
--- a/std_execution.bs
+++ b/std_execution.bs
@@ -5736,10 +5736,7 @@ template <class E> // arguments are not associated entities ([lib.tmpl-heads
1. `execution::when_all` is used to join multiple sender chains and create a sender whose execution is dependent on all of the input senders that only send a single set of values. `execution::when_all_with_variant`
is used to join multiple sender chains and create a sender whose execution is dependent on all of the input senders, each of which may have one or more sets of sent values.
-2. The name `execution::when_all` denotes a customization point object. For some subexpressions <code>s<i><sub>i</sub></i>...</code>, let <code>S<i><sub>i</sub></i>...</code> be <code>decltype((s<i><sub>i</sub></i>))...</code>. The expression <code>execution::when_all(s<i><sub>i</sub></i>...)</code> is ill-formed if any of the following is true:
-
- * If the number of subexpressions <code>s<i><sub>i</sub></i>...</code> is 0, or
- * If any type <code>S<i><sub>i</sub></i></code> does not satisfy `execution::sender`.
+2. The name `execution::when_all` denotes a customization point object. For some subexpressions <code>s<i><sub>i</sub></i>...</code>, let <code>S<i><sub>i</sub></i>...</code> be <code>decltype((s<i><sub>i</sub></i>))...</code>. The expression <code>execution::when_all(s<i><sub>i</sub></i>...)</code> is ill-formed if any type <code>S<i><sub>i</sub></i></code> does not satisfy `execution::sender`.
Otherwise, the expression <code>execution::when_all(s<i><sub>i</sub></i>...)</code> is expression-equivalent to:
@@ -5758,7 +5755,7 @@ template <class E> // arguments are not associated entities ([lib.tmpl-heads
1. For each sender <code>s<i><sub>i</sub></i></code>, constructs a receiver <code>r<i><sub>i</sub></i></code> such that:
- 1. If <code>execution::set_value(r<i><sub>i</sub></i>, t<i><sub>i</sub></i>...)</code> is called for every <code>r<i><sub>i</sub></i></code>, `op_state`'s associated stop callback optional is reset and <code>execution::set_value(out_r, t<i><sub>0</sub></i>..., t<i><sub>1</sub></i>..., ..., t<i><sub>n-1</sub></i>...)</code> is called, where `n` the number of subexpressions in <code>s<i><sub>i</sub></i>...</code>.
+ 1. If <code>execution::set_value(r<i><sub>i</sub></i>, t<i><sub>i</sub></i>...)</code> is called for every <code>r<i><sub>i</sub></i></code>, or if the number of subexpressions in <code>s<i><sub>i</sub></i>...</code> is 0, `op_state`'s associated stop callback optional is reset and <code>execution::set_value(out_r, t<i><sub>0</sub></i>..., t<i><sub>1</sub></i>..., ..., t<i><sub>n-1</sub></i>...)</code> is called, where `n` is the number of subexpressions in <code>s<i><sub>i</sub></i>...</code>.
2. Otherwise, `execution::set_error` or `execution::set_stopped` was called for at least one receiver <code>r<i><sub>i</sub></i></code>. If the first such to complete did so with the call <code>execution::set_error(r<i><sub>i</sub></i>, e)</code>, `request_stop` is called on `op_state`'s associated stop source. When all child operations have completed, `op_state`'s associated stop callback optional is reset and `execution::set_error(out_r, e)` is called.
-- |
@@ -44,6 +44,11 @@ TEST_CASE("transfer_when_all simple example", "[adaptors][transfer_when_all]") { | |||
auto op = ex::connect(std::move(snd1), expect_value_receiver<double>{3.1415}); | |||
ex::start(op); | |||
} | |||
TEST_CASE("transfer_when_all with no senders", "[adaptors][transfer_when_all]") { | |||
auto snd = ex::transfer_when_all(inline_scheduler{}); | |||
auto op = ex::connect(std::move(snd), expect_void_receiver{}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dunno, but I would expect when_all()
to be equivalent to just(tuple{})
instead of just()
. Can you comment on why you opted for the current semantics?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when_all concats the values from all the senders, without wrapping them onto tuples. So an empty list of values seems correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the review. In libunifex, just
is special-cased for 0 arguments, and ends up calling set_value()
. But here in stdexec things are more regular, and just
completes by calling set_value(tuple{})
, which is the desired end result for when_all
, allowing a generic program like the following:
#include <iostream>
#include <tuple>
#include <stdexec/execution.hpp>
auto print = [](const auto &...args) {
(std::cout << "[" << ... << args) << "]\n";
};
auto do_test(const auto &...args) {
auto sender = stdexec::when_all(stdexec::just(args)...);
auto results = stdexec::this_thread::sync_wait(sender);
std::apply(print, *results);
}
int main() {
do_test("a", "b", "c");
do_test("x");
do_test();
}
This program compiles on the current branch, but if when_all
added an extra layer of 'tuple', this program would need its own special-case code to work around it.
Note that my current suggestion for when_all
with no senders isn't implemented in terms of just
-- by removing even more special cases, everything just takes care of itself. (Sort of! I did introduce a new if constexpr
test for the case of 0 senders. But that's only an optimization.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just
completes by callingset_value(tuple{})
huh? When the just()
sender completes, it calls set_value()
(or rather set_value(rcvr)
since set_value
always takes a receiver).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when_all concats the values from all the senders, without wrapping them onto tuples. So an empty list of values seems correct?
Oh yeah.
/me pours himself another coffee
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh? When the
just()
sender completes, it callsset_value()
(or ratherset_value(rcvr)
sinceset_value
always takes a receiver).
Right, sorry. I'll just say that adding tuple{}
here would make the case of zero senders the odd-one-out.
'when_all()' with zero input senders completes immediately on start
Thanks for your work on this @bustercopley, and apologies for the loooong wait. As you've probably noticed, we've moved the wording of P2300 into its own repo at brycelelbach/wg21_p2300_execution. The wording changes that you had proposed in an earlier draft of this PR would need to be applied there. |
If I write a variadic function template that takes a pack of senders and passes them to
when_all
, I need to either (a) constrain it to take at least one sender, or (b) handle the case of zero senders specially.It would be more convenient if
when_all()
returned the moral equivalent ofjust(tuple{})
. [Correction:just()
. A sender which, when connected to a receiver and started, callsset_value(tuple{})
on the receiver.]Patch supplied, but I don't seriously believe it can be as easy as this.