{"id":103230,"date":"2019-12-19T07:00:00","date_gmt":"2019-12-19T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103230"},"modified":"2019-12-19T06:30:33","modified_gmt":"2019-12-19T14:30:33","slug":"20191219-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191219-00\/?p=103230","title":{"rendered":"C++ coroutines: The <CODE>co_await<\/CODE> operator and the function search algorithm"},"content":{"rendered":"<p>So you&#8217;re following along <a href=\"https:\/\/kennykerr.ca\/\"> Kenny Kerr&#8217;s blog<\/a> and you get to the part where <a href=\"https:\/\/kennykerr.ca\/2018\/03\/13\/cppwinrt-coroutines-thread-pool\/\"> he uses <code>co_await<\/code> on a time duration<\/a>:<\/p>\n<pre>    co_await 5s;\r\n<\/pre>\n<p>so you try it:<\/p>\n<pre>#include &lt;chrono&gt;\r\nusing namespace std::chrono;\r\n \r\nwinrt::IAsyncAction Delay10Seconds()\r\n{\r\n   co_await 10s;\r\n   co_return;\r\n}\r\n<\/pre>\n<p>and you get the error message<\/p>\n<blockquote class=\"q\"><p>no callable &#8216;await_resume&#8217; function found for type &#8216;Expression&#8217; where Expression=std::chrono::seconds<\/p><\/blockquote>\n<p>We learned that this error message means that we ended up awaiting something that can&#8217;t be awaited. We were hoping that the <code>operator co_await<\/code> would convert the <code>10s<\/code> into an awaiter, but it didn&#8217;t work. As a result, we ended up using the <code>std::<\/code><code>chrono::<\/code><code>seconds<\/code> as its own awaiter, but since it doesn&#8217;t meet the requirements for an awaiter, you get an error.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191218-00\/?p=103221\"> As we learned last time<\/a>, when you <code>co_await<\/code> an expression, one of the steps in obtaining an awaiter is looking for a corresponding overloaded <code>operator co_await<\/code> that accepts the expression. This search follows the usual mechanism for overloaded operators:<\/p>\n<ul>\n<li>A search is conducted for an overloaded operator declared as a member of the class.<\/li>\n<li>A search is conducted for an overloaded operator declared as a free function.<\/li>\n<\/ul>\n<p>Now, the <code>std::<\/code><code>chrono::<\/code><code>seconds<\/code> doesn&#8217;t implement <code>operator co_await<\/code> on its own, so we must search for the overloaded operator as a free function.<\/p>\n<p>Tht search for a free function includes the <code>std::chrono<\/code> namespace, thanks to argument-dependent lookup. And it includes the namespace that is currently active, plus its parent namespaces. And it includes any names that have been imported into those namespaces.<\/p>\n<p>In the case of a duration, the relevant <code>operator co_await<\/code> is in none of those places. It&#8217;s in the <code>winrt<\/code> namespace.<\/p>\n<p>In order for it to be found, you need to be inside a namespace (or sub-namespace) of <code>winrt<\/code>, or you must have imported <code>winrt::operator co_await<\/code> into your namespace with a <code>using namespace ::winrt;<\/code> statement.<\/p>\n<p>If you operate entirely within C++\/WinRT, then doing a <code>using namespace ::winrt;<\/code> is probably not a big deal. But if your code straddles the C++\/WinRT and ABI worlds (or worse, straddles the C++\/WinRT and C++\/CX worlds, or heaven forfend, operates in all three worlds), then blanket-importing the <code>winrt<\/code> namespace is probably not a good idea.<\/p>\n<p>Fortunately, there&#8217;s a workaround.\u00b9<\/p>\n<p>You can use <code>co_await winrt::resume_after(duration)<\/code> as a drop-in substitute for <code>co_await duration;<\/code>. This is literally what happens anyway, because the <code>operator co_await<\/code> definition is<\/p>\n<pre>namespace winrt\r\n{\r\n    inline auto operator co_await(Windows::Foundation::TimeSpan duration)\r\n    {\r\n        return resume_after(duration);\r\n    }\r\n}\r\n<\/pre>\n<p>One lesson learned from this exercise is that it may not a great idea to define a <code>co_await<\/code> operator outside the namespace of the object being awaited\u00b2 because argument-dependent lookup won&#8217;t find the operator if somebody tries to await the object from outside its home namespace.<\/p>\n<p>Another lesson learned is that if you do define a <code>co_await<\/code> operator outside the namespace of the object being awaited, you should define a named function that does the work, and make your <code>co_await<\/code> operator call the named function. That way, people who are not <code>using<\/code> your namespace can still access the underlying functionality by using the named function.<\/p>\n<p>\u00b9 Another workaround is to explicitly invoke the <code>operator co_await<\/code> from the <code>winrt<\/code> namespace.<\/p>\n<pre>co_await winrt::operator co_await(duration);\r\n<\/pre>\n<p>Let us not speak of this workaround again.<\/p>\n<p>\u00b2 Corollary: It may not be a great idea to define a <code>co_await<\/code> operator for a language standard type.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>At the confluence of multiple C++ features.<\/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-103230","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>At the confluence of multiple C++ features.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103230","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=103230"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103230\/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=103230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103230"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}