<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div><blockquote type="cite" class=""><div class=""><div class=""><br class="">Message: 4<br class="">Date: Fri, 26 Jun 2020 00:05:17 -0400<br class="">From: Louis Dionne via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org" class="">cfe-dev@lists.llvm.org</a>><br class="">To: Clang Dev <<a href="mailto:cfe-dev@lists.llvm.org" class="">cfe-dev@lists.llvm.org</a>><br class="">Subject: [cfe-dev] RFC: A mechanism for importing a declaration only<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>when it<span class="Apple-tab-span" style="white-space:pre">     </span>exists<br class="">Message-ID: <<a href="mailto:B287F98A-A416-489E-B122-B78468303C3C@apple.com" class="">B287F98A-A416-489E-B122-B78468303C3C@apple.com</a>><br class="">Content-Type: text/plain; charset=us-ascii<br class=""><br class="">Hi,<br class=""><br class="">The C++ Standard Library has multiple headers of the form <cfoobar>, which are<br class="">basically importing declarations from the corresponding <foobar.h> header into<br class="">namespace std. For example, <cstdio> basically imports all the declarations from<br class=""><stdio.h> into namespace std:<br class=""><br class="">    namespace std {<br class="">        using ::FILE;<br class="">        using ::fpos_t;<br class="">        using ::size_t;<br class="">        ...<br class="">    }<br class=""><br class="">When porting libc++ to new platforms, especially more embedded-ish platforms, <br class="">it is very common that some declarations are not provided by the underlying<br class="">C Standard Library. This leads to having to detect what platform we're on,<br class="">and then to conditionally exclude some declarations:<br class=""><br class="">    namespace std {<br class="">    #if !defined(SOME_WEIRD_PLATFORM)<br class="">        using ::FILE;<br class="">        using ::fpos_t;<br class="">    #endif<br class="">        using ::size_t;<br class="">    }<br class=""><br class="">Unfortunately, different platforms often offer slightly different subsets, so<br class="">these #ifs quickly become difficult to maintain. Trying to find common themes<br class="">for excluding declarations (e.g. #if !defined(_LIBCPP_HAS_NO_FILE)) is vain,<br class="">because the subset provided by a platform is often arbitrary. For example, I've<br class="">seen platforms where malloc() and free() were not provided, however operator new<br class="">was -- so trying to carve out something like _LIBCPP_HAS_NO_ALLOCATION wouldn't<br class="">really make sense.<br class=""><br class="">Given the difficulty of manually excluding such using declarations, I came to<br class="">the conclusion that what we wanted was often something like (pseudocode):<br class=""><br class="">    namespace std {<br class="">    #if __has_declaration(::FILE)<br class="">        using ::FILE;<br class="">    #endif<br class=""><br class="">    #if __has_declaration(::fpos_t)<br class="">        using ::fpos_t;<br class="">    #endif<br class=""><br class="">    #if __has_declaration(::size_t)<br class="">        using ::size_t;<br class="">    #endif<br class=""><br class="">        ...<br class="">    }<br class=""><br class="">Basically, we want to import each declaration into namespace std only if the<br class="">global namespace has such a declaration.<br class=""><br class="">Now, I understand this raises several questions. Of course, this couldn't be<br class="">done at the preprocessor level because I don't think we can know whether a <br class="">declaration exists at that time. However, I was curious to ask on this list<br class="">whether someone could think of a reasonable way to solve this problem.<br class=""><br class="">Having some mechanism for doing this would be a huge life improvement for libc++.<br class=""><br class="">Cheers,<br class="">Louis<br class=""><br class=""></div></div></blockquote></div><br class=""><div class="">I’ll take a shot.  About a year ago I developed what I call a "metaparse" statement node for clang, intended to work with a reflection implementation I developed alongside it, which in a sense brings preprocessor expansion ability into constexpr evaluation.</div><div class=""><br class=""></div><div class="">Assume for now __has_declaration is already defined:</div><div class=""><br class=""></div><div class="">```</div><div class="">consteval { //a "metaprogram"</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">     </span>if (__has_declaration(::FILE))</div><div class=""><span class="Apple-tab-span" style="white-space:pre">            </span>__queue_metaparse("using ::FILE;");</div><div class=""><div class=""><span class="Apple-tab-span" style="white-space:pre">       </span>if (__has_declaration(::fpos_t))</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>__queue_metaparse("using ::fpos_t;");</div></div><div class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>if (__has_declaration(::size_t))</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>__queue_metaparse("using ::size_t;");</div></div><div class=""><span class="Apple-tab-span" style="white-space:pre">       </span>__queue_metaparse("static const int TestVar = 3;");</div><div class=""><br class=""></div><div class="">} //...queued metaparses performed here, placing parsed decls into outer scope...</div><div class=""><br class=""></div><div class="">static_assert(TestVar==3);</div><div class="">```</div><div class=""><br class=""></div><div class="">If you wanted to define __has_declaration via what I call my "mirror-image reflection" implem, rather than via a new built-in: you would first probably want to define a public clang::DeclContext method "bool hasDeclNamed(StringRef name)" to handle the lookup, then after building clang-wreflection that method would be available in all reflected DeclContexts.  Then:</div><div class=""><br class=""></div><div class="">```</div><div class="">#define __has_declaration(NAME) reflexpr(::/*reflects the translation unit*/)->hasDeclNamed(#NAME)</div><div class="">```</div><div class=""><br class=""></div><div class="">The following is old code I have not updated in awhile, and the readme/documentation is a mess, but you'll get the gist.</div><div class=""><a href="https://github.com/drec357/clang-meta/" class="">https://github.com/drec357/clang-meta/</a></div><div class=""><br class=""></div><div class="">The reflection stuff did not catch on here because the clang folks did not want the clang AST interface to be exposed via reflection, as it is not stable.  But I was very surprised at least the metaparsing portion did not catch on — it’s a great general purpose tool that would work with any reflection implementation, or in other metaprogramming situations such as yours, and is dirt-simple to understand and use.  </div><div class=""><br class=""></div><div class="">FWIW Microsoft seemed to get the idea, and several months after my release added what they call "source generators" <a href="https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/" class="">https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/</a> to C# which seems to work in this way.  (See their sourceBuilder.append(…) stuff.  No attribution though :/).</div><div class=""><br class=""></div><div class="">Bottom line, to do these sorts of things in general we need reflection + its inverse ("reification" as some call it), and metaparsing or source generation or whatever you want to call seems to be the most straightforward and general solution for that latter task.</div><div class=""><br class=""></div><div class="">Hope that helps the brainstorming, good luck,</div><div class=""><br class=""></div><div class="">Dave</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div></body></html>