#includes within a namespace, to “embed” prewritten stuff in namespace

前端 未结 3 1492
星月不相逢
星月不相逢 2021-01-14 17:23

BRIEF: is it ever safe to do

 namespace Foo {
 #include \"bar\"
 }

Before you blithely say no, I think I have some rules that allow it fair

相关标签:
3条回答
  • 2021-01-14 17:34

    I don't think it's safe. You put all your includes into the namespace Foo... Imagine some of your includes include something from the std namespace... I cannot imagine the mess !

    I wouldn't do that.

    0 讨论(0)
  • 2021-01-14 17:51

    I know this is an old question, but I want to give a more detailed answer anyway. Also, give a real answer to the underlying problem.

    Here's just a few things that can go wrong if you include a header from within a namespace.

    1. The header includes other headers, which are then also included from within the namespace. Then a different place also wants to include these headers, but from outside the namespace. Because the headers have include guards, only one of the includes actually goes in effect, and the actual namespace of the stuff defined in the headers suddenly subtly depends on the order you include other headers.

    2. The header, or any of its included headers, expects to be in the global namespace. For example, standard library headers will very often (in order to avoid conflicts) refer to other standard stuff (or implementation details) as ::std::other_stuff, i.e. expect std to be directly in the global namespace. If you include the header from within a namespace, that's no longer the case. The name lookup for this stuff will fail and the header will no longer compile. And it's not just standard headers; I'm sure there are some instances of this e.g. in the Boost headers too.

    3. If you take care of the first problem by ensuring that all other headers are included first, and the second problem by making sure no fully qualified names are used, things can still go wrong. Some libraries require that other libraries specialize their stuff. For example, a library might want to specialize std::swap, std::hash or std::less for its own type. (You can overload std::swap instead, but you can't do that for std::hash and std::less.) The way to do this is close your library-specific namespace, open namespace std, and put the specialization there. Except if the header of the library is included in arbitrarily deeply nested namespaces, it cannot close those namespaces. The namespace std it attempts to open won't be ::std, but ::YourStuff::std, which probably doesn't contain any primary template to specialize, and even if it did, that would still be the wrong thing to do.

    4. Finally, things in a namespace simply have different names than things outside. If your library isn't header-only but has a compiled part, the compiled part probably didn't nest everything in the namespace, so the stuff in the library has different names than the stuff you just included. In other words, your program will fail to link.

    So in theory, you can design headers that work when included within a namespace, but they're annoying to use (have to bubble up all dependencies to the includer) and very restricted (can't use fully qualified names or specialize stuff in another library's namespace, must be header-only). So don't do it.

    But you have an old library that doesn't use namespaces, and you want to update it to use them without breaking all your old code. Here's what you should do:

    First, you add a subdirectory to your library's include directory. Call it "namespaced" or something like that. Next, move all the headers into that directory and wrap their contents in a namespace.

    Then you add forwarding headers to the base directory. For each file in the library, you add a forwarder that looks like this:

    #ifndef YOURLIB_LEGACY_THE_HEADER_H
    #define YOURLIB_LEGACY_THE_HEADER_H
    
    #include "namespaced/the_header.h"
    using namespace yourlib;
    
    #endif
    

    Now the old code should just work the way it always did.

    For new code, the trick is not to include "namespaced/the_header.h", but instead change the project settings so that the include directory points at the namespaced subdirectory instead of the library root. Then you can simply include "the_header.h" and get the namespaced version.

    0 讨论(0)
  • 2021-01-14 17:57

    Header files are not black boxes. You can always look at a header you're including in your project and see if it is safe to include it inside a namespace block. Or better yet, you can modify the header itself to add the namespace block. Even if the header is from a third-party library and changes in a subsequent release, the header you have in your project won't change.

    0 讨论(0)
提交回复
热议问题