3

I am working with a library. The library has one file containing the interface that I actually want exposed to other programs, contained in foo.h and foo.cpp. It also contains a bunch of helper classes and utility functions, in files bar1.h, bar2.h, bar1.cpp, bar2.cpp, etc.

If I compile all of these files and stick them in a .lib, the problem I run into is that some of the symbols in the bar files have very common names that clash with those in other external libraries I need to link against.

If all of the code were in one single .cpp file, I know how to fix this: I can use static or namespace { } to stop the linker from exporting the internal symbols. But obviously I have to declare the stuff in bar extern if I want to access it in foo.

I can wrap all of the .cpp files in namespace baz { }. If I choose baz carefully, so that there is little chance of it conflicting with namespaces used in other libraries, that will substantially fix the problem. But ideally, nothing outside of the symbols in foo.h should get exported into my .lib. Is there a technique for doing this?

user168715
  • 5,469
  • 1
  • 31
  • 42
  • 1
    It's usually something that you specify to the linker. Which toolchain are you using? – slaphappy Jun 04 '13 at 15:59
  • I originally tried to post this question, and it said it "did not meet quality standards." (I got it to work eventually with some tweaking of the text.) It's a four-paragraph question!! WTF? – user168715 Jun 04 '13 at 15:59
  • @kbok I'm using the library on multiple platforms, so at the moment, both gcc and msvc. So ideally I'd like a portable solution. – user168715 Jun 04 '13 at 16:00
  • You may want to look at http://stackoverflow.com/questions/3276474/symbol-hiding-in-static-libraries-built-with-xcode-gcc and http://stackoverflow.com/questions/12717991/symbol-visibility-in-windows – slaphappy Jun 04 '13 at 16:02
  • @kbok Ah. I was hoping something could be done at the language level. Thanks! – user168715 Jun 04 '13 at 16:03
  • 1
    Unfortunately, the "linker" part of C++ is not standardized, so you have a specific workflow for each compiler. – slaphappy Jun 04 '13 at 16:04
  • The method works on the *source* level for both Windows and Linux, with just a bit of tweaking on the makefile level. You may just as well consider it a part of the language. – n. m. could be an AI Jun 04 '13 at 17:31

1 Answers1

3

You can achieve this, however it comes at some cost:

in C++ you can have internal linkage. Anything inside a unnamed namespace has internal linkage* (see footnote), as well as static free functions (you should prefer the anonymous namespace).

Update: here's the C++11 standard quote from §3.5,4:

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
— a variable; or
— a function; or
— a named class (Clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3); or
— a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes (7.1.3); or
— an enumerator belonging to an enumeration with linkage; or
— a template.

However, internal linkage applies to translation units, not to static libraries. So if you would use the usual approach putting each class in its own translation unit (=cpp), you could not define them inside anonymous namespaces because you could not link them together to build the library.
You can solve this dilemma by making the whole library one single translation unit: one header providing the library's public interface, one source with the function definitions, and anything else as headers, defined in anonymous namespaces:

mylib.hpp

class MyLib {
public:
 int foo();
 double bar(int i);
};

mylib.cpp

#include "mylib.hpp"
#include "mylibimpl.h"

int MyLib::foo() {
  return fooimpl();
}

double MyLib::bar(int i) {
  return BarImpl(i).do();
}

mylibimpl.h

namespace {
  inline int fooimpl() { return 42; }

  class BarImpl {
    double d;
  public:

    BarImpl(int i) : d(i*3.42) {}
    double do() { return 2*d; }
  };
}

You'll now have one translation unit (mylib.o / mylib.lib), and all the *impl classes and functions cannot be seen from outside, because they have internal linkage.

The cost is that you have to reorganize the sources of your internal classes (e.g. to resolve circular dependencies) and that every simple change of the library's internal code will lead to one big recompilation of everything in the lib, just because there is only the single huge translation unit. So you should do this only when the library code itself is very stable or if the library is not too big. The benefit besides the complete hiding of internal symbols is that the compiler will be able to pull out any optimization it wants, because no implementation details are hidden in different translation units.

*Footnote: As was commented by Billy ONeal, in C++03 entities in anonymous namespaces have not necessarily internal linkage. However, if they have external linkage, they have names unique to their tranlsation unit and are effectively not accessible from outside that TU, meaning that this procedure works in C++03 as well.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • 1
    Actually, things in an unnamed namespace still have external linkage. They just get generated with a name that isn't supposed to conflict with other things. That's why you still see static used in C++. – Billy ONeal Jun 04 '13 at 18:40
  • @BillyONeal I updated the answer with the according standard quote - unnamed namespaces have internal linkage, and functions, classes etc. have the same linkage as the surrounding namespace. Put together, "things" in an unnamed namespace have internal linkage. – Arne Mertz Jun 04 '13 at 18:47
  • Depends on the standard version I guess. In C++03 they had external linkage; (there is no such note at 3.5/4) 7.3.1.2 [namespace.memdef]/1 has a note that says "Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit." C++11 agrees with your quote above. – Billy ONeal Jun 04 '13 at 19:06
  • @BillyONeal Thanks for the clarification. I was not aware of that change in the last standard. – Arne Mertz Jun 05 '13 at 07:17