{"id":104227,"date":"2020-09-16T07:00:00","date_gmt":"2020-09-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104227"},"modified":"2021-10-16T10:18:11","modified_gmt":"2021-10-16T17:18:11","slug":"20200916-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200916-00\/?p=104227","title":{"rendered":"Why did I lose the ability to co_await a std::future and concurrency::task?"},"content":{"rendered":"<p>After upgrading to <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/releases\/tag\/2.0.200729.8\"> version 2.0.200729.8 of C++\/WinRT<\/a>, some customers reported that they lost the ability to <code>co_await<\/code> a <code>std::future<\/code> or a <code>Concurrency::task<\/code>. What happened?<\/p>\n<p>The relevant change is <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/pull\/702\"> PR 702<\/a> which removed &#8220;vestigial support for free awaiters.&#8221; And that&#8217;s the part that&#8217;s relevant here.<\/p>\n<p>&#8220;Free awaiters&#8221; sounds like a rock album from the 60&#8217;s, but it&#8217;s an informal term for a feature that was part of the early explorations of the coroutine TS, before there was even a coroutine TS.<\/p>\n<p>Recall that <a title=\"C++ coroutines: Defining the co_await operator\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191218-00\/?p=103221\"> the algorithm for finding an awaiter for an object <code>x<\/code> has three steps<\/a>:<\/p>\n<ol>\n<li>(We&#8217;re not ready to talk about step 1 yet.)\u00b9<\/li>\n<li>If there is a defined <code>operator co_await<\/code> for <code>x<\/code>, then invoke it to obtain the awaiter.<\/li>\n<li>Otherwise, <code>x<\/code> is its own awaiter.<\/li>\n<\/ol>\n<p>In the early coroutine explorations, the concept of an awaiter hadn&#8217;t yet been invented. Instead, awaiting was described in the form of function calls.<\/p>\n<ul>\n<li>Call the function <code>await_ready(x)<\/code> to decide whether to suspend the coroutine.<\/li>\n<li>Call the function <code>await_suspend(x, handle)<\/code> to suspend the coroutine.<\/li>\n<li>Call the function <code>await_resume(x)<\/code> after the coroutine resumes.<\/li>\n<\/ul>\n<p>Instead of the functions existing as methods on an awaiter object, they were looked up as free functions via argument-dependent lookup.<\/p>\n<p>It was during this point in the exploration of how coroutines could be implemented that the header files for <code>std::future<\/code> and <code>Concurrency::task<\/code> were frozen for inclusion in Visual Studio 2017. Those classes use the prototype free function pattern for creating awaitable objects.<\/p>\n<p>The C++\/WinRT library developed at the same time the coroutine TS was coming together, and it supported the free function pattern as well as the awaiter-based mechanism that made it into the standard. The Microsoft Visual C++ compiler also supports the prototype free function pattern in addition to the standards-based awaiter pattern.<\/p>\n<p>As part of C++\/WinRT&#8217;s continuing move toward standard-conformance,\u00b2 PR 702 removed support for the nonstandard free function pattern for awaitable objects.\u00b3 This means that if you are still using Visual Studio 2017, you lost the ability to <code>co_await<\/code> <code>std::future<\/code> and <code>Concurrency::task<\/code> from a C++\/WinRT coroutine.<\/p>\n<p>But all is not lost. If you aren&#8217;t yet ready to upgrade to Visual Studio 2019 (version 16.8 or higher), you can still recover support for <code>co_await<\/code>&#8216;ing <code>std::future<\/code> and <code>Concurrency::task<\/code> with the help of a modernizer. First, here&#8217;s the idea kernel:<\/p>\n<pre>namespace modernizer\r\n{\r\n    template&lt;typename Awaitable&gt;\r\n    struct cpp20_await_adapter\r\n    {\r\n        Awaitable&amp; awaitable;\r\n\r\n        bool await_ready()\r\n        { return await_ready(awaitable); }\r\n\r\n        template&lt;typename Handle&gt;\r\n        auto await_suspend(Handle handle)\r\n        { return await_suspend(awaitable, handle); }\r\n\r\n        auto await_resume()\r\n        { return await_resume(awaitable); }\r\n    };\r\n}\r\n<\/pre>\n<p>The modernizer takes the awaitable object and forward all of the await methods to the corresponding free function, as determined by argument-dependent lookup.<\/p>\n<p>Unfortunately, this doesn&#8217;t work because of the name conflict: When the <code>await_ready<\/code> method wants to use argument-dependent lookup to find the free <code>await_ready<\/code> function, the thing that is found is\u2026 itself! That&#8217;s because the unqualified call to <code>await_ready<\/code> finds the member function of the same name before trying to look for free functions. The compiler thinks it&#8217;s a recursive call, and it complains that the parameter list is incorrect.<\/p>\n<p>To fix this, we need to create wrappers with different names so the name search doesn&#8217;t find ourselves.<\/p>\n<pre>namespace modernizer\r\n{\r\n    template&lt;typename Awaitable&gt;\r\n    inline auto adl_await_ready(Awaitable&amp; awaitable)\r\n    { return await_ready(awaitable); }\r\n\r\n    template&lt;typename Awaitable, typename Handle&gt;\r\n    inline auto adl_await_suspend(Awaitable&amp; awaitable, Handle handle)\r\n    { return await_suspend(awaitable, handle); }\r\n\r\n    template&lt;typename Awaitable&gt;\r\n    inline auto adl_await_resume(Awaitable&amp; awaitable)\r\n    { return await_resume(awaitable); }\r\n\r\n    template&lt;typename Awaitable&gt;\r\n    struct cpp20_await_adapter\r\n    {\r\n        Awaitable&amp; awaitable;\r\n\r\n        bool await_ready()\r\n        { return adl_await_ready(awaitable); }\r\n\r\n        template&lt;typename Handle&gt;\r\n        auto await_suspend(Handle handle)\r\n        { return adl_await_suspend(awaitable, handle); }\r\n\r\n        auto await_resume()\r\n        { return adl_await_resume(awaitable); }\r\n    };\r\n\r\n    template&lt;typename Awaitable&gt;\r\n    auto make_cpp20_await_adapter(Awaitable&amp; awaitable)\r\n    {\r\n        return cpp20_await_adapter&lt;Awaitable&gt;{ awaitable };\r\n    }\r\n}\r\n<\/pre>\n<p>The helper functions with the <code>adl_<\/code> prefix forward to the corresponding unprefixed function via argument-dependent lookup. We can be a little sloppy and use an lvalue reference for the awaitable instead of a forwarding reference because our caller always passes an lvalue.<\/p>\n<p>We then revise our await adapter to use the forwarder functions. Basically, we&#8217;re renaming the function, and it&#8217;s the renamed functions we use in our adapter.<\/p>\n<p>We also create a <code>make_<\/code> function to simplify our usage later, just in case we are running in C++11 mode, in which case <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/class_template_argument_deduction\"> class template argument deduction<\/a> is not available.<\/p>\n<p>Okay, now that we have the adapter, we can deploy it to bring the old Visual Studio header files into the C++20 coroutine world.<\/p>\n<pre>#if defined(_MSC_VER) &amp;&amp; (_MSC_VER &lt; 1920) &amp;&amp; defined(_RESUMABLE_FUNCTIONS_SUPPORTED)\r\nnamespace std\r\n{\r\n    template&lt;typename T&gt;\r\n    auto operator co_await(future&lt;T&gt;&amp; x)\r\n    { return ::modernizer::make_cpp20_await_adapter(x); }\r\n\r\n    template&lt;typename T&gt;\r\n    auto operator co_await(future&lt;T&gt;&amp;&amp; x)\r\n    { return ::modernizer::make_cpp20_await_adapter(x); }\r\n}\r\n\r\nnamespace Concurrency\r\n{\r\n    template&lt;typename T&gt;\r\n    auto operator co_await(task&lt;T&gt;&amp; x)\r\n    { return ::modernizer::make_cpp20_await_adapter(x); }\r\n\r\n    template&lt;typename T&gt;\r\n    auto operator co_await(task&lt;T&gt;&amp;&amp; x)\r\n    { return ::modernizer::make_cpp20_await_adapter(x); }\r\n}\r\n#endif\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: We could try to generalize the wrapper functions to accommodate future additions to the coroutine specification, such as when the <code>await_suspend<\/code> method was extended to allow returning a <code>bool<\/code> to control whether suspension should be bypassed.<\/p>\n<pre>namespace modernizer\r\n{\r\n    template&lt;typename ...Args&gt;\r\n    inline auto adl_await_ready(Args&amp;&amp;... args)\r\n    { return await_ready(std::forward&lt;Args&gt;(args)...); }\r\n\r\n    template&lt;typename ...Args&gt;\r\n    inline auto adl_await_suspend(Args&amp;&amp;... args)\r\n    { return await_suspend(std::forward&lt;Args&gt;(args)...); }\r\n\r\n    template&lt;typename ...Args&gt;\r\n    inline auto adl_await_resume(Args&amp;&amp;... args)\r\n    { return await_resume(std::forward&lt;Args&gt;(args)...); }\r\n    template&lt;typename Awaitable&gt;\r\n\r\n    struct cpp20_await_adapter\r\n    {\r\n        Awaitable&amp; awaitable;\r\n\r\n        template&lt;typename ...Args&gt;\r\n        auto await_ready(Args&amp;&amp;... args)\r\n        { return await_ready(awaitable, std::forward&lt;Args&gt;(args)...); }\r\n\r\n        template&lt;typename ...Args&gt;\r\n        auto await_suspend(Args&amp;&amp;... args)\r\n        { return await_suspend(awaitable, std::forward&lt;Args&gt;(args)...); }\r\n\r\n        template&lt;typename ...Args&gt;\r\n        auto await_resume(Args&amp;&amp;... args)\r\n        { return await_resume(awaitable, std::forward&lt;Args&gt;(args)...); }\r\n    };\r\n}\r\n<\/pre>\n<p>Note that this version has the wrinkle that if you forgot to create the <code>adl_<\/code>-prefixed wrappers, you sent the compiler into infinite recursion as it expanded <code>await_ready<\/code> to itself with an extra parameter, which then expanded to itself with two extra parameters, and so on until you hit a compiler internal limit or the compiler crashed.<\/p>\n<p>This extra wrinkle is future-proof but also unnecessary, because the point of this adapter is not to anticipate the future but to accommodate the past. Therefore, it need only support what was allowed in the past.<\/p>\n<p>\u00b9 We&#8217;ll get to step 1 next spring.<\/p>\n<p>\u00b2 <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\/\"> Visual Studio has been making steps in that direction as well<\/a>, and they &#8220;recommend that existing coroutine users move to standard coroutines as soon as possible&#8221; because new coroutine features will not be backported to legacy mode. We&#8217;re all moving towards the <a title=\"Microspeak: North star\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20151103-00\/?p=91861\"> same goal<\/a>.\u2074<\/p>\n<p>\u00b3 That PR also did some compiler error message metaprogramming so that C++\/WinRT gives a more comprehensible error when you try to <code>co_await<\/code> something that isn&#8217;t <code>co_await<\/code>-able.<\/p>\n<p>\u2074 Looks like somebody <a title=\"wiktionary: North Star: Changes made on 13 August 2020\" href=\"https:\/\/en.wiktionary.org\/w\/index.php?title=North_Star&amp;type=revision&amp;diff=60066735&amp;oldid=58710273\"> added a business definition for <i>North Star<\/i> to wiktionary in August 2020<\/a> but didn&#8217;t include any citations. I hope nobody adds me as a citation, because that would just make a circular reference.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Early moves in a direction that didn&#8217;t pan out.<\/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,2],"class_list":["post-104227","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code","tag-history"],"acf":[],"blog_post_summary":"<p>Early moves in a direction that didn&#8217;t pan out.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104227","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=104227"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104227\/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=104227"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104227"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104227"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}