{"id":105784,"date":"2021-10-11T07:00:00","date_gmt":"2021-10-11T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105784"},"modified":"2021-10-11T06:47:13","modified_gmt":"2021-10-11T13:47:13","slug":"20211011-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211011-00\/?p=105784","title":{"rendered":"Is there a way that my macro can detect that it&#8217;s running in a C++ coroutine?"},"content":{"rendered":"<p>Say you are writing a macro that wants to behave differently depending on whether it is expanded inside a coroutine or not. Specifically, you want to expand to <code>return<\/code> in a regular function, but <code>co_return<\/code> in a coroutine.<\/p>\n<pre>template&lt;T&gt;\r\nT&amp;&amp; TraceValue(T&amp;&amp; v);\r\n\r\n\/\/ NOTE: Just a sketch. A real macro would have to do more work,\r\n\/\/ but we are focusing on the IF_IN_COROUTINE part.\r\n#define TRACE_RETURN() \\\r\n    TraceExit(); IF_IN_COROUTINE(co_return, return)\r\n\r\n#define TRACE_RETURN_VALUE(v) \\\r\n    IF_IN_COROUTINE(co_return TraceExitValue(v), return TraceExitValue(v))\r\n\r\nbool TestSomething()\r\n{\r\n    TRACE_ENTER();\r\n    TRACE_RETURN_VALUE(IsSomethingReady()); \/\/ want \"return\"\r\n}\r\n\r\ntask&lt;bool&gt; TestSomethingAsync()\r\n{\r\n    TRACE_ENTER();\r\n    TRACE_RETURN_VALUE(IsSomethingReady()); \/\/ want \"co_return\"\r\n}\r\n<\/pre>\n<p>Is it possible to write the magic <code>IF_<wbr \/>IN_<wbr \/>COROUTINE<\/code> macro which expands either its first or second parameter?<\/p>\n<p>It&#8217;s not possible in general, because the decision as to whether a function body is a regular function body or a coroutine function body depends on what is inside the body. Specifically, if the body it contains any <code>co_await<\/code> or co_return statements, then it is a coroutine body. Otherwise, it is a regular function body.<\/p>\n<p>Since the macro is expanded as part of the function body, the decision about whether it is a coroutine or not hasn&#8217;t yet been made. In fact, the macro&#8217;s expansion might be the thing that determines whether the function body is a coroutine!<\/p>\n<p>In the second example above, the function body expands to something like this:<\/p>\n<pre>task&lt;bool&gt; TestSomethingAsync()\r\n{\r\n    TraceEnter(__func__, __FILE_, __LINE__);\r\n#if in coroutine\r\n    co_return TraceExitValue(IsSomethingReady());\r\n#else\r\n    return TraceExitValue(IsSomethingReady());\r\n#endif\r\n}\r\n<\/pre>\n<p>Whether this is a coroutine depends on what the macro chooses!<\/p>\n<p>If the macro detects that this is a coroutine, then the body expands to <code>co_return TraceExitValue(...)<\/code>, and it is that <code>co_return<\/code> that makes the function body a coroutine. But if the macro detects that it&#8217;s not a coroutine, then the body says <code>return TraceExitValue(...)<\/code>, and since there is no <code>co_return<\/code> or <code>co_await<\/code> statement, the function body is a regular function body.<\/p>\n<p>You thought your macro was passively detecting whether it was in a coroutine, but in fact it is actively controlling the decision!<\/p>\n<p>Now, you might think, &#8220;Well, can I just base my decision on the function return type?&#8221;<\/p>\n<p>Even if you could detect the return type from a macro (I&#8217;m not sure you can), that still wouldn&#8217;t be good enough. The <code>task&lt;bool&gt;<\/code> might support construction from a <code>bool<\/code>, say to represent an already-completed task, and therefore both <code>co_return boolValue<\/code> and <code>return boolValue<\/code> are legal in the function body.<\/p>\n<p>Basically, you are trying to be a passive predictor of a future that you inadvertently influence. That doesn&#8217;t work well in science fiction, and it doesn&#8217;t work well here either.<\/p>\n<p><b>Bonus paradox<\/b>: Imagine writing the opposite macro:<\/p>\n<pre>#define TRACE_RETURN_VALUE(v) \\\r\n    IF_IN_COROUTINE(return TraceExitValue(v), co_return TraceExitValue(v))\r\n<\/pre>\n<p>This macro tries to be contrary and says, &#8220;Use <code>return<\/code> if I&#8217;m in a coroutine, but <code>co_return<\/code> if I&#8217;m not.&#8221;<\/p>\n<p>We could call this <a href=\"https:\/\/en.wikipedia.org\/wiki\/Russell%27s_paradox\"> Russell&#8217;s<\/a> macro since it creates a similar paradox:<\/p>\n<pre>task&lt;bool&gt; TestSomethingAsync()\r\n{\r\n    TraceEnter(__func__, __FILE_, __LINE__);\r\n#if in coroutine\r\n    return TraceExitValue(IsSomethingReady());\r\n#else\r\n    co_return TraceExitValue(IsSomethingReady());\r\n#endif\r\n}\r\n<\/pre>\n<p>If the coroutine detector says, &#8220;This is a coroutine&#8221;, then the macro expands to <code>return<\/code>, which makes the function body <i>not<\/i> a coroutine. But if the coroutine detector says, &#8220;This is not a coroutine&#8221;, then the macro expands to <code>co_return<\/code>, which makes the function body a coroutine after all!<\/p>\n<p>Proof by logical contradiction that a perfect coroutine-detector macro is impossible to write.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You are part of the decision.<\/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-105784","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You are part of the decision.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105784","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=105784"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105784\/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=105784"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105784"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105784"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}