4

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 fairly safely.

But I don't like them, since they require the includer to separately include all global scope headers needed. Although this mightr be tolerable, if we imagine including within a namespace to be just a special management feature.

And overall, externs and forward declarations just don't work well from within namespaces.

So I gues I am asking

a) What other gotchas

b) is there a better way

== A [[Header-only library]] ==

I like writing libraries. [[Header-only libraries and linker libraries]].

E.g.

#include "Valid.hpp"

defines a template Valid, for a simple wrapper type.

(Don't get bogged down in "You should use some standard library for this rather than your own. This is an exanple. I dunno if Boost or C++ have yet standardized this. I have been using wrappers since templates were added to C++.)

Also, let us say, it is a header only library, that defines, in Valid.hpp, a print function

std::string to_string( const Valid& v ) { std::ostringstream oss; if( v.valid() ) { oss << v; } else { "invalid"; } return oss.str(); }

And because I think it is the right thing to do, I have Valid.hpp include the headers it depends on:

   Valid.hpp:

         #include <iostream>
         #include <sstream>

         template<typename T> 
         class Valid {
         private:
           T value_;
           bool valid_
           ...
         };

         ...

         std::string to_string( const Valid<T>& v ) { ...

So far, so good.

I can use Valid straightforwardly.

== Name collision - trying to use include within namespace to work around ==

But sometimes there is a collision. Sometimes somebody else has their own Valid.

Namespaces to the rescue, right? But I don't want to change all of my existing code to use the namespace. So, I am tempted, in a new project that has a collision, to do

   namespace AG {
    namespace Wrapper {
    #include "lib/AG/Wrapper/Valid.hpp"
    }
    }

    AG::Wrapper::Valid<T> foo_v; 

    ...

PROBLEM: the headers included are no longer freestanding. Everything defined inside is no placed inside namespace AG::Wrapper.

It's not hard to "fix".
Al we "must" do is include all the top level libraries that Valid.hpp depends on. If they have include guards, they will not be re-included.

   #include <iostream>
    #include <sstream>

    namespace AG {
    namespace Wrapper {
    #include "lib/AG/Wrapper/Valid.hpp"
    }
    }

    AG::Wrapper::Valid<T> foo_v; 

    ...

But it is no longer freestanding. :-(

Worse, sometimes the header-only library contains extern declarations and forward declarations of stuff outside itself. These declarations get placed inside the namespace too. In particular, if the extern declarayion is inside a function defined in the namespace.

I.e. sometimes we use extern and forward declarations, rather than included an entire header file. These get included in the namespace.

Q: is there a better way?

== :: doesn't do it ==

Hint: :: doesn't do it. At least not all the time, not in gcc 4.7.2.
(Gcc's behavior in this has changed over time. Gcc 4.1.2 behaved differently.)

E.g.

 Type var;

 namespace Foo {
 void bar() {
    extern ::Type      ::var;;
    extern ::Type      ::Foo::bar;
    extern ::Type::foo      ::bar;  // see the ambiguity?
 };

But it's not just the ambiguity.

int var;

 namespace Foo {
 void bar() {
    extern int var;
 };

works - Foo::bar'svar is equal to ::var.

But it only works because of the declaration outside the namespace.

The following doesn't work

header int var; cpp namespace Foo { void bar() { extern int var; } }

although the following does:

header int var; cpp void bar() { extern int var; } }

Basically, what this amounts to saying is that it is not a trivial refactoring to put functions inside a namespace. Wrapping a namespace around a chunk of code, whether or not it is #include'd, is not a sufficient. ... at least not if there are extern or forward declarations.

And even if you

== Opinion against putting includes within namespaces ==

Stackoverflow folks seem to be against putting #includes inside namespaces:

E.g. How to use class defined in a separate header within a namespace:

... you should never write a #include inside a namespace. Where "never" means, "unless you're doing something really obscure that I haven't thought of and that justifies it". You want to be able to look at a file, and see what the fully-qualified names are of all the things in it. You can't do that if someone comes along later and sticks an extra namespace on the front by including from inside their namespace. – Steve Jessop Jan 6 '12 at 16:38

Overall question:

Is there any way, from deep within a namespace, to say "and now here are some names that I am depending on from the outside world, not within the namespace."?

I.e. I would like to be able to say

namespace A {
void foo() {
   // --- here is a reference to gloal scope extreren ...
4

3 に答える 3

8

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.

于 2013-07-23T09:56:35.167 に答える
4

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.

于 2013-01-11T01:59:41.537 に答える
-1

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.

于 2013-01-11T02:01:29.220 に答える