worenga
worenga

Reputation: 5856

C++ header-only library avoid "using namespace" pollution

I have a header-only C++ library with several namespaces.

For example one header file may contain

//header1.h
namespace library{
namespace componentA{
   template<typename T>
   class Someclass{};
}
}

And another one

//header2.h
namespace library{
namespace componentB{
   template<typename T>
   class SomeOtherClass{
       void Foo(const componentA::Someclass<T>& reference);
       void Bar(const componentA::Someclass<T>& reference);
   };
}
}

Now while this works, having a header-only library it becomes tedious to write the namespace again and again, especially when you are having multiple classes and nested namespaces involved.

So I did this:

//new header2.h
namespace library{
namespace componentB{

   using namespace componentA;

   template<typename T>
   class SomeOtherClass{
       void Foo(const Someclass<T>& reference);
       void Bar(const Someclass<T>& reference);
       void FooBar(const Someclass<T>& reference);
       void FooWithBar(const Someclass<T>& reference);
   };
}
}

While this is certainly more convenient to type, it has the problem that now a client of the library can also use Someclass<T> by using the componentB namespace like this, which leads to an ambiguous interface and ultimately to inconsistent code. For example a client now could use componentB::Someclass<T> even though it is originally defined in componentA

Is there a way to get the shorthand only available "privately"?

Upvotes: 2

Views: 1137

Answers (4)

worenga
worenga

Reputation: 5856

I figured Out the solution myself after a while of thinking. Unfortunately this is not straightforward.

//header1.h
namespace lib_noexport{

namespace library{
namespace componentA{
   template<typename T>
   class Someclass{};
}
}
}
}

using namespace lib_noexport;

And then

//header2.h
namespace lib_noexport{

using library::componentA::Someclass;

namespace library{

    namespace componentB{

       template<typename T>
       class SomeOtherClass{
           void Foo(const Someclass<T>& reference){}
       };
    }
}

}
using namespace lib_noexport;

Now this yields the following:

library::componentB::SomeOtherClass<int> a; //works as expected
Someclass<int> b; //error
library::componentB::Someclass<int> c; //error
library::componentA::Someclass<int> b; //works

Still, the user could be stupid enough to use the undocumented lib_noexport:: but then i cannot help her any more..

Upvotes: 1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275896

namespace library {
  namespace componentAImpl{
    using ::library::componentB;
    // ...
    inline namespace exports{
      struct foo{};
    }
  }
  namespace componentA{
    using namespace library::componentAImpl::exports;
  }
}

users can access componentAImpl, but should not.

Meanwhile, a clean set of symbols are exposed in library::componentA.

Upvotes: 6

Alexander V
Alexander V

Reputation: 8718

Well, C++ 11? Using-declaration introduces a member of another namespace into current namespace or block scope.

namespace library{

    namespace componentB{

       using componentA::SomeOtherClass;

       template<typename T>
       SomeOtherClass{
           void Foo(const Someclass<T>& reference);
           void Bar(const Someclass<T>& reference);
           void FooBar(const Someclass<T>& reference);
           void FooWithBar(const Someclass<T>& reference);
       };
    }

}

Upvotes: 1

Andy Prowl
Andy Prowl

Reputation: 126552

If I understand your question correctly, then the answer is no. If typing/reading componentA:: before type names from the componentA namespace is too much of a problem (honestly, that would be my preference), then you could use short namespace aliases:

namespace library { namespace componentB
{
// ...
namespace ca = componentA;
// ...
void foo(ca::SomeClass<T>);
// ...
} }

In any case, I discourage using directives in header files: they lead to name clashes and maintenance issues.

Upvotes: 3

Related Questions