{"id":105176,"date":"2021-05-04T07:00:00","date_gmt":"2021-05-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105176"},"modified":"2021-05-03T06:19:41","modified_gmt":"2021-05-03T13:19:41","slug":"20210504-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210504-00\/?p=105176","title":{"rendered":"C++ coroutines: Promise constructors"},"content":{"rendered":"<p>So far, all of our coroutine promises have had only a default constructor. But the standard actually gives the promise access to the coroutine parameters, if it wants them.\u00b9<\/p>\n<p>If somebody declares a coroutine that uses your promise, say,<\/p>\n<pre>simple_task&lt;void&gt; Awesome(int x, int y)\r\n{\r\n    ...\r\n}\r\n<\/pre>\n<p>the compiler first looks for a promise constructor that accepts those parameters, prefixed if applicable by the hidden <code>*this<\/code> parameter. In this example, it tries to construct a <code>simple_promise(x, y)<\/code>. Standard overload rules apply, so the actual constructor could take two integer lvalues, or one integer lvalue and one integer by value, or maybe it takes two <code>long<\/code>s, since integers are implicitly convertible to <code>long<\/code>. This gives your coroutine an opportunity to snoop on the parameters. For example, you might have a promise that detects that one of the parameters is a <code>Logger<\/code>, in which case it uses that logging object for its own internal logging.<\/p>\n<p>If no suitable constructor can be found, then the compiler falls back to using the default constructor for the promise.<\/p>\n<p>You might say, &#8220;Well, that&#8217;s interesting, but it has no effect on me because my only constructor is the default constructor, so that&#8217;s the only one the compiler will ever use.&#8221;<\/p>\n<p>You&#8217;d be wrong.<\/p>\n<p>Because the compiler will autogenerate a copy constructor.<\/p>\n<p>Somebody could create a coroutine like this:<\/p>\n<pre>simple_task&lt;void&gt; Weirdo(simple_promise&lt;void&gt; wha)\r\n{\r\n    ...\r\n}\r\n<\/pre>\n<p>If they do that, then the compiler will look for a promise constructor that takes a <code>simple_promise&lt;void&gt;<\/code> parameter, and it will find one: The copy constructor. The promise for the coroutine will therefore be <i>copy-constructed<\/i> from the <code>wha<\/code> parameter, which is probably not what you were expecting.<\/p>\n<p>On the other hand, the fact that they are passing your private promise type as a parameter suggests that they are intentionally messing with the internals and therefore deserve what they get.<\/p>\n<p>However, an unwitting developer might stumble into this case if they create a generic type similar to <code>std::any<\/code>:<\/p>\n<pre>struct Object\r\n{\r\n    template&lt;typename T&gt;\r\n    operator T() { return std::any_cast&lt;T&gt;(o); }\r\n\r\n    template&lt;typename T&gt;\r\n    Object&amp; operator=(T&amp;&amp; other)\r\n    { o = std::forward&lt;T&gt;(other); return *this; }\r\n\r\nprivate:\r\n    std::any o;\r\n};\r\n<\/pre>\n<p>This is a generic type that can hold any value, and you can get the same value out by converting to the thing you hope is inside.<\/p>\n<p>Which means that it can try to convert to <code>simple_promise<\/code>.<\/p>\n<pre>simple_task&lt;void&gt; Print(Object o)\r\n{\r\n    ...\r\n}\r\n<\/pre>\n<p>The compiler will see that an <code>Object<\/code> can be passed to the <code>simple_<wbr \/>promise<\/code> copy constructor, which will try to convert the <code>Object<\/code> to a <code>simple_<wbr \/>promise<\/code> in order to copy it. The conversion will (probably) fail with a <code>std::<wbr \/>bad_<wbr \/>any_<wbr \/>cast<\/code>, and your program crashes for a totally mysterious reason. You&#8217;ll be looking at the crash dumps wondering, &#8220;Why is this code trying to convert my <code>Object<\/code> to a <code>simple_<wbr \/>promise<\/code>?&#8221;<\/p>\n<p>Let&#8217;s fix that by explicitly denying copying.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct simple_promise_base\r\n    {\r\n        ...\r\n\r\n        <span style=\"color: blue;\">simple_promise_base() = default;\r\n        simple_promise_base(simple_promise_base const&amp;) = delete;\r\n        void operator=(simple_promise_base const&amp;) = delete;<\/span>\r\n\r\n        ...\r\n    };\r\n<\/pre>\n<p>I&#8217;m going to declare this the nominal end of what turned into a 47-part series on coroutines,\u00b2 because I&#8217;m pretty sure you&#8217;re all sick of coroutines by now. There are still some other topics related to coroutines that aren&#8217;t connected to this series, so you&#8217;re not out of the woods yet. And there&#8217;s generators, which is deserving of its own series, but I&#8217;ll wait until the outrage dies down.<\/p>\n<p>\u00b9 Be aware that this is a dark corner of the language specification that not all implementations agree on. The specification says that the parameters are passed as lvalues, but gcc passes them as their original reference class, and MSVC doesn&#8217;t pass them at all until you <a title=\"C++ Coroutines in Visual Studio 2019 Version 16.8\" href=\"https:\/\/devblogs.microsoft.com\/cppblog\/c-coroutines-in-visual-studio-2019-version-16-8\/\"> upgrade to version 16.8 or higher<\/a>, set <code>\/std:c++latest<\/code>, and omit the legacy <code>\/await<\/code> flag.<\/p>\n<p>I get the impression that the gcc behavior is a bug, rather than a feature, because setting <code>-pedantic<\/code> does not cause gcc to switch to the standard-conforming behavior.<\/p>\n<p>\u00b2 Or 48 parts if you count <a title=\"What does error E_ILLEGAL_DELEGATE_ASSIGNMENT mean?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210226-00\/?p=104911\"> the prologue article about <code>E_<wbr \/>ILLEGAL_<wbr \/>DELEGATE_<wbr \/>ASSIGNMENT<\/code><\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Snooping on the coroutine parameters, with a gotcha.<\/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-105176","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Snooping on the coroutine parameters, with a gotcha.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105176","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=105176"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105176\/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=105176"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105176"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105176"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}