views:

380

answers:

5

Something that has been troubling me for a while:

The current wisdom is that types should be kept in a namespace that only contains functions which are part of the type's non-member interface (see C++ Coding Standards Sutter and Alexandrescu or here) to prevent ADL pulling in unrelated definitions.

Does this imply that all classes must have a namespace of their own? If we assume that a class may be augmented in the future by the addition of non-member functions, then it can never be safe to put two types in the same namespace as either one of them may introduce non-member functions that could interfere with the other.

The reason I ask is that namespaces are becoming cumbersome for me. I'm writing a header-only library and I find myself using classes names such as project::component::class_name::class_name. Their implementations call helper functions but as these can't be in the same namespace they also have to be fully qualified!

Edit:

Several answers have suggested that C++ namespaces are simply a mechanism for avoiding name clashes. This is not so. In C++ functions that take a parameter are resolved using Argument Dependent Lookup. This means that when the compiler tries to find a function definition that matches the function name it will look at every function in the same namespace(s) as the type(s) of its parameter(s) when finding candidates.

This can have unintended, unpleasant consequences as detailed in A Modest Proposal: Fixing ADL. Sutter and Alexandrescu's rule states never put a function in the same namespace as a class unless it is meant to be part of the interface of that class. I don't see how I can obey that rule unless I'm prepared to give every class its own namespace.

More suggestions very welcome!

+1  A: 

Probably not. See Eric Lippert's post on the subject.

Couple things here:

  1. Eric Lippert is a C# designer, but what he's saying about bad hierarchical design applies here.
  2. A lot of what is being described in that article has to do with naming your class the same thing as a namespace in C#, but many of the same pitfalls apply to C++.

You can save on some of the typedef pain by using typedefs but that's of course only a band-aid.

Billy ONeal
Eric Lippert's advice is great for C#, and maybe .Net in general, but it doesn't really apply to C++ because C++ namespaces don't work the same. C++ namespaces are needed in this case to keep *non-class* functions from being ambiguous.
Gabe
You still should not have class and namespace names be the same. You still run into the same kind of resolution problems. I agree non class functions are a tidbit C# doesn't have to deal with, but C# has extension methods that C++ doesn't deal with either so I think they are about even in that department. In any case, I agree with you that there are better answers here. But I don't dislike my own enough to delete it.
Billy ONeal
+15  A: 

No. I have never heard that convention. Usually each library has its own namespace, and if that library has multiple different modules (e.g. different logical units that differ in functionality), then those might have their own namespace, although one namespace per library is sufficient. Within the library or module namespace, you might use namespace detail or an anonymous namespace to store implementation details. Using one namespace per class is, IMHO, complete overkill. I would definitely shy away from that. At the same time, I would strongly to urge you to have at least one namespace for your library and put everything within that one namespace or a sub-namespace thereof to avoid name clashes with other libraries.

To make this more concrete, allow me to use the venerable Boost C++ Libraries as an example. All of the elements within boost reside in boost::. There are some modules within Boost, such as the interprocess library that have its own namespace such as boost::interprocess::, but for the most part, elements of boost (especially those used very frequently and across modules) simply reside in boost::. If you look within boost, it frequently uses boost::detail or boost::name_of_module::detail for storing implementation details for the given namespace. I suggest you model your namespaces in that way.

Michael Aaron Safyan
+1 for "no" however i would not have one per library - a library is an implementation unit. A namespace is typically to do with teams / projects / sub projects. You could have a namespace that spans 2 libs and an exe
pm100
@pm100, yes, if you have a multi-module project (one with multiple libraries or other logical units), then you might unify them collectively under one namespace, but then I would still suggest subdividing internally with one namespace per library.
Michael Aaron Safyan
+9  A: 

No, no and a thousand times no! Namespaces in C++ are not architectural or design elements. They are simply a mechanism for preventing name clashes. If in practice you don't have name clashes, you don't need namespaces.

