{"id":104172,"date":"2020-09-04T07:00:00","date_gmt":"2020-09-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104172"},"modified":"2020-09-09T06:09:00","modified_gmt":"2020-09-09T13:09:00","slug":"20200904-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200904-00\/?p=104172","title":{"rendered":"Rough edges in the when_all coroutine, part 2: Overloaded comma operator"},"content":{"rendered":"<p>Last time, we looked at <a title=\"Rough edges in the when_all coroutine, part 1: Empty parameter list\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200903-00\/?p=104160\"> a problematic edge case in our <code>when_all<\/code> coroutine: The empty parameter list<\/a>.<\/p>\n<p>There&#8217;s another edge case that can cause trouble, and that&#8217;s the case where the comma operator itself has been overloaded.\u00b9<\/p>\n<pre>struct S\r\n{\r\n  void Detonate();\r\n  S operator,(S right) { Detonate(); return right; }\r\n};\r\n\r\nstruct async_s : std::experimental::suspend_never\r\n{\r\n  S await_resume() { return {}; }\r\n};\r\n\r\nwhen_all(async_s(), async_s()); \/\/ kaboom\r\n<\/pre>\n<p>We start by defining a type <code>S<\/code> that has a comma operator. When you comma two <code>S<\/code> objects together, the first one explodes.<\/p>\n<p>Next, we define an awaitable object <code>async_s<\/code>: When you <code>co_await<\/code>, an <code>S<\/code> comes out.<\/p>\n<p>And then we pass two of these objects to <code>when_all<\/code>. The expectation is that the <code>when_all<\/code> awaits the two objects, throws away the results, and returns.<\/p>\n<p>Instead, what happens is that the <code>S<\/code> object explodes.<\/p>\n<p>What went wrong is that our fold expression expanded to<\/p>\n<pre>IAsyncAction when_all(async_s v1, async_s v2)\r\n{\r\n  (co_await v1, co_await v2);\r\n  co_return;\r\n}\r\n<\/pre>\n<p>The intent of the comma in the fold expression was to throw away the left-hand operand, leaving the last surviving operand to be thrown away by the statement-expression. But thanks to the custom comma operator, it actually causes the left-hand operand to explode.<\/p>\n<p>To suppress any custom comma operators, we can cast the result of the <code>co_await<\/code> to <code>void<\/code>. Since you cannot overload the comma operator for <code>void<\/code>, this forces the use of the default comma operator, so we just comma-combine a bunch of <code>void<\/code>s, which is harmless.<\/p>\n<pre>template &lt;typename... T&gt;\r\nWindows::Foundation::IAsyncAction when_all(T... async)\r\n{\r\n    (void(co_await async), ...);\r\n    co_return;\r\n}\r\n<\/pre>\n<p><a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/pull\/645\">Here is the PR that fixes the empty parameter list and comma operator issues<\/a>, and a <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/pull\/647\"> follow-up<\/a>.<\/p>\n<p><b>Bonus chatter<\/b>: We could also have used a right fold:<\/p>\n<pre>    (co_await async, ..., void());\r\n<\/pre>\n<p>which expands to<\/p>\n<pre>    (co_await v1, (co_await v2, void()));\r\n<\/pre>\n<p>But I think casting away the value is simpler.<\/p>\n<p>\u00b9 Shame on you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Shame on you.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-104172","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Shame on you.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104172","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=104172"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104172\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=104172"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104172"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104172"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}