{"id":105002,"date":"2021-03-25T07:00:00","date_gmt":"2021-03-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105002"},"modified":"2021-03-26T08:05:00","modified_gmt":"2021-03-26T15:05:00","slug":"20210325-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210325-00\/?p=105002","title":{"rendered":"Creating a task completion source for a C++ coroutine: Producing nothing"},"content":{"rendered":"<p>Last time, we <a title=\"Creating a task completion source for a C++ coroutine: Producing a result with references\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210324-00\/?p=104995\"> created a <code>result_holder<\/code> that can hold a reference<\/a>, and we solved it by using a wrapper. But there&#8217;s another type that we can&#8217;t put in a <code>result_holder<\/code>, not even with the help of a wrapper. That type is <code>void<\/code>.<\/p>\n<pre>    struct wrapper\r\n    {\r\n        void value; \/\/ not allowed\r\n    };\r\n<\/pre>\n<p>This doesn&#8217;t work because you cannot have an object of type <code>void<\/code>. You might nevertheless want to have a <code>result_<wbr \/>holder<\/code> that &#8220;holds&#8221; a <code>void<\/code>, because that is basically an event: The result is the fact that something happened.<\/p>\n<p>There are a few ways to work around this problem. One is to redirect <code>void<\/code> to some other type like <code>bool<\/code>, and just ignore the <code>bool<\/code> value. This is the approach that is often used in C# code: A task completion event of <code>void<\/code> is just a task completion event of <code>bool<\/code> where the <code>bool<\/code> is ignored.<\/p>\n<p>But in C++, we have partial specialization, so we can get all fancy-like.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct wrapper\r\n    {\r\n        T value;\r\n        T get_value() { return static_cast&lt;T&gt;(value); }\r\n    };\r\n\r\n    template&lt;&gt;\r\n    struct wrapper&lt;void&gt;\r\n    {\r\n        void get_value() { }\r\n    };\r\n<\/pre>\n<p>In the case of <code>void<\/code>, we use an empty class. This avoids the trouble of having to initialize a dummy <code>bool<\/code> member, and it opens the door to empty base class optimization, although we won&#8217;t take advantage of EBO here. We then add <code>get_<wbr \/>value<\/code> methods to extract the value in a uniform way:<\/p>\n<ul>\n<li>For <code>void<\/code> it returns nothing.<\/li>\n<li>For references, it returns the reference.<\/li>\n<li>For values, it returns a copy of the object.<\/li>\n<\/ul>\n<p>(Recall that this is intended for an object that can be awaited multiple times, so the underlying object needs to be copyable so that each client that does a <code>co_await<\/code> gets its own copy.)<\/p>\n<p>Now we can revise our code that sets the result so it knows the special way of setting nothing.<\/p>\n<pre>    <span style=\"color: blue;\">template&lt;typename Dummy = void&gt;\r\n    std::enable_if_t&lt;std::is_same_v&lt;T, void&gt;, Dummy&gt;\r\n        set_result(node_list&amp; list)\r\n    {\r\n        if (!ready.load(std::memory_order_relaxed)) {\r\n            new (std::addressof(result.wrap)) wrapper{ };\r\n            ready.store(true, std::memory_order_release);\r\n            this-&gt;resume_all(list);\r\n        }\r\n    }\r\n    template&lt;typename Dummy = void&gt;\r\n    std::enable_if_t&lt;!std::is_same_v&lt;T, void&gt;, Dummy&gt;<\/span>\r\n        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.wrap))\r\n                wrapper{ std::forward&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    <span style=\"color: blue;\">template&lt;typename Dummy = void&gt;\r\n    std::enable_if_t&lt;std::is_same_v&lt;T, void&gt;, Dummy&gt;\r\n        set_result() const noexcept\r\n    {\r\n        this-&gt;action_impl(&amp;state::set_result);\r\n    }\r\n    template&lt;typename Dummy = void&gt;\r\n    std::enable_if_t&lt;!std::is_same_v&lt;T, void&gt;, Dummy&gt;<\/span>\r\n        set_result(T result) const noexcept\r\n    {\r\n        this-&gt;action_impl(&amp;state::set_result,\r\n            std::forward&lt;T&gt;(result));\r\n    }\r\n};\r\n<\/pre>\n<p>Getting the value back out is simpler thanks to our <code>get_value<\/code> helper.<\/p>\n<pre>    T get_result()\r\n    {\r\n        return result.wrap.<span style=\"color: blue;\">get_value()<\/span>;\r\n    }\r\n};\r\n<\/pre>\n<p>Okay, so now we know how to deal with a result of nothing. But how do you report the failure to produce a result at all? We&#8217;ll look at that <a title=\"Creating a task completion source for a C++ coroutine: Failing to produce a result\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210326-00\/?p=105009\"> next time<\/a>.<\/p>\n<p><b>Bonus chatter<\/b>: While we&#8217;re at it, we may as well put <code>[[no_unique_address]]<\/code> on the <code>T value<\/code>, in case <code>T<\/code> is an empty class.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct wrapper\r\n    {\r\n        <span style=\"color: blue;\">[[no_unique_address]]<\/span> T value;\r\n        T get_value() { return static_cast&lt;T&gt;(value); }\r\n    };\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Nothing is produced, but you still want to know that the production completed.<\/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-105002","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Nothing is produced, but you still want to know that the production completed.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105002","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=105002"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105002\/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=105002"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105002"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105002"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}