Name collisions go deeper than I thought

I’ve always thought C++ namespaces were kind-of overrated.

As I’ve seen them up until recently, they are just a compile-time scoping mechanism for names to avoid collisions between two header files from different codebases containing the same symbol name (e.g. Anatomy::Butt vs. Smoking::Butt.)

Sure, should that problem occur, it’s great to be able to solve it by wrapping the #include statement of one of the offenders in a namespace, like so:

namespace Wrapping
  #include <NakedButt.h>

It’s just that it rarely happens that way.

Now, what changed my mind was when I changed the internals of a static library in our large-ish MFC application, and the final link suddenly failed due to a name collision.

What happened was I started using a class, PrivateDetail, inside a publicly accessible class, PublicUtility. PublicUtility, in turn, was used from another project which also had a PrivateDetail class.

So, when the linker tried to resolve all externals, it faced a conflict: do I use the PrivateDetail in my current project, or the PrivateDetail from the static library? I suspect this is a breach of the ODR, so I guess I was lucky my linker complained.

My first instinct was to break it out into a shared location, maybe let the outer project use the definition from the static library, but this was a generated (read-only) class, the definitions were identical and it was late in the project and in the day…

I realised wrapping one of the definitions in a namespace would fix the collision, so I put one in the static library, to make sure no other users would be affected when linking with it.

What did I learn?

Compile-time you can wrap symbols in namespaces. Namespaces can be fully spelled out, or subtly opened (using, using namespace) when you’re using a symbol. If there are any ambiguities with competing open namespaces, the compiler will complain.

At link-time, however, only the full name matters. The compiler has already figured out which namespace + symbol name that identifies the type, so if there are multiple definitions available, things will go wrong. Either the linker complains, too, or it falls back on undefined behavior and just uses some random definition, not the one you expected.

In a static library, if you use PrivateDetail from PublicUtility, linking to PublicUtility will require PrivateDetail‘s definition to be resolved.

These last two realisations opened up my eyes to the fact that namespaces matter even as far as link-time. If you’re shipping static libraries and use symbol names internally that you suspect might cause collisions, please wrap them in a good namespace.

It will save your butt, and mine, one day.