{"id":102354,"date":"2019-03-22T07:00:00","date_gmt":"2019-03-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102354"},"modified":"2019-05-07T06:44:40","modified_gmt":"2019-05-07T13:44:40","slug":"20190322-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190322-00\/?p=102354","title":{"rendered":"Turning anything into a fire-and-forget coroutine"},"content":{"rendered":"<p><a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190321-00\/?p=102350\">Last time<\/a>, we wrote a helper function for converting an awaitable into a <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code>, as well as another helper function that takes a lambda that <i>returns<\/i> an awaitable, and which invokes the lambmda as a <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code>. <\/p>\n<p>After I wrote the two functions, I wondered if I could unify them. Mostly because I wanted to use the same name <code>no_await<\/code> for both functions. <\/p>\n<p>This took me down the horrible rabbit hole known as C++ template metaprogramming. I wanted two versions of the function, one that is used if the parameter is awaitable, and another that is used if the parameter is a functor. This led me to try using things like <code>std::<\/code><code>enable_<\/code><code>if<\/code> to detect which case I&#8217;m in, and that led to lots of frustration, especially because there&#8217;s no easy way to detect if a type is awaitable. My closest approach was <\/p>\n<pre>\ntemplate&lt;typename T, typename Promise = std::void_t&lt;&gt;&gt;\nstruct is_awaitable : std::false_type {};\n\ntemplate&lt;typename T&gt;\nstruct is_awaitable&lt;T, std::void_t&lt;typename std::experimental::coroutine_traits&lt;T&gt;::promise_type&gt;&gt; : std::true_type {};\n\ntemplate&lt;typename T&gt;\ninline constexpr bool is_awaitable_v = is_awaitable&lt;T&gt;::value;\n<\/pre>\n<p>which infers that a type is awaitable by sniffing whether it has an associated <code>promise_<\/code><code>type<\/code>. This isn&#8217;t foolproof, because some types like <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code> have a <code>promise_<\/code><code>type<\/code> that cannot be awaited. <\/p>\n<p>My first realization was that I could flip the test. Instead of checking whether the argument is awaitable, I check whether it is invokable. <\/p>\n<p>My second realization was that I didn&#8217;t have to do fancy template metaprogramming at all. I could take advantage of the new <a HREF=\"https:\/\/hackernoon.com\/a-tour-of-c-17-if-constexpr-3ea62f62ff65\"><code>if constexpr<\/code> feature<\/a>. <\/p>\n<pre>\ntemplate&lt;typename T&gt;\nfire_and_forget no_await(T t)\n{\n    if constexpr (std::is_invocable_v&lt;T&gt;)\n    {\n        co_await t();\n    }\n    else\n    {\n        co_await t;\n    }\n}\n<\/pre>\n<p>Now you can use <code>no_<\/code><code>await<\/code> with awaitables or functors that return awaitables. <\/p>\n<pre>\nvoid Stuff()\n{\n  \/\/ Start this operation but don't wait for it to finish\n  no_await(DoSomethingAsync());\n\n  \/\/ Start this sequence of things and don't wait for\n  \/\/ them to finish.\n  no_await([=]() -&gt; IAsyncAction\n  {\n    co_await Step1Async();\n    \/\/ Step 2 doesn't start until Step 1 completes.\n    co_await Step2Async();\n  });\n}\n<\/pre>\n<p>On the other hand, for the case of the lambda passed to <code>no_<\/code><code>await<\/cODE>, you could just declare your lambda as returning a <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code>, and then you wouldn't need <code>no_<\/code><code>await<\/code>. <\/p>\n<pre>\nvoid Stuff()\n{\n  \/\/ Start this operation but don't wait for it to finish\n  no_await(DoSomethingAsync());\n\n  \/\/ Start this sequence of things and don't wait for\n  \/\/ them to finish.\n  invoke_async_lambda([=]() -&gt; winrt::fire_and_forget\n  {\n    co_await Step1Async();\n    \/\/ Step 2 doesn't start until Step 1 completes.\n    co_await Step2Async();\n  });\n}\n<\/pre>\n<p>But I like the fact that the first example uniformly uses the name <code>no_<\/code><code>await<\/code> to describe the concept of \"I'm not going to wait for this thing to finish.\" And also I'm perhaps unduly attached to the cute name. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Turning on the metaprogramming.<\/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-102354","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Turning on the metaprogramming.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102354","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=102354"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102354\/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=102354"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102354"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102354"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}