{"id":10404,"date":"2024-12-09T10:25:34","date_gmt":"2024-12-09T10:25:34","guid":{"rendered":"https:\/\/www.modernescpp.com\/?p=10404"},"modified":"2025-07-04T15:54:27","modified_gmt":"2025-07-04T15:54:27","slug":"stdexecution-composition-of-senders","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/stdexecution-composition-of-senders\/","title":{"rendered":"std::execution: Composition of Senders"},"content":{"rendered":"\n<p>Most sender adaptors are composable using the pipe operator.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"711\" height=\"491\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/11\/Timeexecution-2.png\" alt=\"\" class=\"wp-image-10406\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/11\/Timeexecution-2.png 711w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/11\/Timeexecution-2-300x207.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/11\/Timeexecution-2-705x487.png 705w\" sizes=\"auto, (max-width: 711px) 100vw, 711px\" \/><\/figure>\n\n\n\n<p>Let me start with a simple example of composition with the pipe operator.<\/p>\n\n\n\n<p>Instead of the nested function calls<\/p>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; Hallo overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\">call1(call2(input))\n<\/pre><\/div>\n\n\n\n<p>you can write<\/p>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\">call1 <span style=\"color: #555555\">|<\/span> call2(input)\n<\/pre><\/div>\n\n\n\n<p>or even:<\/p>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"> input <span style=\"color: #555555\">|<\/span> call1 <span style=\"color: #555555\">|<\/span> call2\n<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Function composition<\/h2>\n\n\n\n<p>Okay, this example was straightforward. Let&#8217;s do something more complicated. Proposal <a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2024\/p2300r10.html\">P2300R10<\/a> compares nested function invocation with function invocation using a temporary and composition using the pipe operator. <\/p>\n\n\n\n<p>All three function compositions calculate 610 = (123*5)-5 using a <code>thread_pool <\/code>and <code>cuda<\/code>.  Consider, in particular, the lambda  <code>[]{ return 123; })<\/code>.  This lambda is not evaluated. Why?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Nested Function Invocation<\/h3>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #006699; font-weight: bold\">auto<\/span> snd <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>then(\n             execution<span style=\"color: #555555\">::<\/span>continues_on(\n               execution<span style=\"color: #555555\">::<\/span>then(\n                 execution<span style=\"color: #555555\">::<\/span>continues_on(\n                   execution<span style=\"color: #555555\">::<\/span>then(\n                     execution<span style=\"color: #555555\">::<\/span>schedule(thread_pool.scheduler())\n                     []{ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span>; }),\n                   cuda<span style=\"color: #555555\">::<\/span>new_stream_scheduler()),\n                 [](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span> <span style=\"color: #555555\">*<\/span> <span style=\"color: #FF6600\">5<\/span>; }),\n               thread_pool.scheduler()),\n             [](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> i <span style=\"color: #555555\">-<\/span> <span style=\"color: #FF6600\">5<\/span>; });\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> [result] <span style=\"color: #555555\">=<\/span> this_thread<span style=\"color: #555555\">::<\/span>sync_wait(snd).value();\n<span style=\"color: #0099FF; font-style: italic\">\/\/ result == 610<\/span>\n<\/pre><\/div>\n\n\n\n<p>It isn&#8217;t easy to understand this nesting of function calls, to see which function bodies belong together, or to understand why the lambda is not executed. Nor is it fun to debug this composition or change it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"> Function Invocation with Temporaries<\/h3>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #006699; font-weight: bold\">auto<\/span> snd0 <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>schedule(thread_pool.scheduler());\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> snd1 <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>then(snd0, []{ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span>; });\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> snd2 <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>continues_on(snd1, cuda<span style=\"color: #555555\">::<\/span>new_stream_scheduler());\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> snd3 <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>then(snd2, [](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span> <span style=\"color: #555555\">*<\/span> <span style=\"color: #FF6600\">5<\/span>; })\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> snd4 <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>continues_on(snd3, thread_pool.scheduler())\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> snd5 <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>then(snd4, [](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> i <span style=\"color: #555555\">-<\/span> <span style=\"color: #FF6600\">5<\/span>; });\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> [result] <span style=\"color: #555555\">=<\/span> <span style=\"color: #555555\">*<\/span>this_thread<span style=\"color: #555555\">::<\/span>sync_wait(snd4);\n<span style=\"color: #0099FF; font-style: italic\">\/\/ result == 610<\/span>\n<\/pre><\/div>\n\n\n\n<p>Using temporaries may help a lot in understanding the composition&#8217;s structure. Now, it is easy to see the sequence of function calls. You may also see why the lambda  <code>[]{ return 123; }<\/code>. There is no sender consumer, such as <code>this_thread::sync_wait(snd4)<\/code>. The sender adaptors, such as <code>then<\/code> and <code>continue_on<\/code>, are lazy. They only produce a value if asked for it.<\/p>\n\n\n\n<p>I like this solution from a readability point of view, but it has a severe downside: many temporaries are created.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Function Composition  with the Pipe Operator <\/h3>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #006699; font-weight: bold\">auto<\/span> snd <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>schedule(thread_pool.scheduler())\n         <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>then([]{ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span>; })\n         <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>continues_on(cuda<span style=\"color: #555555\">::<\/span>new_stream_scheduler())\n         <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>then([](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span> <span style=\"color: #555555\">*<\/span> <span style=\"color: #FF6600\">5<\/span>; })\n         <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">:: Double quote <\/span>continues_on(thread_pool.scheduler())\n         <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>then([](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> i <span style=\"color: #555555\">-<\/span> <span style=\"color: #FF6600\">5<\/span>; });\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> [result] <span style=\"color: #555555\">=<\/span> this_thread<span style=\"color: #555555\">::<\/span>sync_wait(snd).value();\n<span style=\"color: #0099FF; font-style: italic\">\/\/ result == 610<\/span>\n<\/pre><\/div>\n\n\n\n<p>Function composition with the pipe operator overcomes both issues. First, it is readable, and second, it doesn&#8217;t need unnecessary temporaries.<\/p>\n\n\n\n<p>Not all sender adaptors are pipeable. Specifically, the following sender adaptors are not pipeable.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>execution::when_all<\/code> and <code>execution::when_all_with_variant<\/code>: Both sender adaptors take an arbitrary number of senders. It would, therefore, be unclear which sender should be adapted.<\/li>\n\n\n\n<li><code>execution::starts_on<\/code>: This sender adaptor changes the execution resource on which the sender runs. It does not adapt the sender.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Layout is Important<\/h3>\n\n\n\n<p>It is essential to layout it in a readable way for function composition. So, don&#8217;t make a smart one-liner out of it:<\/p>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #006699; font-weight: bold\">auto<\/span> snd <span style=\"color: #555555\">=<\/span> execution<span style=\"color: #555555\">::<\/span>schedule(thread_pool.scheduler()) <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>then([]{ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span>; }) <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>continues_on(cuda<span style=\"color: #555555\">::<\/span>new_stream_scheduler()) <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>then([](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> <span style=\"color: #FF6600\">123<\/span> <span style=\"color: #555555\">*<\/span> <span style=\"color: #FF6600\">5<\/span>; }) <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span> Double quote continues_on(thread_pool.scheduler()) <span style=\"color: #555555\">|<\/span> execution<span style=\"color: #555555\">::<\/span>then([](<span style=\"color: #007788; font-weight: bold\">int<\/span> i){ <span style=\"color: #006699; font-weight: bold\">return<\/span> i <span style=\"color: #555555\">-<\/span> <span style=\"color: #FF6600\">5<\/span>; });\n<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s Next?<\/h2>\n\n\n\n<p><code>std::execution<\/code> offers a few sender factories, particularly many senders, to model your concurrent workflow. I will present them in my next post.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most sender adaptors are composable using the pipe operator. Let me start with a simple example of composition with the pipe operator. Instead of the nested function calls call1(call2(input)) you can write call1 | call2(input) or even: input | call1 | call2 Function composition Okay, this example was straightforward. Let&#8217;s do something more complicated. Proposal [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":10406,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[559],"tags":[561],"class_list":["post-10404","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c26-blog","tag-execution"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/10404","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=10404"}],"version-history":[{"count":19,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/10404\/revisions"}],"predecessor-version":[{"id":10489,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/10404\/revisions\/10489"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/10406"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=10404"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=10404"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=10404"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}