74

I am working in C++ under Mac OS X (10.8.2) and I recently came up with the need of using C++11 features, which are available through the clang++ compiler using the libc++ stdlib. However, I also need to use some legacy library compiled and linked against libstdc++ (coming from MacPorts).

In doing so, I got linking errors, since the headers of the legacy libraries using, e.g., std::string, required to be resolved against the std::__1::basic_string (i.e., the libc++ implementation of std::string) instead of the std::basic_string implementation.

Is there a way to mix the two libraries in development (e.g. by using some preprocessors flags?)

Praetorian
  • 106,671
  • 19
  • 240
  • 328
user1690715
  • 841
  • 1
  • 7
  • 5

1 Answers1

98

What you're seeing is the use of inline namespaces to achieve ABI versioning.

What that means:

The libstdc++ std::string is a different data structure than the libc++ std::string. The former is a reference counted design, whereas the latter is not. Although they are API compatible, they are not ABI compatible. That means that if you construct a std::string with libstdc++, and then pass it to other code that is linked against libc++, the receiving code would think it has a libc++ std::string. I.e. the receiver would not have a clue that it should be incrementing or decrementing reference counts.

Without inline namespaces, the result would be a run time error. The best you could hope for is a crash. With inline namespaces this run time error is translated into a link time error.

To you the programmer the libstdc++ std::string and the libc++ std::string look like the same type. But to the linker, they look like completely different types (the clue is the std::__1 namespace). And the linker's view is correct. They are completely different types.

So yes, you could manipulate some preprocessor flags to get things to link. But then you would have a devil of a time debugging the resultant run time bugs.

The only way to do what you want to is to make the interfaces between these dylibs not involve std:: types such as string. For example you could pass arrays of char instead. You can even transfer memory ownership from libstdc++-linked code to libc++-linked code and vice-versa (they will both drop through to the same malloc pool).

Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 7
    So, summarizing, there's no hope to use legacy libraries (i.e., libraries linked against libstdc++) with libc++. That's really a serious limitation – user1690715 Sep 25 '12 at 15:44
  • 13
    There is a maximum hope: You can mix the two libraries in a process as long as you don't pass versioned symbols across dylib boundaries. And if you accidentally do, the mistake is caught at link time. You can throw `std::exception`-derived exceptions across dylib boundaries and transfer memory ownership across dylib boundaries. I suspect this may be better ABI compatibility than gcc will provide with its own 4.2 version. Note that by the std spec, not even `std::string` is ABI compatible between C++98/03 and C++11. The former is widely ref-counted whereas the latter is forbidden to be. – Howard Hinnant Sep 26 '12 at 03:08
  • 4
    Another example: The pre-C++11 gcc `std::list` is not ABI compatible with the C++11 spec of `std::list`. In other words: your remark about libc++ applies equally well (if not more) to pre-C++11 versions of libstdc++. libc++ is no more ABI incompatible with pre-C++11 libstdc++ than a C++11-conforming libstdc++ will be. – Howard Hinnant Sep 26 '12 at 03:15
  • Actually, if one of the two libraries is boost something, you have little hope to get rid of `std::string` :-( – user1690715 Sep 30 '12 at 18:52
  • 3
    @user1690715 It would be more accurate to say that there's no hope for interworking libraries build against the two runtimes when they expose STL objects on their APIs - something which has always been a very dicey proposition. – marko Apr 25 '13 at 14:54
  • 3
    Wouldn't it be possible to create an assignment overload for std::string to std::_1::string and vice-versa ? – Michael Mar 28 '14 at 14:10
  • @michael Neat idea. It would be a bit compilcated. It seems you would have to be able to #include the headers from both versions of the stdlibs for that to work, and have them in the correct namespace right at compile time. – Catskul Jul 13 '16 at 23:38