19

In his book The C++ Programming Language (third edition) Stroustrup teaches to define individual components in their own namespace and import them in a general namespace.

For example:

namespace array_api {    
    struct array {};    
    void print(const array&) { }
}

namespace list_api {
    struct list {};        
    void print(const list&) { }
}

namespace api {
    using array_api::array;
    using list_api::list;
}

I looks interesting, but I have never seen this approach used in practice.

Why is this technique almost never used?

StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • 9
    I'd say because it requires more effort then not doing :P people be lazy dog – thecoshman Jun 01 '13 at 09:57
  • 1
    As in most of cases, because people don't know what it is for, and what good it will bring in the project. – SChepurin Jun 01 '13 at 10:23
  • 4
    Counterexample: Boost. Most boost libraries have their own namespace, and then provide *convenience headers* that not only include a number of headers, but also import their symbols one level higher in the hierarchy of namespaces, up until the main level `boost` headers. – Matthieu M. Jun 01 '13 at 10:29
  • As B. Stroustroup noted himself its usefulness is most evident in large projects (as Boost, for example) - "A namespace is a scope. Thus, ‘‘namespace’’ is a very fundamental and relatively simple concept. The larger a program is, the more useful namespaces are to express logical separations of its parts. Ordinary local scopes, global scopes, and classes are namespaces." – SChepurin Jun 01 '13 at 10:32
  • Good for you. I did see use of namespaces like this or like java, its a horrible mess. Where I have power I allow a namespace for every say 250 KLOC. If you ship a library, sure, put it in its own single namespace, but in an application it mostly just create trouble. – Balog Pal Jun 01 '13 at 11:50

4 Answers4

6

Mostly I wonder what the benefits would be (as Raymond Chen says, every feature starts with -100 points). However, I have a counterpoint to offer: Luabind, which does use something that looks like this. See luabind/object.hpp, which essentially says:

namespace luabind {
  namespace adl {
    class object {
    };
  }
  using adl::object;
}

From the name alone we can infer the motivation: to support argument-dependent lookup. Given what the user knows as a luabind::object, which is actually a luabind::adl::object, related functions will be discovered automatically by the compiler from the luabind::adl namespace. Yet those functions, which the user may not need to know about in a very explicit way, do not "pollute" the main luabind namespace. So that's nice, I guess.

But you know what's not nice? Forward declaring one of these classes. This fails:

namespace luabind { class object; }

You need to do this instead:

namespace luabind { namespace adl { class object; } }

Thus the abstraction quickly leaks, as the user does need to know about this magic adl namespace after all.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • 2
    This could suggest that library writers using the technique should provide e.g. both `lib/submodule/foo.hpp` and `lib/submodule/foo_fwd.hpp`. – Luc Danton Jun 01 '13 at 10:45
  • -1 for not explaining how this relates to ADL. You don't need to do anything special to support ADL, it "just works." This looks like a workaround for a buggy compiler, or a particular corner case. – Potatoswatter Jun 01 '13 at 12:18
  • @Potatoswatter: I did say "Yet those functions, which the user may not need to know about in a very explicit way, do not "pollute" the main luabind namespace." So while we know that ADL lets us put functions in the `adl` namespace, I did call out one possible (if tenuous) reason why we might not want to just dump everything into the higher-level namespace. I still don't think it's a /sufficient/ reason to adopt this technique in general; maybe Daniel Wallin will someday find this and tell us why he did it. – John Zwinck Jun 01 '13 at 12:25
  • @JohnZwinck If the "public" API functions are in `luabind::` but the classes are defined in `luabind::adl::` that ironically implies that ADL won't work for the user calling the API. There are other ways to associate the public classes to the private namespace, such as tag dispatching, if ADL is so important to the internal implementation. But usually it's the other way around: more important to the user API than to the internals (where all the functions are already in scope). – Potatoswatter Jun 01 '13 at 14:42
3

Simply because nobody learns it. Most programmers are taught Java-style OOP, and it's more common to see C++ code which encapsulates a namespace-style API into a class.

C++ has argument-dependent function lookup (ADL), which allows it to select a function from an API based on the namespaces of the types of the arguments it's called with. It's a powerful mechanism which allows you to bypass much of the OOP boilerplate yet retain the benefits. But it's not really taught to beginners, for no good particular reason.

For what it's worth, your example is roughly equivalent to this condensed version:

namespace api {

struct array {
    friend void print(const array&) { }
};

struct list {       
    friend void print(const list&) { }
};

}

When you define a function as a friend within a class, it belongs not to the class, but the enclosing namespace. Moreover, its name is available only within the class, not the enclosing namespace, except when it's called using the class as an argument.

Polluting the global namespace with array_api and list_api namespaces is a bad idea. Better to make a hierarchy as in my example.

So, given the above, you could do this:

api::array x;

print( x ); // ADL finds api::print defined inside api::array

Indeed, this is how the iostream-style operator << works. You don't need a using declaration (or directive) to print objects from a library.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
0

I guess because it reduces encapsulation. In your example, it's going to be a right pain when you write map_api to go to the api.h and import it into the api namespace. Having one big header api.h that imports every sub-namespace is not exactly include minimization.

  • map.h:

    namespace map_api { /* fns */ }
    
  • api.h:

    #include <map.h>
    namespace api { using map_api::map; }
    

Or, considering other header layouts: if the imports into the api namespace happen in another file:

  • map_internal.h:

    namespace map_api { /* fns */ }
    
  • map_api.h:

    #include <map_internal.h>
    namespace api { using map_api::map; }
    

That's pretty painful too, having to split out the api into two files like that.

The final possibility is doing it all in one file:

  • map.h:

    namespace map_api { /* fns */ }
    namespace api { using map_api::map; }
    

In that case, what's the point I wonder of putting things in two namespaces that are on the same level of abstraction, if they're both equally exposed to all clients/includers..?

Nicholas Wilson
  • 9,435
  • 1
  • 41
  • 80
0

I feel the other answers have left out the first, simplest, and most useful way to compose namespaces: to use only what you need.

namespace Needed {                                                                                                                                                                            
  using std::string;                                                                                                                                                                          
  using std::bind;                                                                                                                                                                            
  using std::function;                                                                                                                                                                        
  using std::cout;                                                                                                                                                                            
  using std::endl;                                                                                                                                                                            
  using namespace std::placeholders;                                                                                                                                                          
}                                                                                                                                                                                             


int main(int argc, char* argv[])                                                                                                                                                              
{                                                                                                                                                                                             

  /*  using namespace std;                                                                                                                                                                    
      would avoid all these individual using clauses,                                                                                                                     
      but this way only these are included in the global                                                                                                                                      
      namespace.                                                                                                                                                                          
  */                                                                                                                                                                                          

 using namespace Needed;  // pulls in the composition

 string s("Now I have the namespace(s) I need,");

 string t("But not the ones I don't.");

 cout << s << "\n" << t << endl;                                                                                               

 // ...

Thus any conflicts with other parts of the STL are avoided,as well as the need to preface the common used functions with std::.

Chris Reid
  • 460
  • 4
  • 9