{"id":102664,"date":"2019-07-08T07:00:00","date_gmt":"2019-07-08T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102664"},"modified":"2019-07-08T11:17:19","modified_gmt":"2019-07-08T18:17:19","slug":"20190708-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190708-00\/?p=102664","title":{"rendered":"Detecting in C++ whether a type is defined, part 1: The initial plunge"},"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>Suppose you want to be able to detect in C++ whether a type has been defined. For example, maybe you want to use a type if it exists. This can happen if, say, you are a library like <a href=\"https:\/\/github.com\/Microsoft\/react-native-windows\"> React Native for Windows<\/a>, and you need to be able to run with different versions of the Windows SDK. Or you&#8217;re writing a library where the client can customize the behavior by defining another class with a well-known name. Perhaps you&#8217;re trying to mimic C# partial classes.<\/p>\n<p>My initial idea was to take advantage of <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/unqualified_lookup\"> unqualified name lookup<\/a> by creating an alternate definition for the type that sits at a lower priority than the one we&#8217;re looking for.<\/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  constexpr bool is_special_defined =\r\n    !std::is_same_v&lt;special, ::detect::impl::not_implemented&gt;;\r\n}\r\n<\/pre>\n<p>The idea here is that I declare an alternate version of the <code>special<\/code> structure in the <code>detect::impl<\/code> namespace, and place it in the search order at a location that comes <i>after<\/i> searching in the <code>awesome<\/code> namespace.<\/p>\n<p>The <code>using namespace ::detect<\/code><code>::impl;<\/code> directive makes the names from the <code>detect::impl<\/code> visible as if they had been declared in the global namespace. Why the global namespace? Because the rule for <code>using namespace<\/code> is that the names from the imported-from namespace are treated as if they had been declared in the namespace which is the nearest common ancestor of the importing namespace and the imported-from namespace. In our case, the imported-from namespace is <code>::detect<\/code><code>::impl<\/code> and the importing namespace is <code>::awesome<\/code><code>::detect<\/code>. Since they don&#8217;t even share a common top-level namespace, the nearest common ancestor is the global namespace.<\/p>\n<p>Next, I check the name <code>special<\/code>. The unqualified name lookup searches in the following order:<\/p>\n<ul>\n<li><code>::awesome<\/code><code>::detect<\/code><code>::special<\/code><\/li>\n<li><code>::awesome<\/code><code>::special<\/code><\/li>\n<li><code>::special<\/code> (which, thanks to our <code>using namespace ::detect<\/code><code>::impl;<\/code> directive also searches in <code>::detect<\/code><code>::impl<\/code>.)<\/li>\n<\/ul>\n<p>There is definitely no <code>special<\/code> declared in the <code>::awesome<\/code><code>::detect<\/code> namespace, so it comes down to the other two. If it exists in the <code>::awesome<\/code> namespace, then the unqualified lookup will find that type; otherwise, it will find the one in the <code>::detect<\/code><code>::impl<\/code> namespace.<\/p>\n<p>We then use <code>std::<\/code><code>is_same_v<\/code> to see whether the type we found is our fake one.<\/p>\n<p>This works, but it&#8217;s awkward because you have to do the detection from inside the <code>::awesome<\/code><code>::detect<\/code> namespace, since that&#8217;s where we set up the search order. For every type you want to detect, you need to create an alias in the <code>::detect<\/code><code>::impl<\/code> namespace and a custom <code>is_whatever_defined<\/code> constant.<\/p>\n<p>Next time, we&#8217;ll look at my second attempt.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s play games with unqualified name lookup.<\/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-102664","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Let&#8217;s play games with unqualified name lookup.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102664","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=102664"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102664\/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=102664"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102664"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102664"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}