{"id":104995,"date":"2021-03-24T07:00:00","date_gmt":"2021-03-24T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104995"},"modified":"2021-03-25T07:46:50","modified_gmt":"2021-03-25T14:46:50","slug":"20210324-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210324-00\/?p=104995","title":{"rendered":"Creating a task completion source for a C++ coroutine: Producing a result with references"},"content":{"rendered":"<p>Last time, we <a title=\"Creating a task completion source for a C++ coroutine: Producing a result\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210323-00\/?p=104987\"> created a <code>result_holder<\/code> that can be awaited until it is assigned a result<\/a>, and I noted that the code was broken.<\/p>\n<p>One of the reasons that it&#8217;s broken is that it doesn&#8217;t handle references properly.<\/p>\n<pre>result_holder&lt;int&amp;&gt; counter; \/\/ errors!\r\n<\/pre>\n<p>If <code>T<\/code> is a reference, we run into trouble trying to put it into a union:<\/p>\n<pre>    union optional\r\n    {\r\n        optional() {}\r\n        ~optional() {}\r\n\r\n        T value; \/\/ oops\r\n    } result;\r\n<\/pre>\n<p>References may not be members of a union. There&#8217;s also the problem that <a title=\"What's up with error C4838: a WinRT type cannot be a member of a union, and how can I work around it?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200914-00\/?p=104218\"> certain C++\/CX types also cannot be members of a union<\/a>. So what do you do if <code>T<\/code> is one of those &#8220;forbidden in a union&#8221; types?<\/p>\n<p>A common workaround is to wrap the illegal type inside a legal one: Create a wrapper structure that has a single member, namely the type that can&#8217;t go into a union. Then put the structure in the union.<\/p>\n<pre>    <span style=\"color: blue;\">struct wrapper\r\n    {\r\n        T value;\r\n    };<\/span>\r\n\r\n    union optional\r\n    {\r\n        optional() {}\r\n        ~optional() {}\r\n\r\n        <span style=\"color: blue;\">wrapper wrap;<\/span>\r\n    } result;\r\n<\/pre>\n<p>And now every reference to the wrapped value must go through the wrapper.<\/p>\n<pre>    ~result_holder_state()\r\n    {\r\n        if (ready.load(std::memory_order_relaxed)) {\r\n            result.<span style=\"color: blue;\">wrap.~wrapper<\/span>();\r\n        }\r\n    }\r\n\r\n    ...\r\n\r\n    void set_result(node_list&amp; list, T v)\r\n    {\r\n        if (!ready.load(std::memory_order_relaxed)) {\r\n            new (std::addressof(result.<span style=\"color: blue;\">wrap<\/span>))\r\n                <span style=\"color: blue;\">wrapper<\/span>{ std::<span style=\"color: blue;\">forward<\/span>&lt;T&gt;(v) };\r\n            ready.store(true, std::memory_order_release);\r\n            this-&gt;resume_all(list);\r\n        }\r\n    }\r\n    ...\r\n};\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct result_holder\r\n    : async_helpers::awaitable_sync_object&lt;\r\n        result_holder_state&lt;T&gt;&gt;\r\n{\r\n    ...\r\n\r\n    void set_result(T result) const noexcept\r\n    {\r\n        this-&gt;action_impl(&amp;state::set_result,\r\n            std::<span style=\"color: blue;\">forward&lt;T&gt;<\/span>(result));\r\n    }\r\n};\r\n<\/pre>\n<p>Note also that we use <code>std::forward<\/code> instead of <code>std::move<\/code> to construct the wrapper. Forwarding a reference preserves reference-ness, and forwarding a non-reference moves it. (I always have to go back and work out the cases by hand to convince myself that this is true.)<\/p>\n<p>Okay, so that&#8217;s how we can get the reference into the result holder. But how do we get it back out?<\/p>\n<pre>    T get_result()\r\n    {\r\n        return result.<span style=\"color: blue;\">wrap.value<\/span>;\r\n    }\r\n};\r\n<\/pre>\n<p>Now that <code>get_result<\/code> returns a reference, we have to make sure that the reference doesn&#8217;t get decayed to a value as it propagates out of <code>get_result<\/code> back to the awaiter and ultimately to the caller of <code>co_await<\/code>:<\/p>\n<pre>    template&lt;typename State&gt;\r\n    class awaitable_state\r\n    {\r\n        ...\r\n\r\n        <span style=\"color: blue;\">decltype(auto)<\/span> await_resume(\r\n            impl::node&lt;extra_await_data&gt;&amp; node) noexcept\r\n        {\r\n            node.handle = nullptr;\r\n            return parent().get_result();\r\n        }\r\n        ...\r\n    };\r\n\r\n    template&lt;typename State&gt;\r\n    class awaitable_sync_object\r\n    {\r\n        ...\r\n\r\n        struct awaiter\r\n        {\r\n            ...\r\n\r\n            <span style=\"color: blue;\">decltype(auto)<\/span> await_resume()\r\n            { return s.await_resume(node); }\r\n\r\n            ...\r\n        }\r\n    };\r\n<\/pre>\n<p>The <code>decltype(auto)<\/code> specifier allows you to forward a return type perfectly, without incurring the decay that normally occurs if you had used <code>auto<\/code>.<\/p>\n<p>Okay, so now we can put a reference in our <code>result_<wbr \/>holder<\/code>. There&#8217;s another thing we can&#8217;t put in our <code>result_<wbr \/>holder<\/code>: We&#8217;ll look at it <a title=\"Creating a task completion source for a C++ coroutine: Producing nothing\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210325-00\/?p=105002\"> next time<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Carefully preserved to prevent decay.<\/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-104995","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Carefully preserved to prevent decay.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104995","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=104995"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104995\/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=104995"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104995"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104995"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}