{"id":102671,"date":"2019-07-09T07:00:00","date_gmt":"2019-07-09T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102671"},"modified":"2019-07-08T18:27:05","modified_gmt":"2019-07-09T01:27:05","slug":"20190709-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190709-00\/?p=102671","title":{"rendered":"Detecting in C++ whether a type is defined, part 2: Giving it a special name"},"content":{"rendered":"<p><b>Warning to those who got here via a search engine<\/b>: Don&#8217;t use this version. Keep reading to the end of the series.<\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/oldnewthing\/20190708-00\/?p=102664\"> Last time<\/a>, we detected whether a type was defined by setting up the unqualified name search order so that the name search would find the type if it were defined, or a fallback type if not. One problem with that technique was that the search had to be done from the specially-constructed namespace.<\/p>\n<p>So let&#8217;s fix that, and build on the result.<\/p>\n<pre>\/\/ awesome.h\r\nnamespace awesome\r\n{\r\n  \/\/ might or might not contain\r\n  struct special { ... };\r\n}\r\n\r\n\/\/ your code\r\nnamespace detect::impl\r\n{\r\n  struct not_implemented {};\r\n  using special = not_implemented;\r\n}\r\n\r\nnamespace awesome::detect\r\n{\r\n  using namespace ::detect::impl;\r\n  <span style=\"color: blue;\">using special_maybe = special;<\/span>\r\n}\r\n<\/pre>\n<p>This time, I introduce a new type <code>special_<\/code><code>maybe<\/code>. I did it inside the <code>awesome<\/code><code>::detect<\/code> namespace, so the name <code>special<\/code> on the right hand side undergoes unqualified name lookup, like we described last time, and it will pick either the defined type <code>::awesome<\/code><code>::special<\/code> or the fallback type <code>::detect<\/code><code>::impl<\/code><code>::special<\/code>. You can then use some new helpers:<\/p>\n<pre>namespace detect\r\n{\r\n  template&lt;typename T&gt;\r\n  constexpr bool is_defined_v =\r\n    !std::is_same_v&lt;T, impl::not_implemented&gt;;\r\n}\r\n\r\nvoid foo()\r\n{\r\n if constexpr (detect::is_defined_v\r\n                &lt;awesome::detect::special_maybe&gt;) {\r\n   \/\/ do something now that we know \"special\" exists.\r\n }\r\n}\r\n<\/pre>\n<p>This looks like it would work great, but it doesn&#8217;t. Because inside the &#8220;do something now that we know <code>special<\/code> exists&#8221;, you probably want to use <code>special<\/code>. But you can&#8217;t, because <code>special<\/code> might not exist!<\/p>\n<p>While it&#8217;s true that <code>if constexpr<\/code> tells the compiler to discard the not-taken branch, the code in the not-taken branch must still be valid. If <code>special<\/code> is not defined, then the body of the <code>if constexpr<\/code> will contain references to the nonexistent entity <code>special<\/code>, so it will not compile. You could try using <code>special_maybe<\/code>, but that&#8217;s just a dummy type, and it won&#8217;t have the methods you want to call.<\/p>\n<p>So we have to play a trick: Use the type without saying the type!<\/p>\n<pre>template&lt;typename T, typename TLambda&gt;\r\nvoid <a href=\"https:\/\/www.youtube.com\/watch?v=fWNaR-rxAic\">call_me_maybe<\/a>(TLambda&amp;&amp; lambda)\r\n{\r\n  if constexpr (is_defined_v&lt;T&gt;) {\r\n    lambda(static_cast&lt;T*&gt;(nullptr));\r\n  }\r\n}\r\n<\/pre>\n<p>This helper function doesn&#8217;t look all that useful. I mean, if the type exists, then we call the lambda. That just puts us back where we started, doesn&#8217;t it? I mean, the lambda will need to use the type, which it can&#8217;t do if the type doesn&#8217;t exist.<\/p>\n<p>Not quite. Because it lets us do this:<\/p>\n<pre>void foo(Source const&amp; source)\r\n{\r\n  call_me_maybe&lt;awesome::detect::special_maybe&gt;(\r\n    [&amp;](auto* p)\r\n    {\r\n      using T = std::decay_t&lt;decltype(*p)&gt;;\r\n      T::static_method();\r\n      auto s = source.try_get&lt;T&gt;();\r\n      if (s) s-&gt;something();\r\n    });\r\n}\r\n<\/pre>\n<p>What&#8217;s going on?<\/p>\n<p>The way C++ lambdas work is that a lambda becomes an anonymous type with an <code>operator()<\/code> method. For your typical lambda, the <code>operator()<\/code> is a const-qualified method whose prototype matches that of the lambda:<\/p>\n<pre>auto lambda1 = [](int v) -&gt; void { ... };\r\n\r\n\/\/ becomes\r\n\r\nstruct anonymous1\r\n{\r\n auto operator()(int v) -&gt; void const { ... };\r\n};\r\nauto lambda1 = anonymous1();\r\n<\/pre>\n<p>However, if the parameter list uses <code>auto<\/code>, then the <code>operator()<\/code> itself becomes a template function:<\/p>\n<pre>auto lambda2 = [](auto v) -&gt; void { ... };\r\n\r\n\/\/ becomes\r\n\r\nstruct anonymous2\r\n{\r\n template&lt;typename T&gt;\r\n auto operator()(T v) -&gt; void const { ... };\r\n};\r\nauto lambda2 = anonymous2();\r\n<\/pre>\n<p>Next, we take advantage of the fact that in a template function, entities that are dependent upon the template parameter are not resolved until the template is instantiated. In this case, the template function is the <code>operator()<\/code> of the lambda.<\/p>\n<p>This means that our lambda body can do things that depend on the type of <code>p<\/code>, and the compiler can&#8217;t validate that those things are meaningful until the template is instantiated, because it isn&#8217;t until that time that the templated <code>operator()<\/code> is instantiated and the compiler knows what type it needs to use.<\/p>\n<p>In other words, we use the incoming parameter <i>merely for its type information<\/i>. We extract the type of the pointed-to object and call it <code>T<\/code>. Then whenever we would normally say <code>special<\/code>, we just say <code>T<\/code>.\u00b9<\/p>\n<p>And then we realize that we don&#8217;t have to call it <code>T<\/code>. We can call it\u2026 <code>special<\/code>!<\/p>\n<pre>void foo(Source const&amp; source)\r\n{\r\n  call_me_maybe&lt;awesome::detect::special_maybe&gt;(\r\n    [&amp;](auto* p)\r\n    {\r\n      using <span style=\"color: blue;\">special<\/span> = std::decay_t&lt;decltype(*p)&gt;;\r\n      special::static_method();\r\n      auto s = source.try_get&lt;special&gt;();\r\n      if (s) s-&gt;something();\r\n    });\r\n}\r\n<\/pre>\n<p>With this little change, the code inside the lambda looks pretty much like the code you would have written all along, with the bonus feature that it&#8217;s legal code even if <code>special<\/code> doesn&#8217;t exist!<\/p>\n<p>We&#8217;re getting closer. Next time, we&#8217;ll get rid of all this <code>maybe<\/code> nonsense.<\/p>\n<p>\u00b9 C++20 makes this a little easier by letting us get the type directly, rather than having to extract it from the parameter.<\/p>\n<pre>void foo(Source const&amp; source)\r\n{\r\n  call_me_maybe&lt;awesome::detect::special_maybe&gt;(\r\n    [&amp;]<span style=\"color: blue;\">&lt;typename T&gt;(T*)<\/span>\r\n    {\r\n      T::static_method();\r\n      auto s = source.try_get&lt;T&gt;();\r\n      if (s) s-&gt;something();\r\n    });\r\n}\r\n<\/pre>\n<p>Or, using the second trick:<\/p>\n<pre>void foo(Source const&amp; source)\r\n{\r\n  call_me_maybe&lt;awesome::detect::special_maybe&gt;(\r\n    [&amp;]<span style=\"color: blue;\">&lt;typename special&gt;(special*)<\/span>\r\n    {\r\n      special::static_method();\r\n      auto s = source.try_get&lt;special&gt;();\r\n      if (s) s-&gt;something();\r\n    });\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Maybe it&#8217;s the real thing, makybe it&#8217;s a fake.<\/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-102671","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Maybe it&#8217;s the real thing, makybe it&#8217;s a fake.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102671","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=102671"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102671\/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=102671"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102671"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102671"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}