- tmp/tmp10hz0qea/{from.md → to.md} +2117 -0
tmp/tmp10hz0qea/{from.md → to.md}
RENAMED
|
@@ -0,0 +1,2117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### Sender adaptors <a id="exec.adapt">[[exec.adapt]]</a>
|
| 2 |
+
|
| 3 |
+
#### General <a id="exec.adapt.general">[[exec.adapt.general]]</a>
|
| 4 |
+
|
| 5 |
+
Subclause [[exec.adapt]] specifies a set of sender adaptors.
|
| 6 |
+
|
| 7 |
+
The bitwise inclusive operator is overloaded for the purpose of creating
|
| 8 |
+
sender chains. The adaptors also support function call syntax with
|
| 9 |
+
equivalent semantics.
|
| 10 |
+
|
| 11 |
+
Unless otherwise specified:
|
| 12 |
+
|
| 13 |
+
- A sender adaptor is prohibited from causing observable effects, apart
|
| 14 |
+
from moving and copying its arguments, before the returned sender is
|
| 15 |
+
connected with a receiver using `connect`, and `start` is called on
|
| 16 |
+
the resulting operation state.
|
| 17 |
+
- A parent sender [[exec.async.ops]] with a single child sender `sndr`
|
| 18 |
+
has an associated attribute object equal to `FWD-ENV(get_env(sndr))`
|
| 19 |
+
[[exec.fwd.env]].
|
| 20 |
+
- A parent sender with more than one child sender has an associated
|
| 21 |
+
attributes object equal to `env<>{}`.
|
| 22 |
+
- When a parent sender is connected to a receiver `rcvr`, any receiver
|
| 23 |
+
used to connect a child sender has an associated environment equal to
|
| 24 |
+
`FWD-ENV(get_env(rcvr))`.
|
| 25 |
+
- An adaptor whose child senders are all non-dependent
|
| 26 |
+
[[exec.async.ops]] is itself non-dependent.
|
| 27 |
+
- These requirements apply to any function that is selected by the
|
| 28 |
+
implementation of the sender adaptor.
|
| 29 |
+
- *Recommended practice:* Implementations should use the completion
|
| 30 |
+
signatures of the adaptors to communicate type errors to users and to
|
| 31 |
+
propagate any such type errors from child senders.
|
| 32 |
+
|
| 33 |
+
If a sender returned from a sender adaptor specified in [[exec.adapt]]
|
| 34 |
+
is specified to include `set_error_t(Err)` among its set of completion
|
| 35 |
+
signatures where `decay_t<Err>` denotes the type `exception_ptr`, but
|
| 36 |
+
the implementation does not potentially evaluate an error completion
|
| 37 |
+
operation with an `exception_ptr` argument, the implementation is
|
| 38 |
+
allowed to omit the `exception_ptr` error completion signature from the
|
| 39 |
+
set.
|
| 40 |
+
|
| 41 |
+
#### Closure objects <a id="exec.adapt.obj">[[exec.adapt.obj]]</a>
|
| 42 |
+
|
| 43 |
+
A *pipeable sender adaptor closure object* is a function object that
|
| 44 |
+
accepts one or more `sender` arguments and returns a `sender`. For a
|
| 45 |
+
pipeable sender adaptor closure object `c` and an expression `sndr` such
|
| 46 |
+
that `decltype((sndr))` models `sender`, the following expressions are
|
| 47 |
+
equivalent and yield a `sender`:
|
| 48 |
+
|
| 49 |
+
``` cpp
|
| 50 |
+
c(sndr)
|
| 51 |
+
sndr | c
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
Given an additional pipeable sender adaptor closure object `d`, the
|
| 55 |
+
expression `c | d` produces another pipeable sender adaptor closure
|
| 56 |
+
object `e`:
|
| 57 |
+
|
| 58 |
+
`e` is a perfect forwarding call wrapper [[func.require]] with the
|
| 59 |
+
following properties:
|
| 60 |
+
|
| 61 |
+
- Its target object is an object `d2` of type `decltype(auto(d))`
|
| 62 |
+
direct-non-list-initialized with `d`.
|
| 63 |
+
- It has one bound argument entity, an object `c2` of type
|
| 64 |
+
`decltype(auto(c))` direct-non-list-initialized with `c`.
|
| 65 |
+
- Its call pattern is `d2(c2(arg))`, where arg is the argument used in a
|
| 66 |
+
function call expression of `e`.
|
| 67 |
+
|
| 68 |
+
The expression `c | d` is well-formed if and only if the initializations
|
| 69 |
+
of the state entities [[func.def]] of `e` are all well-formed.
|
| 70 |
+
|
| 71 |
+
An object `t` of type `T` is a pipeable sender adaptor closure object if
|
| 72 |
+
`T` models `derived_from<sender_adaptor_closure<T>>`, `T` has no other
|
| 73 |
+
base classes of type `sender_adaptor_closure<U>` for any other type `U`,
|
| 74 |
+
and `T` does not satisfy `sender`.
|
| 75 |
+
|
| 76 |
+
The template parameter `D` for `sender_adaptor_closure` can be an
|
| 77 |
+
incomplete type. Before any expression of type cv `D` appears as an
|
| 78 |
+
operand to the `|` operator, `D` shall be complete and model
|
| 79 |
+
`derived_from<sender_adaptor_closure<D>>`. The behavior of an expression
|
| 80 |
+
involving an object of type cv `D` as an operand to the `|` operator is
|
| 81 |
+
undefined if overload resolution selects a program-defined `operator|`
|
| 82 |
+
function.
|
| 83 |
+
|
| 84 |
+
A *pipeable sender adaptor object* is a customization point object that
|
| 85 |
+
accepts a `sender` as its first argument and returns a `sender`. If a
|
| 86 |
+
pipeable sender adaptor object accepts only one argument, then it is a
|
| 87 |
+
pipeable sender adaptor closure object.
|
| 88 |
+
|
| 89 |
+
If a pipeable sender adaptor object adaptor accepts more than one
|
| 90 |
+
argument, then let `sndr` be an expression such that `decltype((sndr))`
|
| 91 |
+
models `sender`, let `args...` be arguments such that
|
| 92 |
+
`adaptor(sndr, args...)` is a well-formed expression as specified below,
|
| 93 |
+
and let `BoundArgs` be a pack that denotes `decltype(auto(args))...`.
|
| 94 |
+
The expression `adaptor(args...)` produces a pipeable sender adaptor
|
| 95 |
+
closure object `f` that is a perfect forwarding call wrapper with the
|
| 96 |
+
following properties:
|
| 97 |
+
|
| 98 |
+
- Its target object is a copy of adaptor.
|
| 99 |
+
- Its bound argument entities `bound_args` consist of objects of types
|
| 100 |
+
`BoundArgs...` direct-non-list-initialized with
|
| 101 |
+
`std::forward<decltype((args))>(args)...`, respectively.
|
| 102 |
+
- Its call pattern is `adaptor(rcvr, bound_args...)`, where `rcvr` is
|
| 103 |
+
the argument used in a function call expression of `f`.
|
| 104 |
+
|
| 105 |
+
The expression `adaptor(args...)` is well-formed if and only if the
|
| 106 |
+
initializations of the bound argument entities of the result, as
|
| 107 |
+
specified above, are all well-formed.
|
| 108 |
+
|
| 109 |
+
#### `execution::write_env` <a id="exec.write.env">[[exec.write.env]]</a>
|
| 110 |
+
|
| 111 |
+
`write_env` is a sender adaptor that accepts a sender and a queryable
|
| 112 |
+
object, and that returns a sender that, when connected with a receiver
|
| 113 |
+
`rcvr`, connects the adapted sender with a receiver whose execution
|
| 114 |
+
environment is the result of joining the `queryable` object to the
|
| 115 |
+
result of `get_env(rcvr)`.
|
| 116 |
+
|
| 117 |
+
`write_env` is a customization point object. For some subexpressions
|
| 118 |
+
`sndr` and `env`, if `decltype((sndr))` does not satisfy `sender` or if
|
| 119 |
+
`decltype((env))` does not satisfy `queryable`, the expression
|
| 120 |
+
`write_env(sndr, env)` is ill-formed. Otherwise, it is
|
| 121 |
+
expression-equivalent to `make-sender(write_env, env, sndr)`.
|
| 122 |
+
|
| 123 |
+
Let *`write-env-t`* denote the type `decltype(auto(write_env))`. The
|
| 124 |
+
exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 125 |
+
specialized for *`write-env-t`* as follows:
|
| 126 |
+
|
| 127 |
+
``` cpp
|
| 128 |
+
template<>
|
| 129 |
+
struct impls-for<write-env-t> : default-impls {
|
| 130 |
+
static constexpr auto join-env(const auto& state, const auto& env) noexcept {
|
| 131 |
+
return see below;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
static constexpr auto get-env =
|
| 135 |
+
[](auto, const auto& state, const auto& rcvr) noexcept {
|
| 136 |
+
return join-env(state, FWD-ENV(get_env(rcvr)));
|
| 137 |
+
};
|
| 138 |
+
|
| 139 |
+
template<class Sndr, class... Env>
|
| 140 |
+
static consteval void check-types();
|
| 141 |
+
};
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
Invocation of `impls-for<write-env-t>::join-env` returns an object `e`
|
| 145 |
+
such that
|
| 146 |
+
|
| 147 |
+
- `decltype(e)` models `queryable` and
|
| 148 |
+
- given a query object `q`, the expression `e.query(q)` is
|
| 149 |
+
expression-equivalent to `state.query(q)` if that expression is valid,
|
| 150 |
+
otherwise, `e.query(q)` is expression-equivalent to `env.query(q)`.
|
| 151 |
+
|
| 152 |
+
For a type `Sndr` and a pack of types `Env`, let `State` be
|
| 153 |
+
`data-type<Sndr>` and let `JoinEnv` be the pack
|
| 154 |
+
`decltype(join-env(declval<State>(), FWD-ENV(declval<Env>())))`. Then
|
| 155 |
+
`impls-for<write-env-{t}>::check-types<Sndr, Env...>()` is
|
| 156 |
+
expression-equivalent to
|
| 157 |
+
`get_completion_signatures<child-{type}<Sndr>, JoinEnv...>()`.
|
| 158 |
+
|
| 159 |
+
#### `execution::unstoppable` <a id="exec.unstoppable">[[exec.unstoppable]]</a>
|
| 160 |
+
|
| 161 |
+
`unstoppable` is a sender adaptor that connects its inner sender with a
|
| 162 |
+
receiver that has the execution environment of the outer receiver but
|
| 163 |
+
with an object of type `never_stop_token` as the result of the
|
| 164 |
+
`get_stop_token query`.
|
| 165 |
+
|
| 166 |
+
For a subexpression `sndr`, `unstoppable(sndr)` is expression-equivalent
|
| 167 |
+
to `write_env(sndr, prop(get_stop_token, never_stop_token{}))`.
|
| 168 |
+
|
| 169 |
+
#### `execution::starts_on` <a id="exec.starts.on">[[exec.starts.on]]</a>
|
| 170 |
+
|
| 171 |
+
`starts_on` adapts an input sender into a sender that will start on an
|
| 172 |
+
execution agent belonging to a particular scheduler’s associated
|
| 173 |
+
execution resource.
|
| 174 |
+
|
| 175 |
+
The name `starts_on` denotes a customization point object. For
|
| 176 |
+
subexpressions `sch` and `sndr`, if `decltype(( sch))` does not satisfy
|
| 177 |
+
`scheduler`, or `decltype((sndr))` does not satisfy `sender`,
|
| 178 |
+
`starts_on(sch, sndr)` is ill-formed.
|
| 179 |
+
|
| 180 |
+
Otherwise, the expression `starts_on(sch, sndr)` is
|
| 181 |
+
expression-equivalent to:
|
| 182 |
+
|
| 183 |
+
``` cpp
|
| 184 |
+
transform_sender(
|
| 185 |
+
query-with-default(get_domain, sch, default_domain()),
|
| 186 |
+
make-sender(starts_on, sch, sndr))
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
except that `sch` is evaluated only once.
|
| 190 |
+
|
| 191 |
+
Let `out_sndr` and `env` be subexpressions such that `OutSndr` is
|
| 192 |
+
`decltype((out_sndr))`. If `sender-for<OutSndr, starts_on_t>` is
|
| 193 |
+
`false`, then the expressions `starts_on.transform_env(out_sndr, env)`
|
| 194 |
+
and `starts_on.transform_sender(out_sndr, env)` are ill-formed;
|
| 195 |
+
otherwise
|
| 196 |
+
|
| 197 |
+
- `starts_on.transform_env(out_sndr, env)` is equivalent to:
|
| 198 |
+
``` cpp
|
| 199 |
+
auto&& [_, sch, _] = out_sndr;
|
| 200 |
+
return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
|
| 201 |
+
```
|
| 202 |
+
- `starts_on.transform_sender(out_sndr, env)` is equivalent to:
|
| 203 |
+
``` cpp
|
| 204 |
+
auto&& [_, sch, sndr] = out_sndr;
|
| 205 |
+
return let_value(
|
| 206 |
+
schedule(sch),
|
| 207 |
+
[sndr = std::forward_like<OutSndr>(sndr)]() mutable
|
| 208 |
+
noexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {
|
| 209 |
+
return std::move(sndr);
|
| 210 |
+
});
|
| 211 |
+
```
|
| 212 |
+
|
| 213 |
+
Let `out_sndr` be a subexpression denoting a sender returned from
|
| 214 |
+
`starts_on(sch, sndr)` or one equal to such, and let `OutSndr` be the
|
| 215 |
+
type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
|
| 216 |
+
a receiver that has an environment of type `Env` such that
|
| 217 |
+
`sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
|
| 218 |
+
the operation state that results from connecting `out_sndr` with
|
| 219 |
+
`out_rcvr`. Calling `start(op)` shall start `sndr` on an execution agent
|
| 220 |
+
of the associated execution resource of `sch`. If scheduling onto `sch`
|
| 221 |
+
fails, an error completion on `out_rcvr` shall be executed on an
|
| 222 |
+
unspecified execution agent.
|
| 223 |
+
|
| 224 |
+
#### `execution::continues_on` <a id="exec.continues.on">[[exec.continues.on]]</a>
|
| 225 |
+
|
| 226 |
+
`continues_on` adapts a sender into one that completes on the specified
|
| 227 |
+
scheduler.
|
| 228 |
+
|
| 229 |
+
The name `continues_on` denotes a pipeable sender adaptor object. For
|
| 230 |
+
subexpressions `sch` and `sndr`, if `decltype((sch))` does not satisfy
|
| 231 |
+
`scheduler`, or `decltype((sndr))` does not satisfy `sender`,
|
| 232 |
+
`continues_on(sndr, sch)` is ill-formed.
|
| 233 |
+
|
| 234 |
+
Otherwise, the expression `continues_on(sndr, sch)` is
|
| 235 |
+
expression-equivalent to:
|
| 236 |
+
|
| 237 |
+
``` cpp
|
| 238 |
+
transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
except that `sndr` is evaluated only once.
|
| 242 |
+
|
| 243 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 244 |
+
specialized for `continues_on_t` as follows:
|
| 245 |
+
|
| 246 |
+
``` cpp
|
| 247 |
+
namespace std::execution {
|
| 248 |
+
template<>
|
| 249 |
+
struct impls-for<continues_on_t> : default-impls {
|
| 250 |
+
static constexpr auto get-attrs =
|
| 251 |
+
[](const auto& data, const auto& child) noexcept -> decltype(auto) {
|
| 252 |
+
return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
|
| 253 |
+
};
|
| 254 |
+
};
|
| 255 |
+
}
|
| 256 |
+
```
|
| 257 |
+
|
| 258 |
+
Let `sndr` and `env` be subexpressions such that `Sndr` is
|
| 259 |
+
`decltype((sndr))`. If `sender-for<Sndr, continues_on_t>` is `false`,
|
| 260 |
+
then the expression `continues_on.transform_sender(sndr, env)` is
|
| 261 |
+
ill-formed; otherwise, it is equal to:
|
| 262 |
+
|
| 263 |
+
``` cpp
|
| 264 |
+
auto [_, data, child] = sndr;
|
| 265 |
+
return schedule_from(std::move(data), std::move(child));
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
[*Note 1*: This causes the `continues_on(sndr, sch)` sender to become
|
| 269 |
+
`schedule_from(sch, sndr)` when it is connected with a receiver whose
|
| 270 |
+
execution domain does not customize `continues_on`. — *end note*]
|
| 271 |
+
|
| 272 |
+
Let `out_sndr` be a subexpression denoting a sender returned from
|
| 273 |
+
`continues_on(sndr, sch)` or one equal to such, and let `OutSndr` be the
|
| 274 |
+
type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
|
| 275 |
+
a receiver that has an environment of type `Env` such that
|
| 276 |
+
`sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
|
| 277 |
+
the operation state that results from connecting `out_sndr` with
|
| 278 |
+
`out_rcvr`. Calling `start(op)` shall start `sndr` on the current
|
| 279 |
+
execution agent and execute completion operations on `out_rcvr` on an
|
| 280 |
+
execution agent of the execution resource associated with `sch`. If
|
| 281 |
+
scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
|
| 282 |
+
executed on an unspecified execution agent.
|
| 283 |
+
|
| 284 |
+
#### `execution::schedule_from` <a id="exec.schedule.from">[[exec.schedule.from]]</a>
|
| 285 |
+
|
| 286 |
+
`schedule_from` schedules work dependent on the completion of a sender
|
| 287 |
+
onto a scheduler’s associated execution resource.
|
| 288 |
+
|
| 289 |
+
[*Note 1*: `schedule_from` is not meant to be used in user code; it is
|
| 290 |
+
used in the implementation of `continues_on`. — *end note*]
|
| 291 |
+
|
| 292 |
+
The name `schedule_from` denotes a customization point object. For some
|
| 293 |
+
subexpressions `sch` and `sndr`, let `Sch` be `decltype((sch))` and
|
| 294 |
+
`Sndr` be `decltype((sndr))`. If `Sch` does not satisfy `scheduler`, or
|
| 295 |
+
`Sndr` does not satisfy `sender`, `schedule_from(sch, sndr)` is
|
| 296 |
+
ill-formed.
|
| 297 |
+
|
| 298 |
+
Otherwise, the expression `schedule_from(sch, sndr)` is
|
| 299 |
+
expression-equivalent to:
|
| 300 |
+
|
| 301 |
+
``` cpp
|
| 302 |
+
transform_sender(
|
| 303 |
+
query-with-default(get_domain, sch, default_domain()),
|
| 304 |
+
make-sender(schedule_from, sch, sndr))
|
| 305 |
+
```
|
| 306 |
+
|
| 307 |
+
except that `sch` is evaluated only once.
|
| 308 |
+
|
| 309 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 310 |
+
specialized for `schedule_from_t` as follows:
|
| 311 |
+
|
| 312 |
+
``` cpp
|
| 313 |
+
namespace std::execution {
|
| 314 |
+
template<>
|
| 315 |
+
struct impls-for<schedule_from_t> : default-impls {
|
| 316 |
+
static constexpr auto get-attrs = see below;
|
| 317 |
+
static constexpr auto get-state = see below;
|
| 318 |
+
static constexpr auto complete = see below;
|
| 319 |
+
|
| 320 |
+
template<class Sndr, class... Env>
|
| 321 |
+
static consteval void check-types();
|
| 322 |
+
};
|
| 323 |
+
}
|
| 324 |
+
```
|
| 325 |
+
|
| 326 |
+
The member `impls-for<schedule_from_t>::get-attrs` is initialized with a
|
| 327 |
+
callable object equivalent to the following lambda:
|
| 328 |
+
|
| 329 |
+
``` cpp
|
| 330 |
+
[](const auto& data, const auto& child) noexcept -> decltype(auto) {
|
| 331 |
+
return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
|
| 332 |
+
}
|
| 333 |
+
```
|
| 334 |
+
|
| 335 |
+
The member `impls-for<schedule_from_t>::get-state` is initialized with a
|
| 336 |
+
callable object equivalent to the following lambda:
|
| 337 |
+
|
| 338 |
+
``` cpp
|
| 339 |
+
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
|
| 340 |
+
requires sender_in<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)> {
|
| 341 |
+
|
| 342 |
+
auto& [_, sch, child] = sndr;
|
| 343 |
+
|
| 344 |
+
using sched_t = decltype(auto(sch));
|
| 345 |
+
using variant_t = see below;
|
| 346 |
+
using receiver_t = see below;
|
| 347 |
+
using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>;
|
| 348 |
+
constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr}));
|
| 349 |
+
|
| 350 |
+
struct state-type {
|
| 351 |
+
Rcvr& rcvr; // exposition only
|
| 352 |
+
variant_t async-result; // exposition only
|
| 353 |
+
operation_t op-state; // exposition only
|
| 354 |
+
|
| 355 |
+
explicit state-type(sched_t sch, Rcvr& rcvr) noexcept(nothrow)
|
| 356 |
+
: rcvr(rcvr), op-state(connect(schedule(sch), receiver_t{this})) {}
|
| 357 |
+
};
|
| 358 |
+
|
| 359 |
+
return state-type{sch, rcvr};
|
| 360 |
+
}
|
| 361 |
+
```
|
| 362 |
+
|
| 363 |
+
``` cpp
|
| 364 |
+
template<class Sndr, class... Env>
|
| 365 |
+
static consteval void check-types();
|
| 366 |
+
```
|
| 367 |
+
|
| 368 |
+
*Effects:* Equivalent to:
|
| 369 |
+
|
| 370 |
+
``` cpp
|
| 371 |
+
get_completion_signatures<schedule_result_t<data-type<Sndr>>, FWD-ENV-T(Env)...>();
|
| 372 |
+
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
|
| 373 |
+
decay-copyable-result-datums(cs); // see [exec.snd.expos]
|
| 374 |
+
```
|
| 375 |
+
|
| 376 |
+
Objects of the local class *`state-type`* can be used to initialize a
|
| 377 |
+
structured binding.
|
| 378 |
+
|
| 379 |
+
Let `Sigs` be a pack of the arguments to the `completion_signatures`
|
| 380 |
+
specialization named by
|
| 381 |
+
`completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
|
| 382 |
+
Let *`as-tuple`* be an alias template such that `as-tuple<Tag(Args...)>`
|
| 383 |
+
denotes the type `decayed-tuple<Tag, Args...>`, and let
|
| 384 |
+
*`is-nothrow-decay-copy-sig`* be a variable template such that
|
| 385 |
+
`auto(is-nothrow-decay-copy-sig<Tag(Args...{})>)` is a constant
|
| 386 |
+
expression of type `bool` and equal to
|
| 387 |
+
`(is_nothrow_constructible_v<decay_t<Args>, Args> && ...)`. Let
|
| 388 |
+
*`error-completion`* be a pack consisting of the type
|
| 389 |
+
`set_error_t(exception_ptr)` if
|
| 390 |
+
`(is-nothrow-decay-copy-sig<Sigs> &&...)` is `false`, and an empty pack
|
| 391 |
+
otherwise. Then `variant_t` denotes the type
|
| 392 |
+
`variant<monostate, as-tuple<Sigs>..., error-completion...>`, except
|
| 393 |
+
with duplicate types removed.
|
| 394 |
+
|
| 395 |
+
`receiver_t` is an alias for the following exposition-only class:
|
| 396 |
+
|
| 397 |
+
``` cpp
|
| 398 |
+
namespace std::execution {
|
| 399 |
+
struct receiver-type {
|
| 400 |
+
using receiver_concept = receiver_t;
|
| 401 |
+
state-type* state; // exposition only
|
| 402 |
+
|
| 403 |
+
void set_value() && noexcept {
|
| 404 |
+
visit(
|
| 405 |
+
[this]<class Tuple>(Tuple& result) noexcept -> void {
|
| 406 |
+
if constexpr (!same_as<monostate, Tuple>) {
|
| 407 |
+
auto& [tag, ...args] = result;
|
| 408 |
+
tag(std::move(state->rcvr), std::move(args)...);
|
| 409 |
+
}
|
| 410 |
+
},
|
| 411 |
+
state->async-result);
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
template<class Error>
|
| 415 |
+
void set_error(Error&& err) && noexcept {
|
| 416 |
+
execution::set_error(std::move(state->rcvr), std::forward<Error>(err));
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
void set_stopped() && noexcept {
|
| 420 |
+
execution::set_stopped(std::move(state->rcvr));
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
decltype(auto) get_env() const noexcept {
|
| 424 |
+
return FWD-ENV(execution::get_env(state->rcvr));
|
| 425 |
+
}
|
| 426 |
+
};
|
| 427 |
+
}
|
| 428 |
+
```
|
| 429 |
+
|
| 430 |
+
The expression in the `noexcept` clause of the lambda is `true` if the
|
| 431 |
+
construction of the returned *`state-type`* object is not potentially
|
| 432 |
+
throwing; otherwise, `false`.
|
| 433 |
+
|
| 434 |
+
The member `impls-for<schedule_from_t>::complete` is initialized with a
|
| 435 |
+
callable object equivalent to the following lambda:
|
| 436 |
+
|
| 437 |
+
``` cpp
|
| 438 |
+
[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept
|
| 439 |
+
-> void {
|
| 440 |
+
using result_t = decayed-tuple<Tag, Args...>;
|
| 441 |
+
constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...);
|
| 442 |
+
|
| 443 |
+
try {
|
| 444 |
+
state.async-result.template emplace<result_t>(Tag(), std::forward<Args>(args)...);
|
| 445 |
+
} catch (...) {
|
| 446 |
+
if constexpr (!nothrow)
|
| 447 |
+
state.async-result.template emplace<tuple<set_error_t,
|
| 448 |
+
exception_ptr>>(set_error, current_exception());
|
| 449 |
+
}
|
| 450 |
+
start(state.op-state);
|
| 451 |
+
};
|
| 452 |
+
```
|
| 453 |
+
|
| 454 |
+
Let `out_sndr` be a subexpression denoting a sender returned from
|
| 455 |
+
`schedule_from(sch, sndr)` or one equal to such, and let `OutSndr` be
|
| 456 |
+
the type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression
|
| 457 |
+
denoting a receiver that has an environment of type `Env` such that
|
| 458 |
+
`sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
|
| 459 |
+
the operation state that results from connecting `out_sndr` with
|
| 460 |
+
`out_rcvr`. Calling `start(op)` shall start `sndr` on the current
|
| 461 |
+
execution agent and execute completion operations on `out_rcvr` on an
|
| 462 |
+
execution agent of the execution resource associated with `sch`. If
|
| 463 |
+
scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
|
| 464 |
+
executed on an unspecified execution agent.
|
| 465 |
+
|
| 466 |
+
#### `execution::on` <a id="exec.on">[[exec.on]]</a>
|
| 467 |
+
|
| 468 |
+
The `on` sender adaptor has two forms:
|
| 469 |
+
|
| 470 |
+
- `on(sch, sndr)`, which starts a sender `sndr` on an execution agent
|
| 471 |
+
belonging to a scheduler `sch`’s associated execution resource and
|
| 472 |
+
that, upon `sndr`’s completion, transfers execution back to the
|
| 473 |
+
execution resource on which the `on` sender was started.
|
| 474 |
+
- `on(sndr, sch, closure)`, which upon completion of a sender `sndr`,
|
| 475 |
+
transfers execution to an execution agent belonging to a scheduler
|
| 476 |
+
`sch`’s associated execution resource, then executes a sender adaptor
|
| 477 |
+
closure `closure` with the async results of the sender, and that then
|
| 478 |
+
transfers execution back to the execution resource on which `sndr`
|
| 479 |
+
completed.
|
| 480 |
+
|
| 481 |
+
The name `on` denotes a pipeable sender adaptor object. For
|
| 482 |
+
subexpressions `sch` and `sndr`, `on(sch, sndr)` is ill-formed if any of
|
| 483 |
+
the following is `true`:
|
| 484 |
+
|
| 485 |
+
- `decltype((sch))` does not satisfy `scheduler`, or
|
| 486 |
+
- `decltype((sndr))` does not satisfy `sender` and `sndr` is not a
|
| 487 |
+
pipeable sender adaptor closure object [[exec.adapt.obj]], or
|
| 488 |
+
- `decltype((sndr))` satisfies `sender` and `sndr `is also a pipeable
|
| 489 |
+
sender adaptor closure object.
|
| 490 |
+
|
| 491 |
+
Otherwise, if `decltype((sndr))` satisfies `sender`, the expression
|
| 492 |
+
`on(sch, sndr)` is expression-equivalent to:
|
| 493 |
+
|
| 494 |
+
``` cpp
|
| 495 |
+
transform_sender(
|
| 496 |
+
query-with-default(get_domain, sch, default_domain()),
|
| 497 |
+
make-sender(on, sch, sndr))
|
| 498 |
+
```
|
| 499 |
+
|
| 500 |
+
except that `sch` is evaluated only once.
|
| 501 |
+
|
| 502 |
+
For subexpressions `sndr`, `sch`, and `closure`, if
|
| 503 |
+
|
| 504 |
+
- `decltype((sch))` does not satisfy `scheduler`, or
|
| 505 |
+
- `decltype((sndr))` does not satisfy `sender`, or
|
| 506 |
+
- `closure` is not a pipeable sender adaptor closure object
|
| 507 |
+
[[exec.adapt.obj]],
|
| 508 |
+
|
| 509 |
+
the expression `on(sndr, sch, closure)` is ill-formed; otherwise, it is
|
| 510 |
+
expression-equivalent to:
|
| 511 |
+
|
| 512 |
+
``` cpp
|
| 513 |
+
transform_sender(
|
| 514 |
+
get-domain-early(sndr),
|
| 515 |
+
make-sender(on, product-type{sch, closure}, sndr))
|
| 516 |
+
```
|
| 517 |
+
|
| 518 |
+
except that `sndr` is evaluated only once.
|
| 519 |
+
|
| 520 |
+
Let `out_sndr` and `env` be subexpressions, let `OutSndr` be
|
| 521 |
+
`decltype((out_sndr))`, and let `Env` be `decltype((env))`. If
|
| 522 |
+
`sender-for<OutSndr, on_t>` is `false`, then the expressions
|
| 523 |
+
`on.transform_env(out_sndr, env)` and
|
| 524 |
+
`on.transform_sender(out_sndr, env)` are ill-formed.
|
| 525 |
+
|
| 526 |
+
Otherwise: Let *`not-a-scheduler`* be an unspecified empty class type.
|
| 527 |
+
|
| 528 |
+
The expression `on.transform_env(out_sndr, env)` has effects equivalent
|
| 529 |
+
to:
|
| 530 |
+
|
| 531 |
+
``` cpp
|
| 532 |
+
auto&& [_, data, _] = out_sndr;
|
| 533 |
+
if constexpr (scheduler<decltype(data)>) {
|
| 534 |
+
return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env)));
|
| 535 |
+
} else {
|
| 536 |
+
return std::forward<Env>(env);
|
| 537 |
+
}
|
| 538 |
+
```
|
| 539 |
+
|
| 540 |
+
The expression `on.transform_sender(out_sndr, env)` has effects
|
| 541 |
+
equivalent to:
|
| 542 |
+
|
| 543 |
+
``` cpp
|
| 544 |
+
auto&& [_, data, child] = out_sndr;
|
| 545 |
+
if constexpr (scheduler<decltype(data)>) {
|
| 546 |
+
auto orig_sch =
|
| 547 |
+
query-with-default(get_scheduler, env, not-a-scheduler());
|
| 548 |
+
|
| 549 |
+
if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
|
| 550 |
+
return not-a-sender{};
|
| 551 |
+
} else {
|
| 552 |
+
return continues_on(
|
| 553 |
+
starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
|
| 554 |
+
std::move(orig_sch));
|
| 555 |
+
}
|
| 556 |
+
} else {
|
| 557 |
+
auto& [sch, closure] = data;
|
| 558 |
+
auto orig_sch = query-with-default(
|
| 559 |
+
get_completion_scheduler<set_value_t>,
|
| 560 |
+
get_env(child),
|
| 561 |
+
query-with-default(get_scheduler, env, not-a-scheduler()));
|
| 562 |
+
|
| 563 |
+
if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
|
| 564 |
+
return not-a-sender{};
|
| 565 |
+
} else {
|
| 566 |
+
return write_env(
|
| 567 |
+
continues_on(
|
| 568 |
+
std::forward_like<OutSndr>(closure)(
|
| 569 |
+
continues_on(
|
| 570 |
+
write_env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
|
| 571 |
+
sch)),
|
| 572 |
+
orig_sch),
|
| 573 |
+
SCHED-ENV(sch));
|
| 574 |
+
}
|
| 575 |
+
}
|
| 576 |
+
```
|
| 577 |
+
|
| 578 |
+
Let `out_sndr` be a subexpression denoting a sender returned from
|
| 579 |
+
`on(sch, sndr)` or one equal to such, and let `OutSndr` be the type
|
| 580 |
+
`decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting a
|
| 581 |
+
receiver that has an environment of type `Env` such that
|
| 582 |
+
`sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
|
| 583 |
+
the operation state that results from connecting `out_sndr` with
|
| 584 |
+
`out_rcvr`. Calling `start(op)` shall
|
| 585 |
+
|
| 586 |
+
- remember the current scheduler, `get_scheduler(get_env(rcvr))`;
|
| 587 |
+
- start `sndr` on an execution agent belonging to `sch`’s associated
|
| 588 |
+
execution resource;
|
| 589 |
+
- upon `sndr`’s completion, transfer execution back to the execution
|
| 590 |
+
resource associated with the scheduler remembered in step 1; and
|
| 591 |
+
- forward `sndr`’s async result to `out_rcvr`.
|
| 592 |
+
|
| 593 |
+
If any scheduling operation fails, an error completion on `out_rcvr`
|
| 594 |
+
shall be executed on an unspecified execution agent.
|
| 595 |
+
|
| 596 |
+
Let `out_sndr` be a subexpression denoting a sender returned from
|
| 597 |
+
`on(sndr, sch, closure)` or one equal to such, and let `OutSndr` be the
|
| 598 |
+
type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
|
| 599 |
+
a receiver that has an environment of type `Env` such that
|
| 600 |
+
`sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
|
| 601 |
+
the operation state that results from connecting `out_sndr` with
|
| 602 |
+
`out_rcvr`. Calling `start(op)` shall
|
| 603 |
+
|
| 604 |
+
- remember the current scheduler, which is the first of the following
|
| 605 |
+
expressions that is well-formed:
|
| 606 |
+
- `get_completion_scheduler<set_value_t>(get_env(sndr))`
|
| 607 |
+
- `get_scheduler(get_env(rcvr))`;
|
| 608 |
+
- start `sndr` on the current execution agent;
|
| 609 |
+
- upon `sndr`’s completion, transfer execution to an agent owned by
|
| 610 |
+
`sch`’s associated execution resource;
|
| 611 |
+
- forward `sndr`’s async result as if by connecting and starting a
|
| 612 |
+
sender `closure(S)`, where `S` is a sender that completes
|
| 613 |
+
synchronously with `sndr`’s async result; and
|
| 614 |
+
- upon completion of the operation started in the previous step,
|
| 615 |
+
transfer execution back to the execution resource associated with the
|
| 616 |
+
scheduler remembered in step 1 and forward the operation’s async
|
| 617 |
+
result to `out_rcvr`.
|
| 618 |
+
|
| 619 |
+
If any scheduling operation fails, an error completion on `out_rcvr`
|
| 620 |
+
shall be executed on an unspecified execution agent.
|
| 621 |
+
|
| 622 |
+
#### `execution::then`, `execution::upon_error`, `execution::upon_stopped` <a id="exec.then">[[exec.then]]</a>
|
| 623 |
+
|
| 624 |
+
`then` attaches an invocable as a continuation for an input sender’s
|
| 625 |
+
value completion operation. `upon_error` and `upon_stopped` do the same
|
| 626 |
+
for the error and stopped completion operations, respectively, sending
|
| 627 |
+
the result of the invocable as a value completion.
|
| 628 |
+
|
| 629 |
+
The names `then`, `upon_error`, and `upon_stopped` denote pipeable
|
| 630 |
+
sender adaptor objects. Let the expression *`then-cpo`* be one of
|
| 631 |
+
`then`, `upon_error`, or `upon_stopped`. For subexpressions `sndr` and
|
| 632 |
+
`f`, if `decltype((sndr))` does not satisfy `sender`, or `decltype((f))`
|
| 633 |
+
does not satisfy `movable-value`, `then-cpo(sndr, f) `is ill-formed.
|
| 634 |
+
|
| 635 |
+
Otherwise, the expression `then-cpo(sndr, f)` is expression-equivalent
|
| 636 |
+
to:
|
| 637 |
+
|
| 638 |
+
``` cpp
|
| 639 |
+
transform_sender(get-domain-early(sndr), make-sender(then-cpo, f, sndr))
|
| 640 |
+
```
|
| 641 |
+
|
| 642 |
+
except that `sndr` is evaluated only once.
|
| 643 |
+
|
| 644 |
+
For `then`, `upon_error`, and `upon_stopped`, let *`set-cpo`* be
|
| 645 |
+
`set_value`, `set_error`, and `set_stopped`, respectively. The
|
| 646 |
+
exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 647 |
+
specialized for *`then-cpo`* as follows:
|
| 648 |
+
|
| 649 |
+
``` cpp
|
| 650 |
+
namespace std::execution {
|
| 651 |
+
template<>
|
| 652 |
+
struct impls-for<decayed-typeof<then-cpo>> : default-impls {
|
| 653 |
+
static constexpr auto complete =
|
| 654 |
+
[]<class Tag, class... Args>
|
| 655 |
+
(auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
|
| 656 |
+
if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
|
| 657 |
+
TRY-SET-VALUE(rcvr,
|
| 658 |
+
invoke(std::move(fn), std::forward<Args>(args)...));
|
| 659 |
+
} else {
|
| 660 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 661 |
+
}
|
| 662 |
+
};
|
| 663 |
+
|
| 664 |
+
template<class Sndr, class... Env>
|
| 665 |
+
static consteval void check-types();
|
| 666 |
+
};
|
| 667 |
+
}
|
| 668 |
+
```
|
| 669 |
+
|
| 670 |
+
``` cpp
|
| 671 |
+
template<class Sndr, class... Env>
|
| 672 |
+
static consteval void check-types();
|
| 673 |
+
```
|
| 674 |
+
|
| 675 |
+
*Effects:* Equivalent to:
|
| 676 |
+
|
| 677 |
+
``` cpp
|
| 678 |
+
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
|
| 679 |
+
auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
|
| 680 |
+
if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>)
|
| 681 |
+
throw unspecified-exception();
|
| 682 |
+
};
|
| 683 |
+
cs.for-each(overload-set{fn, [](auto){}});
|
| 684 |
+
```
|
| 685 |
+
|
| 686 |
+
where *`unspecified-exception`* is a type derived from `exception`.
|
| 687 |
+
|
| 688 |
+
The expression `then-cpo(sndr, f)` has undefined behavior unless it
|
| 689 |
+
returns a sender `out_sndr` that
|
| 690 |
+
|
| 691 |
+
- invokes `f` or a copy of such with the value, error, or stopped result
|
| 692 |
+
datums of `sndr` for `then`, `upon_error`, and `upon_stopped`,
|
| 693 |
+
respectively, using the result value of `f` as `out_sndr`’s value
|
| 694 |
+
completion, and
|
| 695 |
+
- forwards all other completion operations unchanged.
|
| 696 |
+
|
| 697 |
+
#### `execution::let_value`, `execution::let_error`, `execution::let_stopped` <a id="exec.let">[[exec.let]]</a>
|
| 698 |
+
|
| 699 |
+
`let_value`, `let_error`, and `let_stopped` transform a sender’s value,
|
| 700 |
+
error, and stopped completions, respectively, into a new child
|
| 701 |
+
asynchronous operation by passing the sender’s result datums to a
|
| 702 |
+
user-specified callable, which returns a new sender that is connected
|
| 703 |
+
and started.
|
| 704 |
+
|
| 705 |
+
For `let_value`, `let_error`, and `let_stopped`, let *`set-cpo`* be
|
| 706 |
+
`set_value`, `set_error`, and `set_stopped`, respectively. Let the
|
| 707 |
+
expression *`let-cpo`* be one of `let_value`, `let_error`, or
|
| 708 |
+
`let_stopped`. For a subexpression `sndr`, let `let-env(sndr)` be
|
| 709 |
+
expression-equivalent to the first well-formed expression below:
|
| 710 |
+
|
| 711 |
+
- `\exposid{SCHED-ENV}(get_completion_scheduler<\exposid{decayed-typeof}<\exposid{set-cpo}>>(get_env(sndr)))`
|
| 712 |
+
- `\exposid{MAKE-ENV}(get_domain, get_domain(get_env(sndr)))`
|
| 713 |
+
- `(void(sndr), env<>{})`
|
| 714 |
+
|
| 715 |
+
The names `let_value`, `let_error`, and `let_stopped` denote pipeable
|
| 716 |
+
sender adaptor objects. For subexpressions `sndr` and `f`, let `F` be
|
| 717 |
+
the decayed type of `f`. If `decltype((sndr))` does not satisfy `sender`
|
| 718 |
+
or if `decltype((f))` does not satisfy `movable-value`, the expression
|
| 719 |
+
`let-cpo(sndr, f)` is ill-formed. If `F` does not satisfy `invocable`,
|
| 720 |
+
the expression `let_stopped(sndr, f)` is ill-formed.
|
| 721 |
+
|
| 722 |
+
Otherwise, the expression `let-cpo(sndr, f)` is expression-equivalent
|
| 723 |
+
to:
|
| 724 |
+
|
| 725 |
+
``` cpp
|
| 726 |
+
transform_sender(get-domain-early(sndr), make-sender(let-cpo, f, sndr))
|
| 727 |
+
```
|
| 728 |
+
|
| 729 |
+
except that `sndr` is evaluated only once.
|
| 730 |
+
|
| 731 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 732 |
+
specialized for *`let-cpo`* as follows:
|
| 733 |
+
|
| 734 |
+
``` cpp
|
| 735 |
+
namespace std::execution {
|
| 736 |
+
template<class State, class Rcvr, class... Args>
|
| 737 |
+
void let-bind(State& state, Rcvr& rcvr, Args&&... args); // exposition only
|
| 738 |
+
|
| 739 |
+
template<>
|
| 740 |
+
struct impls-for<decayed-typeof<let-cpo>> : default-impls {
|
| 741 |
+
static constexpr auto get-state = see below;
|
| 742 |
+
static constexpr auto complete = see below;
|
| 743 |
+
|
| 744 |
+
template<class Sndr, class... Env>
|
| 745 |
+
static consteval void check-types();
|
| 746 |
+
};
|
| 747 |
+
}
|
| 748 |
+
```
|
| 749 |
+
|
| 750 |
+
Let *`receiver2`* denote the following exposition-only class template:
|
| 751 |
+
|
| 752 |
+
``` cpp
|
| 753 |
+
namespace std::execution {
|
| 754 |
+
template<class Rcvr, class Env>
|
| 755 |
+
struct receiver2 {
|
| 756 |
+
using receiver_concept = receiver_t;
|
| 757 |
+
|
| 758 |
+
template<class... Args>
|
| 759 |
+
void set_value(Args&&... args) && noexcept {
|
| 760 |
+
execution::set_value(std::move(rcvr), std::forward<Args>(args)...);
|
| 761 |
+
}
|
| 762 |
+
|
| 763 |
+
template<class Error>
|
| 764 |
+
void set_error(Error&& err) && noexcept {
|
| 765 |
+
execution::set_error(std::move(rcvr), std::forward<Error>(err));
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
void set_stopped() && noexcept {
|
| 769 |
+
execution::set_stopped(std::move(rcvr));
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
decltype(auto) get_env() const noexcept {
|
| 773 |
+
return see below;
|
| 774 |
+
}
|
| 775 |
+
|
| 776 |
+
Rcvr& rcvr; // exposition only
|
| 777 |
+
Env env; // exposition only
|
| 778 |
+
};
|
| 779 |
+
}
|
| 780 |
+
```
|
| 781 |
+
|
| 782 |
+
Invocation of the function `receiver2::get_env` returns an object `e`
|
| 783 |
+
such that
|
| 784 |
+
|
| 785 |
+
- `decltype(e)` models `queryable` and
|
| 786 |
+
- given a query object `q`, the expression `e.query(q)` is
|
| 787 |
+
expression-equivalent to `env.query(q)` if that expression is valid;
|
| 788 |
+
otherwise, if the type of `q` satisfies `forwarding-query`,
|
| 789 |
+
`e.query(q)` is expression-equivalent to `get_env(rcvr).query(q)`;
|
| 790 |
+
otherwise, `e.query(q)` is ill-formed.
|
| 791 |
+
|
| 792 |
+
``` cpp
|
| 793 |
+
template<class Sndr, class... Env>
|
| 794 |
+
static consteval void check-types();
|
| 795 |
+
```
|
| 796 |
+
|
| 797 |
+
*Effects:* Equivalent to:
|
| 798 |
+
|
| 799 |
+
``` cpp
|
| 800 |
+
using LetFn = remove_cvref_t<data-type<Sndr>>;
|
| 801 |
+
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
|
| 802 |
+
auto fn = []<class... Ts>(decayed-typeof<set-cpo>(*)(Ts...)) {
|
| 803 |
+
if constexpr (!is-valid-let-sender) // see below
|
| 804 |
+
throw unspecified-exception();
|
| 805 |
+
};
|
| 806 |
+
cs.for-each(overload-set(fn, [](auto){}));
|
| 807 |
+
```
|
| 808 |
+
|
| 809 |
+
where *`unspecified-exception`* is a type derived from `exception`, and
|
| 810 |
+
where *`is-valid-let-sender`* is `true` if and only if all of the
|
| 811 |
+
following are `true`:
|
| 812 |
+
|
| 813 |
+
- `(constructible_from<decay_t<Ts>, Ts> &&...)`
|
| 814 |
+
- `invocable<LetFn, decay_t<Ts>&...>`
|
| 815 |
+
- `sender<invoke_result_t<LetFn, decay_t<Ts>&...>>`
|
| 816 |
+
- `sizeof...(Env) == 0 || sender_in<invoke_result_t<LetFn, decay_t<Ts>&...>, `*`env-t`*`...>`
|
| 817 |
+
|
| 818 |
+
where *`env-t`* is the pack
|
| 819 |
+
`decltype(`*`let-cpo`*`.transform_env(declval<Sndr>(), declval<Env>()))`.
|
| 820 |
+
|
| 821 |
+
`\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}`
|
| 822 |
+
|
| 823 |
+
is initialized with a callable object equivalent to the following:
|
| 824 |
+
|
| 825 |
+
``` cpp
|
| 826 |
+
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
|
| 827 |
+
auto& [_, fn, child] = sndr;
|
| 828 |
+
using fn_t = decay_t<decltype(fn)>;
|
| 829 |
+
using env_t = decltype(let-env(child));
|
| 830 |
+
using args_variant_t = see below;
|
| 831 |
+
using ops2_variant_t = see below;
|
| 832 |
+
|
| 833 |
+
struct state-type {
|
| 834 |
+
fn_t fn; // exposition only
|
| 835 |
+
env_t env; // exposition only
|
| 836 |
+
args_variant_t args; // exposition only
|
| 837 |
+
ops2_variant_t ops2; // exposition only
|
| 838 |
+
};
|
| 839 |
+
return state-type{allocator-aware-forward(std::forward_like<Sndr>(fn), rcvr),
|
| 840 |
+
let-env(child), {}, {}};
|
| 841 |
+
}
|
| 842 |
+
```
|
| 843 |
+
|
| 844 |
+
Let `Sigs` be a pack of the arguments to the `completion_signatures`
|
| 845 |
+
specialization named by
|
| 846 |
+
`completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
|
| 847 |
+
Let `LetSigs` be a pack of those types in `Sigs` with a return type of
|
| 848 |
+
`decayed-typeof<set-cpo>`. Let *`as-tuple`* be an alias template such
|
| 849 |
+
that `as-tuple<Tag(Args...)>` denotes the type `decayed-tuple<Args...>`.
|
| 850 |
+
Then `args_variant_t` denotes the type
|
| 851 |
+
`variant<monostate, as-tuple<LetSigs>...>` except with duplicate types
|
| 852 |
+
removed.
|
| 853 |
+
|
| 854 |
+
Given a type `Tag` and a pack `Args`, let *`as-sndr2`* be an alias
|
| 855 |
+
template such that `as-sndr2<Tag(Args...)>` denotes the type
|
| 856 |
+
`call-result-t<F, decay_t<Args>&...>`. Then `ops2_variant_t` denotes the
|
| 857 |
+
type
|
| 858 |
+
|
| 859 |
+
``` cpp
|
| 860 |
+
variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, env_t>>...>
|
| 861 |
+
```
|
| 862 |
+
|
| 863 |
+
except with duplicate types removed.
|
| 864 |
+
|
| 865 |
+
The *requires-clause* constraining the above lambda is satisfied if and
|
| 866 |
+
only if the types `args_variant_t` and `ops2_variant_t` are well-formed.
|
| 867 |
+
|
| 868 |
+
The exposition-only function template *`let-bind`* has effects
|
| 869 |
+
equivalent to:
|
| 870 |
+
|
| 871 |
+
``` cpp
|
| 872 |
+
using args_t = decayed-tuple<Args...>;
|
| 873 |
+
auto mkop2 = [&] {
|
| 874 |
+
return connect(
|
| 875 |
+
apply(std::move(state.fn),
|
| 876 |
+
state.args.template emplace<args_t>(std::forward<Args>(args)...)),
|
| 877 |
+
receiver2{rcvr, std::move(state.env)});
|
| 878 |
+
};
|
| 879 |
+
start(state.ops2.template emplace<decltype(mkop2())>(emplace-from{mkop2}));
|
| 880 |
+
```
|
| 881 |
+
|
| 882 |
+
`\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{complete}`
|
| 883 |
+
|
| 884 |
+
is initialized with a callable object equivalent to the following:
|
| 885 |
+
|
| 886 |
+
``` cpp
|
| 887 |
+
[]<class Tag, class... Args>
|
| 888 |
+
(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
|
| 889 |
+
if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
|
| 890 |
+
TRY-EVAL(rcvr, let-bind(state, rcvr, std::forward<Args>(args)...));
|
| 891 |
+
} else {
|
| 892 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 893 |
+
}
|
| 894 |
+
}
|
| 895 |
+
```
|
| 896 |
+
|
| 897 |
+
Let `sndr` and `env` be subexpressions, and let `Sndr` be
|
| 898 |
+
`decltype((sndr))`. If `sender-for<Sndr, decayed-typeof<let-cpo>>` is
|
| 899 |
+
`false`, then the expression `let-cpo.transform_env(sndr, env)` is
|
| 900 |
+
ill-formed. Otherwise, it is equal to:
|
| 901 |
+
|
| 902 |
+
``` cpp
|
| 903 |
+
auto& [_, _, child] = sndr;
|
| 904 |
+
return JOIN-ENV(let-env(child), FWD-ENV(env));
|
| 905 |
+
```
|
| 906 |
+
|
| 907 |
+
Let the subexpression `out_sndr` denote the result of the invocation
|
| 908 |
+
`let-cpo(sndr, f)` or an object equal to such, and let the subexpression
|
| 909 |
+
`rcvr` denote a receiver such that the expression
|
| 910 |
+
`connect(out_sndr, rcvr)` is well-formed. The expression
|
| 911 |
+
`connect(out_sndr, rcvr)` has undefined behavior unless it creates an
|
| 912 |
+
asynchronous operation [[exec.async.ops]] that, when started:
|
| 913 |
+
|
| 914 |
+
- invokes `f` when *`set-cpo`* is called with `sndr`’s result datums,
|
| 915 |
+
- makes its completion dependent on the completion of a sender returned
|
| 916 |
+
by `f`, and
|
| 917 |
+
- propagates the other completion operations sent by `sndr`.
|
| 918 |
+
|
| 919 |
+
#### `execution::bulk`, `execution::bulk_chunked`, and `execution::bulk_unchunked` <a id="exec.bulk">[[exec.bulk]]</a>
|
| 920 |
+
|
| 921 |
+
`bulk`, `bulk_chunked`, and `bulk_unchunked` run a task repeatedly for
|
| 922 |
+
every index in an index space.
|
| 923 |
+
|
| 924 |
+
The names `bulk`, `bulk_chunked`, and `bulk_unchunked` denote pipeable
|
| 925 |
+
sender adaptor objects. Let `bulk-algo` be either `bulk`,
|
| 926 |
+
`bulk_chunked`, or `bulk_unchunked`. For subexpressions `sndr`,
|
| 927 |
+
`policy`, `shape`, and `f`, let `Policy` be
|
| 928 |
+
`remove_cvref_t<decltype(policy)>`, `Shape` be `decltype(auto(shape))`,
|
| 929 |
+
and `Func` be `decay_t<decltype((f))>`. If
|
| 930 |
+
|
| 931 |
+
- `decltype((sndr))` does not satisfy `sender`, or
|
| 932 |
+
- `is_execution_policy_v<Policy>` is `false`, or
|
| 933 |
+
- `Shape` does not satisfy `integral`, or
|
| 934 |
+
- `Func` does not model `copy_constructible`,
|
| 935 |
+
|
| 936 |
+
`bulk-algo(sndr, policy, shape, f)` is ill-formed.
|
| 937 |
+
|
| 938 |
+
Otherwise, the expression `bulk-algo(sndr, policy, shape, f)` is
|
| 939 |
+
expression-equivalent to:
|
| 940 |
+
|
| 941 |
+
``` cpp
|
| 942 |
+
transform_sender(get-domain-early(sndr), make-sender(
|
| 943 |
+
bulk-algo, product-type<see below, Shape, Func>{policy, shape, f}, sndr))
|
| 944 |
+
```
|
| 945 |
+
|
| 946 |
+
except that `sndr` is evaluated only once. The first template argument
|
| 947 |
+
of *`product-type`* is `Policy` if `Policy` models `copy_constructible`,
|
| 948 |
+
and `const Policy&` otherwise.
|
| 949 |
+
|
| 950 |
+
Let `sndr` and `env` be subexpressions such that `Sndr` is
|
| 951 |
+
`decltype((sndr))`. If `sender-for<Sndr, bulk_t>` is `false`, then the
|
| 952 |
+
expression `bulk.transform_sender(sndr, env)` is ill-formed; otherwise,
|
| 953 |
+
it is equivalent to:
|
| 954 |
+
|
| 955 |
+
``` cpp
|
| 956 |
+
auto [_, data, child] = sndr;
|
| 957 |
+
auto& [policy, shape, f] = data;
|
| 958 |
+
auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)
|
| 959 |
+
noexcept(noexcept(f(begin, vs...))) {
|
| 960 |
+
while (begin != end) func(begin++, vs...);
|
| 961 |
+
}
|
| 962 |
+
return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
|
| 963 |
+
```
|
| 964 |
+
|
| 965 |
+
[*Note 1*: This causes the `bulk(sndr, policy, shape, f)` sender to be
|
| 966 |
+
expressed in terms of `bulk_chunked(sndr, policy, shape, f)` when it is
|
| 967 |
+
connected to a receiver whose execution domain does not customize
|
| 968 |
+
`bulk`. — *end note*]
|
| 969 |
+
|
| 970 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 971 |
+
specialized for `bulk_chunked_t` as follows:
|
| 972 |
+
|
| 973 |
+
``` cpp
|
| 974 |
+
namespace std::execution {
|
| 975 |
+
template<>
|
| 976 |
+
struct impls-for<bulk_chunked_t> : default-impls {
|
| 977 |
+
static constexpr auto complete = see below;
|
| 978 |
+
|
| 979 |
+
template<class Sndr, class... Env>
|
| 980 |
+
static consteval void check-types();
|
| 981 |
+
};
|
| 982 |
+
}
|
| 983 |
+
```
|
| 984 |
+
|
| 985 |
+
The member `impls-for<bulk_chunked_t>::complete` is initialized with a
|
| 986 |
+
callable object equivalent to the following lambda:
|
| 987 |
+
|
| 988 |
+
``` cpp
|
| 989 |
+
[]<class Index, class State, class Rcvr, class Tag, class... Args>
|
| 990 |
+
(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
|
| 991 |
+
-> void requires see below {
|
| 992 |
+
if constexpr (same_as<Tag, set_value_t>) {
|
| 993 |
+
auto& [policy, shape, f] = state;
|
| 994 |
+
constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...));
|
| 995 |
+
TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
|
| 996 |
+
f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
|
| 997 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 998 |
+
}());
|
| 999 |
+
} else {
|
| 1000 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 1001 |
+
}
|
| 1002 |
+
}
|
| 1003 |
+
```
|
| 1004 |
+
|
| 1005 |
+
The expression in the *requires-clause* of the lambda above is `true` if
|
| 1006 |
+
and only if `Tag` denotes a type other than `set_value_t` or if the
|
| 1007 |
+
expression `f(auto(shape), auto(shape), args...)` is well-formed.
|
| 1008 |
+
|
| 1009 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 1010 |
+
specialized for `bulk_unchunked_t` as follows:
|
| 1011 |
+
|
| 1012 |
+
``` cpp
|
| 1013 |
+
namespace std::execution {
|
| 1014 |
+
template<>
|
| 1015 |
+
struct impls-for<bulk_unchunked_t> : default-impls {
|
| 1016 |
+
static constexpr auto complete = see below;
|
| 1017 |
+
};
|
| 1018 |
+
}
|
| 1019 |
+
```
|
| 1020 |
+
|
| 1021 |
+
The member `impls-for<bulk_unchunked_t>::complete` is initialized with a
|
| 1022 |
+
callable object equivalent to the following lambda:
|
| 1023 |
+
|
| 1024 |
+
``` cpp
|
| 1025 |
+
[]<class Index, class State, class Rcvr, class Tag, class... Args>
|
| 1026 |
+
(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
|
| 1027 |
+
-> void requires see below {
|
| 1028 |
+
if constexpr (same_as<Tag, set_value_t>) {
|
| 1029 |
+
auto& [policy, shape, f] = state;
|
| 1030 |
+
constexpr bool nothrow = noexcept(f(auto(shape), args...));
|
| 1031 |
+
TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
|
| 1032 |
+
for (decltype(auto(shape)) i = 0; i < shape; ++i) {
|
| 1033 |
+
f(auto(i), args...);
|
| 1034 |
+
}
|
| 1035 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 1036 |
+
}());
|
| 1037 |
+
} else {
|
| 1038 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 1039 |
+
}
|
| 1040 |
+
}
|
| 1041 |
+
```
|
| 1042 |
+
|
| 1043 |
+
The expression in the *requires-clause* of the lambda above is `true` if
|
| 1044 |
+
and only if `Tag` denotes a type other than `set_value_t` or if the
|
| 1045 |
+
expression `f(auto(shape), args...)` is well-formed.
|
| 1046 |
+
|
| 1047 |
+
``` cpp
|
| 1048 |
+
template<class Sndr, class... Env>
|
| 1049 |
+
static consteval void check-types();
|
| 1050 |
+
```
|
| 1051 |
+
|
| 1052 |
+
*Effects:* Equivalent to:
|
| 1053 |
+
|
| 1054 |
+
``` cpp
|
| 1055 |
+
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
|
| 1056 |
+
auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
|
| 1057 |
+
if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts&...>)
|
| 1058 |
+
throw unspecified-exception();
|
| 1059 |
+
};
|
| 1060 |
+
cs.for-each(overload-set(fn, [](auto){}));
|
| 1061 |
+
```
|
| 1062 |
+
|
| 1063 |
+
where *`unspecified-exception`* is a type derived from `exception`.
|
| 1064 |
+
|
| 1065 |
+
Let the subexpression `out_sndr` denote the result of the invocation
|
| 1066 |
+
`bulk-algo(sndr, policy, shape, f)` or an object equal to such, and let
|
| 1067 |
+
the subexpression `rcvr` denote a receiver such that the expression
|
| 1068 |
+
`connect(out_sndr, rcvr)` is well-formed. The expression
|
| 1069 |
+
`connect(out_sndr, rcvr)` has undefined behavior unless it creates an
|
| 1070 |
+
asynchronous operation [[exec.async.ops]] that, when started:
|
| 1071 |
+
|
| 1072 |
+
- If `sndr` has a successful completion, where `args` is a pack of
|
| 1073 |
+
lvalue subexpressions referring to the value completion result datums
|
| 1074 |
+
of `sndr`, or decayed copies of those values if they model
|
| 1075 |
+
`copy_constructible`, then:
|
| 1076 |
+
- If `out_sndr` also completes successfully, then:
|
| 1077 |
+
- for `bulk`, invokes `f(i, args...)` for every i of type `Shape`
|
| 1078 |
+
from `0` to `shape`;
|
| 1079 |
+
- for `bulk_unchunked`, invokes `f(i, args...)` for every i of type
|
| 1080 |
+
`Shape` from `0` to `shape`; *Recommended practice:* The
|
| 1081 |
+
underlying scheduler should execute each iteration on a distinct
|
| 1082 |
+
execution agent.
|
| 1083 |
+
- for `bulk_chunked`, invokes `f(b, e, args...)` zero or more times
|
| 1084 |
+
with pairs of b and e of type `Shape` in range \[`0`, `shape`\],
|
| 1085 |
+
such that b < e and for every i of type `Shape` from `0` to
|
| 1086 |
+
`shape`, there is exactly one invocation with a pair b and e, such
|
| 1087 |
+
that i is in the range \[b, e).
|
| 1088 |
+
- If `out_sndr` completes with `set_error(rcvr, eptr)`, then the
|
| 1089 |
+
asynchronous operation may invoke a subset of the invocations of `f`
|
| 1090 |
+
before the error completion handler is called, and `eptr` is an
|
| 1091 |
+
`exception_ptr` containing either:
|
| 1092 |
+
- an exception thrown by an invocation of `f`, or
|
| 1093 |
+
- a `bad_alloc` exception if the implementation fails to allocate
|
| 1094 |
+
required resources, or
|
| 1095 |
+
- an exception derived from `runtime_error`.
|
| 1096 |
+
- If `out_sndr` completes with `set_stopped(rcvr)`, then the
|
| 1097 |
+
asynchronous operation may invoke a subset of the invocations of `f`
|
| 1098 |
+
before the stopped completion handler.
|
| 1099 |
+
- If `sndr` does not complete with `set_value`, then the completion is
|
| 1100 |
+
forwarded to `recv`.
|
| 1101 |
+
- For `bulk-algo`, the parameter `policy` describes the manner in which
|
| 1102 |
+
the execution of the asynchronous operations corresponding to these
|
| 1103 |
+
algorithms may be parallelized and the manner in which they apply `f`.
|
| 1104 |
+
Permissions and requirements on parallel algorithm element access
|
| 1105 |
+
functions [[algorithms.parallel.exec]] apply to `f`.
|
| 1106 |
+
|
| 1107 |
+
[*Note 2*: The asynchronous operation corresponding to
|
| 1108 |
+
`bulk-algo(sndr, policy, shape, f)` can complete with `set_stopped` if
|
| 1109 |
+
cancellation is requested or ignore cancellation
|
| 1110 |
+
requests. — *end note*]
|
| 1111 |
+
|
| 1112 |
+
#### `execution::when_all` <a id="exec.when.all">[[exec.when.all]]</a>
|
| 1113 |
+
|
| 1114 |
+
`when_all` and `when_all_with_variant` both adapt multiple input senders
|
| 1115 |
+
into a sender that completes when all input senders have completed.
|
| 1116 |
+
`when_all` only accepts senders with a single value completion signature
|
| 1117 |
+
and on success concatenates all the input senders’ value result datums
|
| 1118 |
+
into its own value completion operation.
|
| 1119 |
+
`when_all_with_variant(sndrs...)` is semantically equivalent to
|
| 1120 |
+
w`hen_all(into_variant(sndrs)...)`, where `sndrs` is a pack of
|
| 1121 |
+
subexpressions whose types model `sender`.
|
| 1122 |
+
|
| 1123 |
+
The names `when_all` and `when_all_with_variant` denote customization
|
| 1124 |
+
point objects. Let `sndrs` be a pack of subexpressions, let `Sndrs` be a
|
| 1125 |
+
pack of the types `decltype((sndrs))...`, and let `CD` be the type
|
| 1126 |
+
`common_type_t<decltype(get-domain-early(sndrs))...>`. Let `CD2` be `CD`
|
| 1127 |
+
if `CD` is well-formed, and `default_domain` otherwise. The expressions
|
| 1128 |
+
`when_all(sndrs...)` and `when_all_with_variant(sndrs...)` are
|
| 1129 |
+
ill-formed if any of the following is `true`:
|
| 1130 |
+
|
| 1131 |
+
- `sizeof...(sndrs)` is `0`, or
|
| 1132 |
+
- `(sender<Sndrs> && ...)` is `false`.
|
| 1133 |
+
|
| 1134 |
+
The expression `when_all(sndrs...)` is expression-equivalent to:
|
| 1135 |
+
|
| 1136 |
+
``` cpp
|
| 1137 |
+
transform_sender(CD2(), make-sender(when_all, {}, sndrs...))
|
| 1138 |
+
```
|
| 1139 |
+
|
| 1140 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 1141 |
+
specialized for `when_all_t` as follows:
|
| 1142 |
+
|
| 1143 |
+
``` cpp
|
| 1144 |
+
namespace std::execution {
|
| 1145 |
+
template<>
|
| 1146 |
+
struct impls-for<when_all_t> : default-impls {
|
| 1147 |
+
static constexpr auto get-attrs = see below;
|
| 1148 |
+
static constexpr auto get-env = see below;
|
| 1149 |
+
static constexpr auto get-state = see below;
|
| 1150 |
+
static constexpr auto start = see below;
|
| 1151 |
+
static constexpr auto complete = see below;
|
| 1152 |
+
|
| 1153 |
+
template<class Sndr, class... Env>
|
| 1154 |
+
static consteval void check-types();
|
| 1155 |
+
};
|
| 1156 |
+
}
|
| 1157 |
+
```
|
| 1158 |
+
|
| 1159 |
+
Let *`make-when-all-env`* be the following exposition-only function
|
| 1160 |
+
template:
|
| 1161 |
+
|
| 1162 |
+
``` cpp
|
| 1163 |
+
template<class Env>
|
| 1164 |
+
constexpr auto make-when-all-env(inplace_stop_source& stop_src, // exposition only
|
| 1165 |
+
Env&& env) noexcept {
|
| 1166 |
+
return see below;
|
| 1167 |
+
}
|
| 1168 |
+
```
|
| 1169 |
+
|
| 1170 |
+
Returns an object `e` such that
|
| 1171 |
+
|
| 1172 |
+
- `decltype(e)` models `queryable`, and
|
| 1173 |
+
- `e.query(get_stop_token)` is expression-equivalent to
|
| 1174 |
+
`state.stop-src.get_token()`, and
|
| 1175 |
+
- given a query object `q` with type other than cv `get_stop_token_t`
|
| 1176 |
+
and whose type satisfies *`forwarding-query`*, `e.query(q)` is
|
| 1177 |
+
expression-equivalent to `get_env(rcvr).query(q)`.
|
| 1178 |
+
|
| 1179 |
+
Let `when-all-env` be an alias template such that `when-all-env<Env>`
|
| 1180 |
+
denotes the type
|
| 1181 |
+
`decltype(make-{when-all-env}(declval<inplace_stop_source&>(), declval<Env>()))`.
|
| 1182 |
+
|
| 1183 |
+
``` cpp
|
| 1184 |
+
template<class Sndr, class... Env>
|
| 1185 |
+
static consteval void check-types();
|
| 1186 |
+
```
|
| 1187 |
+
|
| 1188 |
+
Let `Is` be the pack of integral template arguments of the
|
| 1189 |
+
`integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
|
| 1190 |
+
|
| 1191 |
+
*Effects:* Equivalent to:
|
| 1192 |
+
|
| 1193 |
+
``` cpp
|
| 1194 |
+
auto fn = []<class Child>() {
|
| 1195 |
+
auto cs = get_completion_signatures<Child, when-all-env<Env>...>();
|
| 1196 |
+
if constexpr (cs.count-of(set_value) >= 2)
|
| 1197 |
+
throw unspecified-exception();
|
| 1198 |
+
decay-copyable-result-datums(cs); // see [exec.snd.expos]
|
| 1199 |
+
};
|
| 1200 |
+
(fn.template operator()<child-type<Sndr, Is>>(), ...);
|
| 1201 |
+
```
|
| 1202 |
+
|
| 1203 |
+
where *`unspecified-exception`* is a type derived from `exception`.
|
| 1204 |
+
|
| 1205 |
+
*Throws:* Any exception thrown as a result of evaluating the *Effects*,
|
| 1206 |
+
or an exception of an unspecified type derived from `exception` when
|
| 1207 |
+
`CD` is ill-formed.
|
| 1208 |
+
|
| 1209 |
+
The member `impls-for<when_all_t>::get-attrs` is initialized with a
|
| 1210 |
+
callable object equivalent to the following lambda expression:
|
| 1211 |
+
|
| 1212 |
+
``` cpp
|
| 1213 |
+
[](auto&&, auto&&... child) noexcept {
|
| 1214 |
+
if constexpr (same_as<CD, default_domain>) {
|
| 1215 |
+
return env<>();
|
| 1216 |
+
} else {
|
| 1217 |
+
return MAKE-ENV(get_domain, CD());
|
| 1218 |
+
}
|
| 1219 |
+
}
|
| 1220 |
+
```
|
| 1221 |
+
|
| 1222 |
+
The member `impls-for<when_all_t>::get-env` is initialized with a
|
| 1223 |
+
callable object equivalent to the following lambda expression:
|
| 1224 |
+
|
| 1225 |
+
``` cpp
|
| 1226 |
+
[]<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {
|
| 1227 |
+
return make-when-all-env(state.stop-src, get_env(rcvr));
|
| 1228 |
+
}
|
| 1229 |
+
```
|
| 1230 |
+
|
| 1231 |
+
The member `impls-for<when_all_t>::get-state` is initialized with a
|
| 1232 |
+
callable object equivalent to the following lambda expression:
|
| 1233 |
+
|
| 1234 |
+
``` cpp
|
| 1235 |
+
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
|
| 1236 |
+
return e;
|
| 1237 |
+
}
|
| 1238 |
+
```
|
| 1239 |
+
|
| 1240 |
+
where e is the expression
|
| 1241 |
+
|
| 1242 |
+
``` cpp
|
| 1243 |
+
std::forward<Sndr>(sndr).apply(make-state<Rcvr>())
|
| 1244 |
+
```
|
| 1245 |
+
|
| 1246 |
+
and where *`make-state`* is the following exposition-only class
|
| 1247 |
+
template:
|
| 1248 |
+
|
| 1249 |
+
``` cpp
|
| 1250 |
+
enum class disposition { started, error, stopped }; // exposition only
|
| 1251 |
+
|
| 1252 |
+
template<class Rcvr>
|
| 1253 |
+
struct make-state {
|
| 1254 |
+
template<class... Sndrs>
|
| 1255 |
+
auto operator()(auto, auto, Sndrs&&... sndrs) const {
|
| 1256 |
+
using values_tuple = see below;
|
| 1257 |
+
using errors_variant = see below;
|
| 1258 |
+
using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
|
| 1259 |
+
|
| 1260 |
+
struct state-type {
|
| 1261 |
+
void arrive(Rcvr& rcvr) noexcept { // exposition only
|
| 1262 |
+
if (0 == --count) {
|
| 1263 |
+
complete(rcvr);
|
| 1264 |
+
}
|
| 1265 |
+
}
|
| 1266 |
+
|
| 1267 |
+
void complete(Rcvr& rcvr) noexcept; // exposition only
|
| 1268 |
+
|
| 1269 |
+
atomic<size_t> count{sizeof...(sndrs)}; // exposition only
|
| 1270 |
+
inplace_stop_source stop_src{}; // exposition only
|
| 1271 |
+
atomic<disposition> disp{disposition::started}; // exposition only
|
| 1272 |
+
errors_variant errors{}; // exposition only
|
| 1273 |
+
values_tuple values{}; // exposition only
|
| 1274 |
+
optional<stop_callback> on_stop{nullopt}; // exposition only
|
| 1275 |
+
};
|
| 1276 |
+
|
| 1277 |
+
return state-type{};
|
| 1278 |
+
}
|
| 1279 |
+
};
|
| 1280 |
+
```
|
| 1281 |
+
|
| 1282 |
+
Let *`copy-fail`* be `exception_ptr` if decay-copying any of the child
|
| 1283 |
+
senders’ result datums can potentially throw; otherwise, `none-such`,
|
| 1284 |
+
where `none-such` is an unspecified empty class type.
|
| 1285 |
+
|
| 1286 |
+
The alias `values_tuple` denotes the type
|
| 1287 |
+
|
| 1288 |
+
``` cpp
|
| 1289 |
+
tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>...>
|
| 1290 |
+
```
|
| 1291 |
+
|
| 1292 |
+
if that type is well-formed; otherwise, `tuple<>`.
|
| 1293 |
+
|
| 1294 |
+
The alias `errors_variant` denotes the type
|
| 1295 |
+
`variant<none-such, copy-fail, Es...>` with duplicate types removed,
|
| 1296 |
+
where `Es` is the pack of the decayed types of all the child senders’
|
| 1297 |
+
possible error result datums.
|
| 1298 |
+
|
| 1299 |
+
The member `void state-type::complete(Rcvr& rcvr) noexcept` behaves as
|
| 1300 |
+
follows:
|
| 1301 |
+
|
| 1302 |
+
- If `disp` is equal to `disposition::started`, evaluates:
|
| 1303 |
+
``` cpp
|
| 1304 |
+
auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };
|
| 1305 |
+
auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
|
| 1306 |
+
|
| 1307 |
+
on_stop.reset();
|
| 1308 |
+
apply(
|
| 1309 |
+
[&](auto&... opts) noexcept {
|
| 1310 |
+
apply(set, tuple_cat(tie(*opts)...));
|
| 1311 |
+
},
|
| 1312 |
+
values);
|
| 1313 |
+
```
|
| 1314 |
+
- Otherwise, if `disp` is equal to `disposition::error`, evaluates:
|
| 1315 |
+
``` cpp
|
| 1316 |
+
on_stop.reset();
|
| 1317 |
+
visit(
|
| 1318 |
+
[&]<class Error>(Error& error) noexcept {
|
| 1319 |
+
if constexpr (!same_as<Error, none-such>) {
|
| 1320 |
+
set_error(std::move(rcvr), std::move(error));
|
| 1321 |
+
}
|
| 1322 |
+
},
|
| 1323 |
+
errors);
|
| 1324 |
+
```
|
| 1325 |
+
- Otherwise, evaluates:
|
| 1326 |
+
``` cpp
|
| 1327 |
+
on_stop.reset();
|
| 1328 |
+
set_stopped(std::move(rcvr));
|
| 1329 |
+
```
|
| 1330 |
+
|
| 1331 |
+
The member `impls-for<when_all_t>::start` is initialized with a callable
|
| 1332 |
+
object equivalent to the following lambda expression:
|
| 1333 |
+
|
| 1334 |
+
``` cpp
|
| 1335 |
+
[]<class State, class Rcvr, class... Ops>(
|
| 1336 |
+
State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void {
|
| 1337 |
+
state.on_stop.emplace(
|
| 1338 |
+
get_stop_token(get_env(rcvr)),
|
| 1339 |
+
on-stop-request{state.stop_src});
|
| 1340 |
+
if (state.stop_src.stop_requested()) {
|
| 1341 |
+
state.on_stop.reset();
|
| 1342 |
+
set_stopped(std::move(rcvr));
|
| 1343 |
+
} else {
|
| 1344 |
+
(start(ops), ...);
|
| 1345 |
+
}
|
| 1346 |
+
}
|
| 1347 |
+
```
|
| 1348 |
+
|
| 1349 |
+
The member `impls-for<when_all_t>::complete` is initialized with a
|
| 1350 |
+
callable object equivalent to the following lambda expression:
|
| 1351 |
+
|
| 1352 |
+
``` cpp
|
| 1353 |
+
[]<class Index, class State, class Rcvr, class Set, class... Args>(
|
| 1354 |
+
this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {
|
| 1355 |
+
if constexpr (same_as<Set, set_error_t>) {
|
| 1356 |
+
if (disposition::error != state.disp.exchange(disposition::error)) {
|
| 1357 |
+
state.stop_src.request_stop();
|
| 1358 |
+
TRY-EMPLACE-ERROR(state.errors, std::forward<Args>(args)...);
|
| 1359 |
+
}
|
| 1360 |
+
} else if constexpr (same_as<Set, set_stopped_t>) {
|
| 1361 |
+
auto expected = disposition::started;
|
| 1362 |
+
if (state.disp.compare_exchange_strong(expected, disposition::stopped)) {
|
| 1363 |
+
state.stop_src.request_stop();
|
| 1364 |
+
}
|
| 1365 |
+
} else if constexpr (!same_as<decltype(State::values), tuple<>>) {
|
| 1366 |
+
if (state.disp == disposition::started) {
|
| 1367 |
+
auto& opt = get<Index::value>(state.values);
|
| 1368 |
+
TRY-EMPLACE-VALUE(complete, opt, std::forward<Args>(args)...);
|
| 1369 |
+
}
|
| 1370 |
+
}
|
| 1371 |
+
state.arrive(rcvr);
|
| 1372 |
+
}
|
| 1373 |
+
```
|
| 1374 |
+
|
| 1375 |
+
where `TRY-EMPLACE-ERROR(v, e)`, for subexpressions `v` and `e`, is
|
| 1376 |
+
equivalent to:
|
| 1377 |
+
|
| 1378 |
+
``` cpp
|
| 1379 |
+
try {
|
| 1380 |
+
v.template emplace<decltype(auto(e))>(e);
|
| 1381 |
+
} catch (...) {
|
| 1382 |
+
v.template emplace<exception_ptr>(current_exception());
|
| 1383 |
+
}
|
| 1384 |
+
```
|
| 1385 |
+
|
| 1386 |
+
if the expression `decltype(auto(e))(e)` is potentially throwing;
|
| 1387 |
+
otherwise, `v.template emplace<decltype(auto(e))>(e)`; and where
|
| 1388 |
+
`TRY-EMPLACE-VALUE(c, o, as...)`, for subexpressions `c`, `o`, and pack
|
| 1389 |
+
of subexpressions `as`, is equivalent to:
|
| 1390 |
+
|
| 1391 |
+
``` cpp
|
| 1392 |
+
try {
|
| 1393 |
+
o.emplace(as...);
|
| 1394 |
+
} catch (...) {
|
| 1395 |
+
c(Index(), state, rcvr, set_error, current_exception());
|
| 1396 |
+
return;
|
| 1397 |
+
}
|
| 1398 |
+
```
|
| 1399 |
+
|
| 1400 |
+
if the expression `decayed-tuple<decltype(as)...>{as...}` is potentially
|
| 1401 |
+
throwing; otherwise, `o.emplace(as...)`.
|
| 1402 |
+
|
| 1403 |
+
The expression `when_all_with_variant(sndrs...)` is
|
| 1404 |
+
expression-equivalent to:
|
| 1405 |
+
|
| 1406 |
+
``` cpp
|
| 1407 |
+
transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));
|
| 1408 |
+
```
|
| 1409 |
+
|
| 1410 |
+
Given subexpressions `sndr` and `env`, if
|
| 1411 |
+
`sender-for<decltype((sndr)), when_all_with_variant_t>` is `false`, then
|
| 1412 |
+
the expression `when_all_with_variant.transform_sender(sndr, env)` is
|
| 1413 |
+
ill-formed; otherwise, it is equivalent to:
|
| 1414 |
+
|
| 1415 |
+
``` cpp
|
| 1416 |
+
auto&& [_, _, ...child] = sndr;
|
| 1417 |
+
return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
|
| 1418 |
+
```
|
| 1419 |
+
|
| 1420 |
+
[*Note 1*: This causes the `when_all_with_variant(sndrs...)` sender to
|
| 1421 |
+
become `when_all(into_variant(sndrs)...)` when it is connected with a
|
| 1422 |
+
receiver whose execution domain does not customize
|
| 1423 |
+
`when_all_with_variant`. — *end note*]
|
| 1424 |
+
|
| 1425 |
+
#### `execution::into_variant` <a id="exec.into.variant">[[exec.into.variant]]</a>
|
| 1426 |
+
|
| 1427 |
+
`into_variant` adapts a sender with multiple value completion signatures
|
| 1428 |
+
into a sender with just one value completion signature consisting of a
|
| 1429 |
+
`variant` of `tuple`s.
|
| 1430 |
+
|
| 1431 |
+
The name `into_variant` denotes a pipeable sender adaptor object. For a
|
| 1432 |
+
subexpression `sndr`, let `Sndr` be `decltype((sndr))`. If `Sndr` does
|
| 1433 |
+
not satisfy `sender`, `into_variant(sndr)` is ill-formed.
|
| 1434 |
+
|
| 1435 |
+
Otherwise, the expression `into_variant(sndr)` is expression-equivalent
|
| 1436 |
+
to:
|
| 1437 |
+
|
| 1438 |
+
``` cpp
|
| 1439 |
+
transform_sender(get-domain-early(sndr), make-sender(into_variant, {}, sndr))
|
| 1440 |
+
```
|
| 1441 |
+
|
| 1442 |
+
except that `sndr` is only evaluated once.
|
| 1443 |
+
|
| 1444 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 1445 |
+
specialized for `into_variant` as follows:
|
| 1446 |
+
|
| 1447 |
+
``` cpp
|
| 1448 |
+
namespace std::execution {
|
| 1449 |
+
template<>
|
| 1450 |
+
struct impls-for<into_variant_t> : default-impls {
|
| 1451 |
+
static constexpr auto get-state = see below;
|
| 1452 |
+
static constexpr auto complete = see below;
|
| 1453 |
+
|
| 1454 |
+
template<class Sndr, class... Env>
|
| 1455 |
+
static consteval void check-types() {
|
| 1456 |
+
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
|
| 1457 |
+
decay-copyable-result-datums(cs); // see [exec.snd.expos]
|
| 1458 |
+
}
|
| 1459 |
+
};
|
| 1460 |
+
}
|
| 1461 |
+
```
|
| 1462 |
+
|
| 1463 |
+
The member `impls-for<into_variant_t>::get-state` is initialized with a
|
| 1464 |
+
callable object equivalent to the following lambda:
|
| 1465 |
+
|
| 1466 |
+
``` cpp
|
| 1467 |
+
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept
|
| 1468 |
+
-> type_identity<value_types_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>> {
|
| 1469 |
+
return {};
|
| 1470 |
+
}
|
| 1471 |
+
```
|
| 1472 |
+
|
| 1473 |
+
The member `impls-for<into_variant_t>::complete` is initialized with a
|
| 1474 |
+
callable object equivalent to the following lambda:
|
| 1475 |
+
|
| 1476 |
+
``` cpp
|
| 1477 |
+
[]<class State, class Rcvr, class Tag, class... Args>(
|
| 1478 |
+
auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {
|
| 1479 |
+
if constexpr (same_as<Tag, set_value_t>) {
|
| 1480 |
+
using variant_type = State::type;
|
| 1481 |
+
TRY-SET-VALUE(rcvr, variant_type(decayed-tuple<Args...>{std::forward<Args>(args)...}));
|
| 1482 |
+
} else {
|
| 1483 |
+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
|
| 1484 |
+
}
|
| 1485 |
+
}
|
| 1486 |
+
```
|
| 1487 |
+
|
| 1488 |
+
#### `execution::stopped_as_optional` <a id="exec.stopped.opt">[[exec.stopped.opt]]</a>
|
| 1489 |
+
|
| 1490 |
+
`stopped_as_optional` maps a sender’s stopped completion operation into
|
| 1491 |
+
a value completion operation as a disengaged `optional`. The sender’s
|
| 1492 |
+
value completion operation is also converted into an `optional`. The
|
| 1493 |
+
result is a sender that never completes with stopped, reporting
|
| 1494 |
+
cancellation by completing with a disengaged `optional`.
|
| 1495 |
+
|
| 1496 |
+
The name `stopped_as_optional` denotes a pipeable sender adaptor object.
|
| 1497 |
+
For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. The
|
| 1498 |
+
expression `stopped_as_optional(sndr)` is expression-equivalent to:
|
| 1499 |
+
|
| 1500 |
+
``` cpp
|
| 1501 |
+
transform_sender(get-domain-early(sndr), make-sender(stopped_as_optional, {}, sndr))
|
| 1502 |
+
```
|
| 1503 |
+
|
| 1504 |
+
except that `sndr` is only evaluated once.
|
| 1505 |
+
|
| 1506 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 1507 |
+
specialized for `stopped_as_optional_t` as follows:
|
| 1508 |
+
|
| 1509 |
+
``` cpp
|
| 1510 |
+
namespace std::execution {
|
| 1511 |
+
template<>
|
| 1512 |
+
struct impls-for<stopped_as_optional_t> : default-impls {
|
| 1513 |
+
template<class Sndr, class... Env>
|
| 1514 |
+
static consteval void check-types() {
|
| 1515 |
+
default-impls::check-types<Sndr, Env...>();
|
| 1516 |
+
if constexpr (!requires {
|
| 1517 |
+
requires (!same_as<void, single-sender-value-type<child-type<Sndr>,
|
| 1518 |
+
FWD-ENV-T(Env)...>>); })
|
| 1519 |
+
throw unspecified-exception();
|
| 1520 |
+
}
|
| 1521 |
+
};
|
| 1522 |
+
}
|
| 1523 |
+
```
|
| 1524 |
+
|
| 1525 |
+
where `unspecified-exception` is a type derived from `exception`.
|
| 1526 |
+
|
| 1527 |
+
Let `sndr` and `env` be subexpressions such that `Sndr` is
|
| 1528 |
+
`decltype((sndr))` and `Env` is `decltype((env))`. If
|
| 1529 |
+
`sender-for<Sndr, stopped_as_optional_t>` is `false` then the expression
|
| 1530 |
+
`stopped_as_optional.transform_sender(sndr, env)` is ill-formed;
|
| 1531 |
+
otherwise, if `sender_in<child-type<Sndr>, FWD-ENV-T(Env)>` is `false`,
|
| 1532 |
+
the expression `stopped_as_optional.transform_sender(sndr, env)` is
|
| 1533 |
+
equivalent to `not-a-sender()`; otherwise, it is equivalent to:
|
| 1534 |
+
|
| 1535 |
+
``` cpp
|
| 1536 |
+
auto&& [_, _, child] = sndr;
|
| 1537 |
+
using V = single-sender-value-type<child-type<Sndr>, FWD-ENV-T(Env)>;
|
| 1538 |
+
return let_stopped(
|
| 1539 |
+
then(std::forward_like<Sndr>(child),
|
| 1540 |
+
[]<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {
|
| 1541 |
+
return optional<V>(in_place, std::forward<Ts>(ts)...);
|
| 1542 |
+
}),
|
| 1543 |
+
[]() noexcept { return just(optional<V>()); });
|
| 1544 |
+
```
|
| 1545 |
+
|
| 1546 |
+
#### `execution::stopped_as_error` <a id="exec.stopped.err">[[exec.stopped.err]]</a>
|
| 1547 |
+
|
| 1548 |
+
`stopped_as_error` maps an input sender’s stopped completion operation
|
| 1549 |
+
into an error completion operation as a custom error type. The result is
|
| 1550 |
+
a sender that never completes with stopped, reporting cancellation by
|
| 1551 |
+
completing with an error.
|
| 1552 |
+
|
| 1553 |
+
The name `stopped_as_error` denotes a pipeable sender adaptor object.
|
| 1554 |
+
For some subexpressions `sndr` and `err`, let `Sndr` be
|
| 1555 |
+
`decltype((sndr))` and let `Err` be `decltype((err))`. If the type
|
| 1556 |
+
`Sndr` does not satisfy `sender` or if the type `Err` does not satisfy
|
| 1557 |
+
`movable-value`, `stopped_as_error(sndr, err)` is ill-formed. Otherwise,
|
| 1558 |
+
the expression `stopped_as_error(sndr, err)` is expression-equivalent
|
| 1559 |
+
to:
|
| 1560 |
+
|
| 1561 |
+
``` cpp
|
| 1562 |
+
transform_sender(get-domain-early(sndr), make-sender(stopped_as_error, err, sndr))
|
| 1563 |
+
```
|
| 1564 |
+
|
| 1565 |
+
except that `sndr` is only evaluated once.
|
| 1566 |
+
|
| 1567 |
+
Let `sndr` and `env` be subexpressions such that `Sndr` is
|
| 1568 |
+
`decltype((sndr))` and `Env` is `decltype((env))`. If
|
| 1569 |
+
`sender-for<Sndr, stopped_as_error_t>` is `false`, then the expression
|
| 1570 |
+
`stopped_as_error.transform_sender(sndr, env)` is ill-formed; otherwise,
|
| 1571 |
+
it is equivalent to:
|
| 1572 |
+
|
| 1573 |
+
``` cpp
|
| 1574 |
+
auto&& [_, err, child] = sndr;
|
| 1575 |
+
using E = decltype(auto(err));
|
| 1576 |
+
return let_stopped(
|
| 1577 |
+
std::forward_like<Sndr>(child),
|
| 1578 |
+
[err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {
|
| 1579 |
+
return just_error(std::move(err));
|
| 1580 |
+
});
|
| 1581 |
+
```
|
| 1582 |
+
|
| 1583 |
+
#### `execution::associate` <a id="exec.associate">[[exec.associate]]</a>
|
| 1584 |
+
|
| 1585 |
+
`associate` tries to associate a sender with an async scope such that
|
| 1586 |
+
the scope can track the lifetime of any asynchronous operations created
|
| 1587 |
+
with the sender.
|
| 1588 |
+
|
| 1589 |
+
Let *`associate-data`* be the following exposition-only class template:
|
| 1590 |
+
|
| 1591 |
+
``` cpp
|
| 1592 |
+
namespace std::execution {
|
| 1593 |
+
template<scope_token Token, sender Sender>
|
| 1594 |
+
struct associate-data { // exposition only
|
| 1595 |
+
using wrap-sender = // exposition only
|
| 1596 |
+
remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>;
|
| 1597 |
+
|
| 1598 |
+
explicit associate-data(Token t, Sender&& s)
|
| 1599 |
+
: sndr(t.wrap(std::forward<Sender>(s))),
|
| 1600 |
+
token(t) {
|
| 1601 |
+
if (!token.try_associate())
|
| 1602 |
+
sndr.reset();
|
| 1603 |
+
}
|
| 1604 |
+
|
| 1605 |
+
associate-data(const associate-data& other)
|
| 1606 |
+
noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
|
| 1607 |
+
noexcept(other.token.try_associate()));
|
| 1608 |
+
|
| 1609 |
+
associate-data(associate-data&& other)
|
| 1610 |
+
noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
| 1611 |
+
|
| 1612 |
+
~associate-data();
|
| 1613 |
+
|
| 1614 |
+
optional<pair<Token, wrap-sender>>
|
| 1615 |
+
release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
| 1616 |
+
|
| 1617 |
+
private:
|
| 1618 |
+
optional<wrap-sender> sndr; // exposition only
|
| 1619 |
+
Token token; // exposition only
|
| 1620 |
+
};
|
| 1621 |
+
|
| 1622 |
+
template<scope_token Token, sender Sender>
|
| 1623 |
+
associate-data(Token, Sender&&) -> associate-data<Token, Sender>;
|
| 1624 |
+
}
|
| 1625 |
+
```
|
| 1626 |
+
|
| 1627 |
+
For an *`associate-data`* object `a`, `a.sndr.has_value()` is `true` if
|
| 1628 |
+
and only if an association was successfully made and is owned by `a`.
|
| 1629 |
+
|
| 1630 |
+
``` cpp
|
| 1631 |
+
associate-data(const associate-data& other)
|
| 1632 |
+
noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
|
| 1633 |
+
noexcept(other.token.try_associate()));
|
| 1634 |
+
```
|
| 1635 |
+
|
| 1636 |
+
*Constraints:* `copy_constructible<`*`wrap-sender`*`>` is `true`.
|
| 1637 |
+
|
| 1638 |
+
*Effects:* Value-initializes *sndr* and initializes *token* with
|
| 1639 |
+
`other.`*`token`*. If `other.`*`sndr`*`.has_value()` is `false`, no
|
| 1640 |
+
further effects; otherwise, calls *`token`*`.try_associate()` and, if
|
| 1641 |
+
that returns `true`, calls *`sndr`*`.emplace(*other.`*`sndr`*`)` and, if
|
| 1642 |
+
that exits with an exception, calls *`token`*`.disassociate()` before
|
| 1643 |
+
propagating the exception.
|
| 1644 |
+
|
| 1645 |
+
``` cpp
|
| 1646 |
+
associate-data(associate-data&& other)
|
| 1647 |
+
noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
| 1648 |
+
```
|
| 1649 |
+
|
| 1650 |
+
*Effects:* Initializes *sndr* with `std::move(other.`*`sndr`*`)` and
|
| 1651 |
+
initializes *token* with `std::move(other.`*`token`*`)` and then calls
|
| 1652 |
+
`other.`*`sndr`*`.reset()`.
|
| 1653 |
+
|
| 1654 |
+
``` cpp
|
| 1655 |
+
~associate-data();
|
| 1656 |
+
```
|
| 1657 |
+
|
| 1658 |
+
*Effects:* If *`sndr`*`.has_value()` returns `false` then no effect;
|
| 1659 |
+
otherwise, invokes *`sndr`*`.reset()` before invoking
|
| 1660 |
+
*`token`*`.disassociate()`.
|
| 1661 |
+
|
| 1662 |
+
``` cpp
|
| 1663 |
+
optional<pair<Token, wrap-sender>>
|
| 1664 |
+
release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
| 1665 |
+
```
|
| 1666 |
+
|
| 1667 |
+
*Effects:* If *`sndr`*`.has_value()` returns `false` then returns an
|
| 1668 |
+
`optional` that does not contain a value; otherwise returns an
|
| 1669 |
+
`optional` containing a value of type `pair<Token, `*`wrap-sender`*`>`
|
| 1670 |
+
as if by:
|
| 1671 |
+
|
| 1672 |
+
``` cpp
|
| 1673 |
+
return optional(pair(token, std::move(*sndr)));
|
| 1674 |
+
```
|
| 1675 |
+
|
| 1676 |
+
*Ensures:* *sndr* does not contain a value.
|
| 1677 |
+
|
| 1678 |
+
The name `associate` denotes a pipeable sender adaptor object. For
|
| 1679 |
+
subexpressions `sndr` and `token`:
|
| 1680 |
+
|
| 1681 |
+
- If `decltype((sndr))` does not satisfy `sender`, or
|
| 1682 |
+
`remove_cvref_t<decltype((token))>` does not satisfy `scope_token`,
|
| 1683 |
+
then `associate(sndr, token)` is ill-formed.
|
| 1684 |
+
- Otherwise, the expression `associate(sndr, token)` is
|
| 1685 |
+
expression-equivalent to:
|
| 1686 |
+
``` cpp
|
| 1687 |
+
transform_sender(get-domain-early(sndr),
|
| 1688 |
+
make-sender(associate, associate-data(token, sndr)))
|
| 1689 |
+
```
|
| 1690 |
+
|
| 1691 |
+
except that `sndr` is evaluated only once.
|
| 1692 |
+
|
| 1693 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 1694 |
+
specialized for `associate_t` as follows:
|
| 1695 |
+
|
| 1696 |
+
``` cpp
|
| 1697 |
+
namespace std::execution {
|
| 1698 |
+
template<>
|
| 1699 |
+
struct impls-for<associate_t> : default-impls {
|
| 1700 |
+
static constexpr auto get-state = see below; // exposition only
|
| 1701 |
+
static constexpr auto start = see below; // exposition only
|
| 1702 |
+
|
| 1703 |
+
template<class Sndr, class... Env>
|
| 1704 |
+
static consteval void check-types() { // exposition only
|
| 1705 |
+
using associate_data_t = remove_cvref_t<data-type<Sndr>>;
|
| 1706 |
+
using child_type_t = associate_data_t::wrap-sender;
|
| 1707 |
+
(void)get_completion_signatures<child_type_t, FWD-ENV-T(Env)...>();
|
| 1708 |
+
}
|
| 1709 |
+
};
|
| 1710 |
+
}
|
| 1711 |
+
```
|
| 1712 |
+
|
| 1713 |
+
The member `impls-for<associate_t>::get-state` is initialized with a
|
| 1714 |
+
callable object equivalent to the following lambda:
|
| 1715 |
+
|
| 1716 |
+
``` cpp
|
| 1717 |
+
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below) {
|
| 1718 |
+
auto [_, data] = std::forward<Sndr>(sndr);
|
| 1719 |
+
auto dataParts = std::move(data).release();
|
| 1720 |
+
|
| 1721 |
+
using scope_tkn = decltype(dataParts->first);
|
| 1722 |
+
using wrap_sender = decltype(dataParts->second);
|
| 1723 |
+
using op_t = connect_result_t<wrap_sender, Rcvr>;
|
| 1724 |
+
|
| 1725 |
+
struct op_state {
|
| 1726 |
+
bool associated = false; // exposition only
|
| 1727 |
+
union {
|
| 1728 |
+
Rcvr* rcvr; // exposition only
|
| 1729 |
+
struct {
|
| 1730 |
+
scope_tkn token; // exposition only
|
| 1731 |
+
op_t op; // exposition only
|
| 1732 |
+
} assoc; // exposition only
|
| 1733 |
+
};
|
| 1734 |
+
|
| 1735 |
+
explicit op_state(Rcvr& r) noexcept
|
| 1736 |
+
: rcvr(addressof(r)) {}
|
| 1737 |
+
|
| 1738 |
+
explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try
|
| 1739 |
+
: associated(true),
|
| 1740 |
+
assoc(tkn, connect(std::move(sndr), std::move(r))) {
|
| 1741 |
+
}
|
| 1742 |
+
catch (...) {
|
| 1743 |
+
tkn.disassociate();
|
| 1744 |
+
throw;
|
| 1745 |
+
}
|
| 1746 |
+
|
| 1747 |
+
op_state(op_state&&) = delete;
|
| 1748 |
+
|
| 1749 |
+
~op_state() {
|
| 1750 |
+
if (associated) {
|
| 1751 |
+
assoc.op.~op_t();
|
| 1752 |
+
assoc.token.disassociate();
|
| 1753 |
+
assoc.token.~scope_tkn();
|
| 1754 |
+
}
|
| 1755 |
+
}
|
| 1756 |
+
|
| 1757 |
+
void run() noexcept { // exposition only
|
| 1758 |
+
if (associated)
|
| 1759 |
+
start(assoc.op);
|
| 1760 |
+
else
|
| 1761 |
+
set_stopped(std::move(*rcvr));
|
| 1762 |
+
}
|
| 1763 |
+
};
|
| 1764 |
+
|
| 1765 |
+
if (dataParts)
|
| 1766 |
+
return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr};
|
| 1767 |
+
else
|
| 1768 |
+
return op_state{rcvr};
|
| 1769 |
+
}
|
| 1770 |
+
```
|
| 1771 |
+
|
| 1772 |
+
The expression in the `noexcept` clause of
|
| 1773 |
+
`impls-for<associate_t>::get-state` is
|
| 1774 |
+
|
| 1775 |
+
``` cpp
|
| 1776 |
+
is_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> &&
|
| 1777 |
+
is_nothrow_move_constructible_v<wrap-sender> &&
|
| 1778 |
+
nothrow-callable<connect_t, wrap-sender, Rcvr>
|
| 1779 |
+
```
|
| 1780 |
+
|
| 1781 |
+
where *`wrap-sender`* is the type
|
| 1782 |
+
`remove_cvref_t<data-type<Sndr>>::wrap-sender`.
|
| 1783 |
+
|
| 1784 |
+
The member `impls-for<associate_t>::start` is initialized with a
|
| 1785 |
+
callable object equivalent to the following lambda:
|
| 1786 |
+
|
| 1787 |
+
``` cpp
|
| 1788 |
+
[](auto& state, auto&) noexcept -> void {
|
| 1789 |
+
state.run();
|
| 1790 |
+
}
|
| 1791 |
+
```
|
| 1792 |
+
|
| 1793 |
+
The evaluation of `associate(sndr, token)` may cause side effects
|
| 1794 |
+
observable via `token`'s associated async scope object.
|
| 1795 |
+
|
| 1796 |
+
#### Exposition-only `execution::stop-when` <a id="exec.stop.when">[[exec.stop.when]]</a>
|
| 1797 |
+
|
| 1798 |
+
*`stop-when`* fuses an additional stop token `t` into a sender so that,
|
| 1799 |
+
upon connecting to a receiver `r`, the resulting operation state
|
| 1800 |
+
receives stop requests from both `t` and the token returned from
|
| 1801 |
+
`get_stop_token(get_env(r))`.
|
| 1802 |
+
|
| 1803 |
+
The name *`stop-when`* denotes an exposition-only sender adaptor. For
|
| 1804 |
+
subexpressions `sndr` and `token`:
|
| 1805 |
+
|
| 1806 |
+
- If `decltype((sndr))` does not satisfy `sender`, or
|
| 1807 |
+
`remove_cvref_t<decltype((token))>` does not satisfy
|
| 1808 |
+
`stoppable_token`, then `stop-when(sndr, token)` is ill-formed.
|
| 1809 |
+
- Otherwise, if `remove_cvref_t<decltype((token))>` models
|
| 1810 |
+
`unstoppable_token` then `stop-when({}sndr, token)` is
|
| 1811 |
+
expression-equivalent to `sndr`.
|
| 1812 |
+
- Otherwise, `stop-when(sndr, token)` returns a sender `osndr`. If
|
| 1813 |
+
`osndr` is connected to a receiver `r`, let `rtoken` be the result of
|
| 1814 |
+
`get_stop_token(get_env(r))`.
|
| 1815 |
+
- If the type of `rtoken` models `unstoppable_token` then the effects
|
| 1816 |
+
of connecting `osndr` to `r` are equivalent to
|
| 1817 |
+
`connect(write_env(sndr, prop(get_stop_token, token)), r)`.
|
| 1818 |
+
- Otherwise, the effects of connecting `osndr` to `r` are equivalent
|
| 1819 |
+
to `connect(write_env(sndr, prop(get_stop_token, stoken)), r)` where
|
| 1820 |
+
`stoken` is an object of an exposition-only type *`stoken-t`* such
|
| 1821 |
+
that:
|
| 1822 |
+
- *`stoken-t`* models `stoppable_token`;
|
| 1823 |
+
- `stoken.stop_requested()` returns
|
| 1824 |
+
`token.stop_requested() || rtoken.stop_reques-{}ted()`;
|
| 1825 |
+
- `stoken.stop_possible()` returns
|
| 1826 |
+
`token.stop_possible() || rtoken.stop_possible()`; and
|
| 1827 |
+
- for types `Fn` and `Init` such that both `invocable<Fn>` and
|
| 1828 |
+
`constructible_from<Fn, Init>` are modeled,
|
| 1829 |
+
`stoken-t::callback_type<Fn>` models
|
| 1830 |
+
`stoppable-callback-for<Fn, stoken-t, Init>`.\begin{tailnote}
|
| 1831 |
+
For an object \texttt{fn} of type \texttt{Fn}
|
| 1832 |
+
constructed from a value, \texttt{init}, of type \texttt{Init},
|
| 1833 |
+
registering \texttt{fn} using
|
| 1834 |
+
\texttt{\exposid{stoken-t}::callback_type\<Fn\>(stoken, init)}
|
| 1835 |
+
results in an invocation of \texttt{fn} when
|
| 1836 |
+
a callback registered with \texttt{token} or \texttt{rtoken} would be invoked.
|
| 1837 |
+
\texttt{fn} is invoked at most once.
|
| 1838 |
+
\end{tailnote}
|
| 1839 |
+
|
| 1840 |
+
#### `execution::spawn_future` <a id="exec.spawn.future">[[exec.spawn.future]]</a>
|
| 1841 |
+
|
| 1842 |
+
`spawn_future` attempts to associate the given input sender with the
|
| 1843 |
+
given token’s async scope and, on success, eagerly starts the input
|
| 1844 |
+
sender; the return value is a sender that, when connected and started,
|
| 1845 |
+
completes with either the result of the eagerly-started input sender or
|
| 1846 |
+
with `set_stopped` if the input sender was not started.
|
| 1847 |
+
|
| 1848 |
+
The name `spawn_future` denotes a customization point object. For
|
| 1849 |
+
subexpressions `sndr`, `token`, and `env`,
|
| 1850 |
+
|
| 1851 |
+
- let `Sndr` be `decltype((sndr))`,
|
| 1852 |
+
- let `Token` be `remove_cvref_t<decltype((token))>`, and
|
| 1853 |
+
- let `Env` be `remove_cvref_t<decltype((env))>`.
|
| 1854 |
+
|
| 1855 |
+
If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
|
| 1856 |
+
not satisfied, the expression `spawn_future(sndr, token, env)` is
|
| 1857 |
+
ill-formed.
|
| 1858 |
+
|
| 1859 |
+
Let *`spawn-future-state-base`* be the exposition-only class template:
|
| 1860 |
+
|
| 1861 |
+
``` cpp
|
| 1862 |
+
namespace std::execution {
|
| 1863 |
+
template<class Completions>
|
| 1864 |
+
struct spawn-future-state-base; // exposition only
|
| 1865 |
+
|
| 1866 |
+
template<class... Sigs>
|
| 1867 |
+
struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition only
|
| 1868 |
+
using variant-t = see below; // exposition only
|
| 1869 |
+
variant-t result; // exposition only
|
| 1870 |
+
virtual void complete() noexcept = 0; // exposition only
|
| 1871 |
+
};
|
| 1872 |
+
}
|
| 1873 |
+
```
|
| 1874 |
+
|
| 1875 |
+
Let `Sigs` be the pack of arguments to the `completion_signatures`
|
| 1876 |
+
specialization provided as a parameter to the
|
| 1877 |
+
*`spawn-future-state-base`* class template. Let *`as-tuple`* be an alias
|
| 1878 |
+
template that transforms a completion signature `Tag(Args...)` into the
|
| 1879 |
+
tuple specialization `decayed-tuple<Tag, Args...>`.
|
| 1880 |
+
|
| 1881 |
+
- If `is_nothrow_constructible_v<decay_t<Arg>, Arg>` is `true` for every
|
| 1882 |
+
type `Arg` in every parameter pack `Args` in every completion
|
| 1883 |
+
signature `Tag(Args...)` in `Sigs` then *`variant-t`* denotes the type
|
| 1884 |
+
`variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>`, except
|
| 1885 |
+
with duplicate types removed.
|
| 1886 |
+
- Otherwise *`variant-t`* denotes the type
|
| 1887 |
+
`variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>`,
|
| 1888 |
+
except with duplicate types removed.
|
| 1889 |
+
|
| 1890 |
+
Let *`spawn-future-receiver`* be the exposition-only class template:
|
| 1891 |
+
|
| 1892 |
+
``` cpp
|
| 1893 |
+
namespace std::execution {
|
| 1894 |
+
template<class Completions>
|
| 1895 |
+
struct spawn-future-receiver { // exposition only
|
| 1896 |
+
using receiver_concept = receiver_t;
|
| 1897 |
+
|
| 1898 |
+
spawn-future-state-base<Completions>* state; // exposition only
|
| 1899 |
+
|
| 1900 |
+
template<class... T>
|
| 1901 |
+
void set_value(T&&... t) && noexcept {
|
| 1902 |
+
set-complete<set_value_t>(std::forward<T>(t)...);
|
| 1903 |
+
}
|
| 1904 |
+
|
| 1905 |
+
template<class E>
|
| 1906 |
+
void set_error(E&& e) && noexcept {
|
| 1907 |
+
set-complete<set_error_t>(std::forward<E>(e));
|
| 1908 |
+
}
|
| 1909 |
+
|
| 1910 |
+
void set_stopped() && noexcept {
|
| 1911 |
+
set-complete<set_stopped_t>();
|
| 1912 |
+
}
|
| 1913 |
+
|
| 1914 |
+
private:
|
| 1915 |
+
template<class CPO, class... T>
|
| 1916 |
+
void set-complete(T&&... t) noexcept { // exposition only
|
| 1917 |
+
constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...);
|
| 1918 |
+
try {
|
| 1919 |
+
state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{},
|
| 1920 |
+
std::forward<T>(t)...);
|
| 1921 |
+
}
|
| 1922 |
+
catch (...) {
|
| 1923 |
+
if constexpr (!nothrow) {
|
| 1924 |
+
using tuple_t = decayed-tuple<set_error_t, exception_ptr>;
|
| 1925 |
+
state->result.template emplace<tuple_t>(set_error_t{}, current_exception());
|
| 1926 |
+
}
|
| 1927 |
+
}
|
| 1928 |
+
state->complete();
|
| 1929 |
+
}
|
| 1930 |
+
};
|
| 1931 |
+
}
|
| 1932 |
+
```
|
| 1933 |
+
|
| 1934 |
+
Let `ssource-t` be an unspecified type that models `stoppable-source`
|
| 1935 |
+
and let `ssource` be an lvalue of type `ssource-t`. Let `stoken-t` be
|
| 1936 |
+
`decltype(ssource.get_token())`. Let *`future-spawned-sender`* be the
|
| 1937 |
+
alias template:
|
| 1938 |
+
|
| 1939 |
+
``` cpp
|
| 1940 |
+
template<sender Sender, class Env>
|
| 1941 |
+
using future-spawned-sender = // exposition only
|
| 1942 |
+
decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
|
| 1943 |
+
```
|
| 1944 |
+
|
| 1945 |
+
Let *`spawn-future-state`* be the exposition-only class template:
|
| 1946 |
+
|
| 1947 |
+
``` cpp
|
| 1948 |
+
namespace std::execution {
|
| 1949 |
+
template<class Alloc, scope_token Token, sender Sender, class Env>
|
| 1950 |
+
struct spawn-future-state // exposition only
|
| 1951 |
+
: spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
|
| 1952 |
+
using sigs-t = // exposition only
|
| 1953 |
+
completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
|
| 1954 |
+
using receiver-t = // exposition only
|
| 1955 |
+
spawn-future-receiver<sigs-t>;
|
| 1956 |
+
using op-t = // exposition only
|
| 1957 |
+
connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
|
| 1958 |
+
|
| 1959 |
+
spawn-future-state(Alloc alloc, Sender&& sndr, Token token, Env env) // exposition only
|
| 1960 |
+
: alloc(std::move(alloc)),
|
| 1961 |
+
op(connect(
|
| 1962 |
+
write_env(stop-when(std::forward<Sender>(sndr), ssource.get_token()), std::move(env)),
|
| 1963 |
+
receiver-t(this))),
|
| 1964 |
+
token(std::move(token)),
|
| 1965 |
+
associated(token.try_associate()) {
|
| 1966 |
+
if (associated)
|
| 1967 |
+
start(op);
|
| 1968 |
+
else
|
| 1969 |
+
set_stopped(receiver-t(this));
|
| 1970 |
+
}
|
| 1971 |
+
|
| 1972 |
+
void complete() noexcept override; // exposition only
|
| 1973 |
+
void consume(receiver auto& rcvr) noexcept; // exposition only
|
| 1974 |
+
void abandon() noexcept; // exposition only
|
| 1975 |
+
|
| 1976 |
+
private:
|
| 1977 |
+
using alloc-t = // exposition only
|
| 1978 |
+
allocator_traits<Alloc>::template rebind_alloc<spawn-future-state>;
|
| 1979 |
+
|
| 1980 |
+
alloc-t alloc; // exposition only
|
| 1981 |
+
ssource-t ssource; // exposition only
|
| 1982 |
+
op-t op; // exposition only
|
| 1983 |
+
Token token; // exposition only
|
| 1984 |
+
bool associated; // exposition only
|
| 1985 |
+
|
| 1986 |
+
void destroy() noexcept; // exposition only
|
| 1987 |
+
};
|
| 1988 |
+
}
|
| 1989 |
+
```
|
| 1990 |
+
|
| 1991 |
+
For purposes of determining the existence of a data race, *`complete`*,
|
| 1992 |
+
*`consume`*, and *`abandon`* behave as atomic operations
|
| 1993 |
+
[[intro.multithread]]. These operations on a single object of a type
|
| 1994 |
+
that is a specialization of *`spawn-future-state`* appear to occur in a
|
| 1995 |
+
single total order.
|
| 1996 |
+
|
| 1997 |
+
``` cpp
|
| 1998 |
+
void complete() noexcept;
|
| 1999 |
+
```
|
| 2000 |
+
|
| 2001 |
+
*Effects:*
|
| 2002 |
+
|
| 2003 |
+
- No effects if this invocation of *complete* happens before an
|
| 2004 |
+
invocation of *consume* or *abandon* on `*this`;
|
| 2005 |
+
- otherwise, if an invocation of *consume* on `*this` happens before
|
| 2006 |
+
this invocation of *complete* then there is a receiver, `rcvr`,
|
| 2007 |
+
registered and that receiver is completed as if by
|
| 2008 |
+
*`consume`*`(rcvr)`;
|
| 2009 |
+
- otherwise, *destroy* is invoked.
|
| 2010 |
+
|
| 2011 |
+
``` cpp
|
| 2012 |
+
void consume(receiver auto& rcvr) noexcept;
|
| 2013 |
+
```
|
| 2014 |
+
|
| 2015 |
+
*Effects:*
|
| 2016 |
+
|
| 2017 |
+
- If this invocation of *consume* happens before an invocation of
|
| 2018 |
+
*complete* on `*this` then `rcvr` is registered to be completed when
|
| 2019 |
+
*complete* is subsequently invoked on `*this`;
|
| 2020 |
+
- otherwise, `rcvr` is completed as if by:
|
| 2021 |
+
``` cpp
|
| 2022 |
+
std::move(this->result).visit(
|
| 2023 |
+
[&rcvr](auto&& tuple) noexcept {
|
| 2024 |
+
if constexpr (!same_as<remove_reference_t<decltype(tuple)>, monostate>) {
|
| 2025 |
+
apply([&rcvr](auto cpo, auto&&... vals) {
|
| 2026 |
+
cpo(std::move(rcvr), std::move(vals)...);
|
| 2027 |
+
}, std::move(tuple));
|
| 2028 |
+
}
|
| 2029 |
+
});
|
| 2030 |
+
```
|
| 2031 |
+
|
| 2032 |
+
``` cpp
|
| 2033 |
+
void abandon() noexcept;
|
| 2034 |
+
```
|
| 2035 |
+
|
| 2036 |
+
*Effects:*
|
| 2037 |
+
|
| 2038 |
+
- If this invocation of *abandon* happens before an invocation of
|
| 2039 |
+
*complete* on `*this` then equivalent to:
|
| 2040 |
+
``` cpp
|
| 2041 |
+
ssource.request_stop();
|
| 2042 |
+
```
|
| 2043 |
+
- otherwise, *destroy* is invoked.
|
| 2044 |
+
|
| 2045 |
+
``` cpp
|
| 2046 |
+
void destroy() noexcept;
|
| 2047 |
+
```
|
| 2048 |
+
|
| 2049 |
+
*Effects:* Equivalent to:
|
| 2050 |
+
|
| 2051 |
+
``` cpp
|
| 2052 |
+
auto token = std::move(this->token);
|
| 2053 |
+
bool associated = this->associated;
|
| 2054 |
+
|
| 2055 |
+
{
|
| 2056 |
+
auto alloc = std::move(this->alloc);
|
| 2057 |
+
|
| 2058 |
+
allocator_traits<alloc-t>::destroy(alloc, this);
|
| 2059 |
+
allocator_traits<alloc-t>::deallocate(alloc, this, 1);
|
| 2060 |
+
}
|
| 2061 |
+
|
| 2062 |
+
if (associated)
|
| 2063 |
+
token.disassociate();
|
| 2064 |
+
```
|
| 2065 |
+
|
| 2066 |
+
The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
|
| 2067 |
+
specialized for `spawn_future_t` as follows:
|
| 2068 |
+
|
| 2069 |
+
``` cpp
|
| 2070 |
+
namespace std::execution {
|
| 2071 |
+
template<>
|
| 2072 |
+
struct impls-for<spawn_future_t> : default-impls {
|
| 2073 |
+
static constexpr auto start = see below; // exposition only
|
| 2074 |
+
};
|
| 2075 |
+
}
|
| 2076 |
+
```
|
| 2077 |
+
|
| 2078 |
+
The member `impls-for<spawn_future_t>::start` is initialized with a
|
| 2079 |
+
callable object equivalent to the following lambda:
|
| 2080 |
+
|
| 2081 |
+
``` cpp
|
| 2082 |
+
[](auto& state, auto& rcvr) noexcept -> void {
|
| 2083 |
+
state->consume(rcvr);
|
| 2084 |
+
}
|
| 2085 |
+
```
|
| 2086 |
+
|
| 2087 |
+
For the expression `spawn_future(sndr, token, env)` let `new_sender` be
|
| 2088 |
+
the expression `token.wrap(sndr)` and let `alloc` and `senv` be defined
|
| 2089 |
+
as follows:
|
| 2090 |
+
|
| 2091 |
+
- if the expression `get_allocator(env)` is well-formed, then `alloc` is
|
| 2092 |
+
the result of `get_allocator(env)` and `senv` is the expression `env`;
|
| 2093 |
+
- otherwise, if the expression `get_allocator(get_env(new_sender))` is
|
| 2094 |
+
well-formed, then `alloc` is the result of
|
| 2095 |
+
`get_allocator(get_env(new_sender))` and `senv` is the expression
|
| 2096 |
+
`JOIN-ENV(prop(get_allocator, alloc), env)`;
|
| 2097 |
+
- otherwise, `alloc` is `allocator<void>()` and `senv` is the expression
|
| 2098 |
+
`env`.
|
| 2099 |
+
|
| 2100 |
+
The expression `spawn_future(sndr, token, env)` has the following
|
| 2101 |
+
effects:
|
| 2102 |
+
|
| 2103 |
+
- Uses `alloc` to allocate and construct an object `s` of a type that is
|
| 2104 |
+
a specialization of *`spawn-future-{}state`* from `alloc`,
|
| 2105 |
+
`token.wrap(sndr)`, `token`, and `senv`. If an exception is thrown
|
| 2106 |
+
then any constructed objects are destroyed and any allocated memory is
|
| 2107 |
+
deallocated.
|
| 2108 |
+
- Constructs an object `u` of a type that is a specialization of
|
| 2109 |
+
`unique_ptr` such that:
|
| 2110 |
+
- `u.get()` is equal to the address of `s`, and
|
| 2111 |
+
- `u.get_deleter()(u.release())` is equivalent to
|
| 2112 |
+
`u.release()->abandon()`.
|
| 2113 |
+
- Returns `make-sender(spawn_future, std::move(u))`.
|
| 2114 |
+
|
| 2115 |
+
The expression `spawn_future(sndr, token)` is expression-equivalent to
|
| 2116 |
+
`spawn_future(sndr, token, execution::env<>())`.
|
| 2117 |
+
|