views:

321

answers:

4

I am using a C++ third party library that places all of its classes in a versioned namespace, let's call it tplib_v44. They also define a generic namespace alias:

namespace tplib = tplib_v44;

If a forward-declare a member of the library in my own .h file using the generic namespace...

namespace tplib { class SomeClassInTpLib; }

... I get compiler errors on the header in the third-party library (which is being included later in my .cpp implementation file):

error C2386: 'tplib' : a symbol with this name already exists in the current scope

If I use the version-specific namespace, then everything works fine, but then ... what's the point? What's the best way to deal with this?

[EDIT] FYI for future viewers, this was the ICU library. A solution (at least in my situation) is in the comments to the accepted answer.

+2  A: 

It looks like there is an ugly workaround for this, but no good solution.

For ACE (with a decent explanation) and Xerces (with a snarky "this is how c++ works" comment), they define macros that you can use to do this "generically".

ACE_BEGIN_VERSIONED_NAMESPACE_DECL
class ACE_Reactor;
ACE_END_VERSIONED_NAMESPACE_DECL

XERCES_CPP_NAMESPACE_BEGIN
class DOMDocument;
class DOMElement;
XERCES_CPP_NAMESPACE_END

It looks like an unfortunate c++ artifact, try searching around in your tplib for these macros.

The standard treats namespaces and namespace aliases as different things. You're declaring tplib as a namespace, so when the compiler tries to assign an alias later, it cannot be both, so the compiler complains.

Stephen
This was very close to what ended up working for me. As it turned out, my library (the ICU library) has a small header (uversion.h) that--among other things--defines the namespace alias. If I include this header in my header I can then use a versioned-namespace macro they define (U_ICU_NAMESPACE) in my header file, and get away with the generic namespace in my cpp files. Thus, I'm insulated from changes to the versioned namespace yet with minimal compile-time dependencies.
Dave
A: 

Er... What you are saying appears backwards to me. Quite the opposite, what's the point of trying to declare your class as a member of tplib namespace? (Forgetting for a sec that it is not even a namespace, but rather a namespace alias, which is why you get the error.)

It is obvious, that you have some kind of version-control system built on namespaces and namespace aliases. If your class is first introduced in some specific "version" of the namespace (like 44) - that's the namespace it has to be declared in. Why are you trying to shove your class declaration "back in time", i.e. into all past versions of the namespace (like 43 and, say, 30)? Your class did not exist in the previous versions, so you are not supposed to force it there.

AndreyT
I guess my original question wasn't clear. The namespace alias is provided by the third-party developer so that I can refer to their classes as `tplib::SomeClass`. They are using the versioned namespace internally. So in their next release, the internal namespace might be `tplib_v50`. But I won't have to go through all my files and change `tplib_v44::` to `tplib_v50::` because I'm using the alias. I'm not declaring my class in the namespace; just forward-declaring a class from that library in the header file of my classes. Hope that makes sense.
Dave
+1  A: 

I think your problem is due to tplib being an alias rather than a real namespace

Since the versioning is inside a third party library you may not be able to use it, but using the versioned namespace inside a unversioned namespace (rather than aliasing it) seems to work for g++ 4.0.1 and 4.1.2 . However I have a feeling that this is not supposed to work ... and maybe there are some other issues that I'm unaware of.

//This is the versioned namespace
namespace tplib_v44
{
   int foo(){ return 1; }
}

//An unversioned namespace using the versioned one
namespace tplib
{
  using namespace tplib_v44;
}


//Since unversioned is a real namespace, not an alias you can add to it normallly.
namespace tplib
{
   class Something {};
}


int main()
{
  //Just to make sure it all works as expected
  tplib::foo();
}
Michael Anderson
Just beware that this will not raise an error if `Something` was already declared in the versioned namespace. It will silently accept and `tplib::Something` will refer to the versioned one, ignoring `tplib::Something`. Also, ADL will not work for calls with `Something` as an argument - the versioned namespace will not be looked up for candidates.
Johannes Schaub - litb
@Johannes - That sounds sensible. Can you give an example of how ADL will fail in in the using case and not direct namespace case?
Michael Anderson
A: 

EDIT: I've had it pointed out that I've missed the point of the question - please feel free to disregard!

Besides the problems highlighted in the other answers, it also worries me a bit that you're trying to add your own code to a third-party-defined namespace in the first place.

Namespaces exist to prevent conflicting symbols (classes, typdefs, enums, etc) clashing, by placing them in their own namespace, thus developing a unique fully-qualified symbol from potentially identical partially-qualified ones. Adding your own code to third party's namespace can cause issues if (for example) in a later version they decide they too want to use the same symbol (like adding their own SomeClassInTpLib) - all of a sudden, the naming conflicts namespaces are meant to prevent will rear their ugly head. This is why it's generally bad practice to add to the std namespace.

A much safer solution that avoids the issue entirely is to simply use your own namespace. Call it tplib_ex or something similar and the association will still be clear, but conflict will not be an issue and your alias-related issue will also dissapear.

Mac
He's not adding symbols to the library, he's trying to forward declare objects that already exist within the 3rd party namespace (alias).
Stephen
Ah, indeed. Somehow missed that. Thanks for the correction.
Mac