anon
A thousand and one times no.
James D
"They are simply a mechanism for preventing name clashes." False. Namespaces are also for directing ADL. Anyway what they're "for" is far less important than what they actually *do*, if what they do is undesirable in some particular cases.
Steve Jessop
Namespaces are precisely *not* a simple naming mechanism! With Argument Dependent Lookup (http://en.wikipedia.org/wiki/Argument_dependent_name_lookup) every function in the same namespace as a class is considered a candidate when an object of the class is used. Hence Sutter and Alexandrescu's rule.
thehouse
But I agree that you don't need one namespace per class. I'm struggling to see how putting two classes in a single namespace inevitably leads to ADL pulling in the wrong functions, as the questioner says. The problems Sutter describes have more to do with what's in namespace `std`, and with unconstrained template parameters, than with what's in users' namespaces. Non-template functions aren't going to unexpectedly ADL-bind to something unrelated in the same or a different namespace.
Steve Jessop
@steve @thehouse namespaces ARE a simple clash avoidance mechanism - if you design your software that way.
anon
Sure, but by that argument namespace std doesn't exist in C++ - if you design your software that way. So again, Sutter's comments are irrelevant, and there is no problem. The more significant question is, given that namespaces *don't actually work* as a clash avoidance mechanism, because ADL re-creates certain clashes that you thought you had avoided (see Sutter's paper for examples), how does one code defensively against the resulting problems? One option is "don't use names in your namespace that are also in std", but that puts the lie to the theory that namespaces are for clash-avoidance.
Steve Jessop
@Neil: did you read the linked article, N1893? I think the real lesson is not to overload names in `std::` regardless of `using namespace`, or to write operators declarations that don't explicitly mention a user-defined type.
Potatoswatter
@steve So because something sometimes doesn't work, one should never use it at all? That's an argument against using namespaces at all.
anon
@Potatocom Sorry, I search in vain in anything I have written in this this thread for "using namespace" or "std::" or "overload"
anon
@steve I think you may be overstressing the importance of `std`. It's just a namespace like any other. If these problems occur with `std` why would they not occur in general?
thehouse
I think the issue is - can namespaces in C++ be used architecturally? Yes, they can, as can just about every other feature of the language. Should they be? I would say no. I am far, far more interested in designing workable and maintainable software than I am in language lawyering (though sometimes one must do a bit of that too, for portability reasons). Maybe the SO "c++" tag should split into "c++-programming" and "c++-language"?
anon
@Neil: I'm saying that if it sometimes doesn't work, make an effort to understand why, and to invent and deploy whatever techniques you can to prevent it failing in astonishing ways. Surely you aren't suggesting the opposite extreme: if something sometimes works, use it as if it always works? If putting each class in its own namespace isn't the right technique, what is? (Partial answer - when naming functions which take a parameter from another namespace, don't use a name which exists in that namespace. But you therefore cannot ever write function templates with unconstrained parameters).
Steve Jessop
Exactly. Neil, I would love to use namespaces as a simple nameing mechanism. I'm not *choosing* for them to work the way they do. I'm just stuck with it.How can I deal with this (trying to obey Sutter's rule) without the 1:1 craziness?
thehouse
@steve "But you therefore cannot ever write template functions with unconstrained parameters" - yet people do.
anon
@thehouse: they can occur with other namespaces, but they rarely do so in practice (I mean, it's fairly rare for std to cause problems, I don't think I've ever seen a real problem with any other namesapce). Other namespaces are less likely to contain functions which ADL-bind when you don't want them to. For instance in Sutter's example 2.1, `copy` has unconstrained parameters. In other namespaces, you should perhaps avoid putting such templates in the same namespace as your classes. But putting two classes in the same namespace doesn't cause the problem.
Steve Jessop
@thehouse Have you actually encountered a problem?
anon
@neil No ;) Doesn't stop me wanting to do right my the Gods of Murphy
thehouse
@neil Not to mention that fact that my library is meant to be used by others - I have no control over the names in their namespace soup
thehouse
@thehouse Of course. But you cannot possibly use the language features of C++ to prevent all programmer/design errors. and sometimes the cure is worse than the disease.
anon
@neil Actually, that's not true. I did have a problem a while back but that was with an unconstrained template function. I fixed it by constraining the parameters a bit
thehouse
@Neil: yes, and if they call that function without fully qualifying it, passing as argument an object of a template parameter type which happens to be a UDT in a namespace which contains an unrelated function of the same name, then it potentially won't work. So people writing those template functions are probably under-stating the requirements of their template parameter types. There isn't really an option to ignore this issue and act as though it can't affect you, at least not if you're writing libraries and portable code, and want to document them correctly.
Steve Jessop
@neil I agree. But Sutter was so adamant (he actually used the words 'never ever' in his book) that I feel quite uneasy about it.
thehouse
@thehouse The way that other people use your code is their problem! It simply isn't possible to make C++ library code safe against its users - at the end of the day they can always defeat you by using macros. Your responsibility is to DOCUMENT the code.
anon
Of course you can ignore the issue and act as though it probably won't affect you. Your proposed split between "c++-programming" and "c++-language" would presumably ban people in the former from saying things like "integers might not be 2's complement", or "dereferencing a null pointer is *undefined behaviour*, not necessarily a segfault", on the grounds that this is "language lawyering", and doesn't apply to "actual programming". The question then is where to draw the line - if I can find one real example of a particular lawyerly concern, does that push it back into the sphere of programming?
Steve Jessop
Example: `template <typename T> void some_fn(T t) { some_fn_helper(t, 0); }`. "The requirements on T include that it must not be in a namespace in which a `some_fn_helper` function exists which is a better match for the parameters `(t,0)` than the following ...". I don't see that much, because people act as though namespaces are just to avoid clashes. If they acted as though namespaces did what they actually do, then I'd see it more often. Putting `some_fn_helper` in its own namespace and (hence) qualifying the call does "fix" the "problem", the question is if there's a better fix.
Steve Jessop
@Steve Sigh. I'm not suggesting banning anything. And the split thing was tongue in cheek. I should have added a smiley. I don't really think the tag should be split, and it wouldn't make any difference if I did.
anon
Sorry, I know that. I just mean that a particular discussion is all very hypothetical and language lawyerly if you've never hit the problem yourself, but becomes highly programming-relevant the first time you get a bug report that would have been prevented, had you considered that never-gonna-happen corner case that actually happened. I was trying to express this within the "different tags" framework, rather than straw-man you.
Steve Jessop
@Steve You obviously work with code that uses many, many namespaces, which I've already suggested may not be a good idea. My own code typically uses three (or four if I'm using the few Boost features I use). And two of those are completely under my control.
anon
Well, I've been known to write code which will be called by people I've never met, so it's something I think about. Many many users would imply many many namespaces. Actually I haven't ever shipped a C++ template library, which is why I'm at the stage of "yes, I see how there would be problems documenting this" rather than either "aha, I've done all this before and have the answers. Here are the rules I used..." or "Yep, we're all doomed, ADL is broken exactly as Sutter says".
Steve Jessop
@Neil The problem is they're *not* completely under your control. The example Sutter gives in his book is where the STL implementation of `vector<T>::operator[]` pulled in a random unrelated function from the `T`'s namespace causing `v[0]` to fail to compile.Whether you know it or not you are always playing at the pleasure of somebody else's code.
thehouse
@thehouse: again, that example wouldn't have happened if the user had kept their unconstrained template parameters out of the namespace their classes live in. Sutter says he doesn't blame the user, but I think I blame the user a little bit. *Someone* has to avoid the bad ADL-binding, and not everybody can say "oh, I don't have to, someone else will do it". By the time you've analysed the issue enough to document it, I suspect (but don't know) you've analysed it enough to fix it. That generic `operator+` just looks fishy to me.
Steve Jessop
@thehouse Compilation errors are no big deal - I actually LIKE compilation errors.
anon
@thehouse @steve Anyway, I'm off to bed!
anon
@steve That example may well have happened even if the function were not templated. It's all a matter of C++ rules deciding the from function is a better match. Unconstrained templated functions just happen to be a very easy example of that.
thehouse
@Neil: anyway, thanks for the discussion. At some point I will certainly look at some internal-use-only code, and consider firstly Sutter's point, whether there are gotchas with ADL, and secondly your point, whether those gotchas would be mostly avoided by treating namespaces solely as uniquifiers, like underscore_gubbins_in_C.
Steve Jessop
@steve @neilThanks for the though-provoking discussion. My conclusion: the problem exists in theory but I can't keep on creating namepspaces are this rate. I'll just hope it doesn't crop up much in practice.
thehouse
@thehouse: I don't think that problem would happen with a non-template function. For example in the first case, the user cannot validly define an `operator+(__gnu_cxx::__normal_iterator<N::X>, long)` explicitly, but they can define one accidentally, via a template that's considered because N is an associate namespace. Which I didn't even *know* prior to reading that paper, let alone systematically considered when writing code. So if I've avoided this particular badness, it's by accident because I happen not to like the looks of that template for other reasons. Which is not a great feeling.
Steve Jessop
So thanks to you also, thehouse. I think you're right that more namespaces can help. I'm not convinced that focussing on classes is the efficient way to address the problem. I think focus on *functions*, and for every function make sure that if it *can* apply to a class, then it *should* apply. Only make unqualified calls to fns that "should" be ADL-overloadable. So far I see no harm in `namespace foo { class Bar { ... }; class Baz { ... }; void swap(Bar void swap(Baz}`. ADL behaves exactly the same whether Bar and Baz are in the same namespace or different.
Steve Jessop
+4  A: 

