0

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)

  • I think you're going to need to make your question more specific. #ifndef #define is traditionally used to make sure symbols are only defined once, and thrust doesn't appear to use macros to define its namespaces. It's unclear to me what benefits your sample code provides. – Robert Harvey Jun 4 '18 at 20:10
  • @RobertHarvey Nvidia has not maintained parity with the public Thrust repo for some time, they will soon do so. The tuple implementation I was talking about was this one, the candidate for variadic tuple replacement github.com/thrust/thrust/issues/524. Also not sure why you only looked at thrust and not Hydra... – opa Jun 4 '18 at 20:20
  • Emptying the LIB_NS namespace might heavily change the semantics to anonymous namespaces. Be aware of that. – πάντα ῥεῖ Jun 4 '18 at 20:21
  • 1
    @RobertHarvey Wow, that's totally different than " It appears the reason for this is so that you can configure the namespace to your liking"! – opa Jun 4 '18 at 20:50
  • 1
    @snb: "I'm asking why I wouldn't want to do this for nearly every single library" I'd be more curious as to why you would want to. The only possible benefit I can see would be if there were two libraries that wanted to use the same namespace. – Nicol Bolas Jun 4 '18 at 23:30
4

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.