Question

In some code bases (such as hydra, and thrust's tuple implementation) I see namespaces defined entirely with macros. It appears the reason for this is so that you can configure the namespace to your liking. Is this the only use case for this practice? Even so why shouldn't all libraries define a macro for the namespace? Would it not be better to just do the following:

#ifndef LIB_NS
#define LIB_NS default_namespace
#endif

namespace LIB_NS{
...
}

LIB_NS::foo(...);

for every library I make? ifndef is used so that I could define the name of the symbol as a compiler option or somewhere outside of the include before hand.

Here are the examples:

Thrust example

Hydra repository (can't find where they define it, but its used a lot)

Was it helpful?

Solution

This technique is sometimes used to handle transitive dependencies on header only libraries. It is not generally a best practice.

The problem: I am writing a C++ library. I put all of my declarations into a namespace foo { ... }. So users of my library can avoid that namespace. But what happens when I want to use another library in my library? Let's call the second library bar. If I use the bar library normally, I am exposing both the foo and bar namespaces to the users. My dependency is not invisible, and could break code of my users.

If the bar library makes its namespace configurable, I can avoid that by moving it into a foo::detail::bar namespace.

At first, it might seem like I could simply include a header within a namespace:

namespace foo { namespace detail {
#include <bar.h>
} }

However, headers are generally written to assume that they are included at the top-level. In particular, expect this to break the standard library as used through that header. This will also fail if bar is not a header-only library, because the object code will be compiled into a different namespace.

Using macros is the preferable solution. A single namespace macro that takes values such as foo::detail is insufficient prior to C++17, so that separate macros for declaration start/end and the actual namespace are necessary. E.g. see this example in Hydra.

However, these techniques suffer from various problems and restrictions.

  • The header files typically still contain include guards, so that each compilation unit can import a library under at most one namespace. In practice, this is not a big restriction.

  • If a library is imported under multiple namespaces, any objects declared by that library are duplicated. This may or may not be desired.

  • If the library with variable namespaces is not a header-only library, then the object code for that library must also be compiled into the correct namespace. This requires you to define the necessary macros in your build system.

The core problem that these macros are trying to solve is that C++ does not have a proper module system. How are other languages addressing potentially clashing namespaces?

  • Java has a package naming convention based on domain names, so no name clashes between organizations should arise. E.g. If you hold example.com, then you should put the foo library into the com.example.foo namespace.

  • Rust and Perl have one central library index (Crates and CPAN, respectively). Just don't use any namespaces that could be reserved there. Additionally, Rust does not make the namespace of a crate visible unless you import it, whereas the C++ one-definition-rule is global.

  • Python has relative imports. A well-written module does not need to know its absolute location.

Hopefully, C++ modules will adopt a reasonable solution when they finally arrive.

The tuple example you linked to is fundamentally different: the purpose of that library is to patch the standard library if it doesn't provide a tuple of its own. Here, the namespace configurability is only provided to optionally place the tuple into a different namespace.

To summarize: configurable namespaces have legitimate uses when building libraries for libraries. Most C++ code should not bother with this, especially not application code.

Licensed under: CC-BY-SA with attribution
scroll top