To avoid ADL, you need only two namespaces: one with all your classes, and the other with all your loose functions. ADL is definitely not a good reason for every class to have its own namespace.

Now, if you want some functions to be found via ADL, you might want to make a namespace for that purpose. But it's still quite unlikely that you'd actually need a separate namespace per class to avoid ADL collisions.

Ben Voigt
A: 

It's quite an interesting paper, but then given the authors there was a good chance it would be. However, I note that the problem concerns mostly:

  • typedef, because they only introduce an alias and not a new type
  • templates

If I do:

namespace foo
{
  class Bar;

  void copy(const Bar&, Bar&, std::string);
}

And invoke it:

#include <algorithms>

#include "foo/bar.h"

int main(int argc, char* argv[])
{
  Bar source; Bar dest;
  std::string parameter;
  copy(source, dest, parameter);
}

Then it should pick foo::copy. In fact it will consider both foo::copy and std::copy but foo::copy not being template will be given priority.

Matthieu M.
The compiler will happily chose a generic function if it is a better match than a non-generic one. In your case you're okay, but not because `foo::copy` isn't a template, but because allowing `std::copy` would result in an ambiguous call. If `parameter` had been, say, a string literal, `std::copy` would have been chosen, because it can accept such a value directly, while `foo::copy` would require a conversion.
Dennis Zickefoose
Actually, by making `parameter` a string literal, `std::copy` would no longer be considered, so you're still okay. But the general point stands.
Dennis Zickefoose
Exact, I forgot to mention the point about conversions.
Matthieu M.