views:

668

answers:

4

I'm working on certain C++ library (or more framework). I want to make it backward compatible with previous versions preserving not only API compatibility but also ABI (like the great job Qt does).

I use lots of functionality of Boost and it seems for me that this makes backward compatibility just impossible, unless I force user to have exactly the same (sometimes old) version of Boost.

Is there any way (without rewriting 1/2 of Boost) to make some "prefix" around its namespace/rename it in order to prevent it interferring with user version of Boost?

For example my libXYZ uses Boost 1.33 and it has class boost::foo. In version 1.35 boost::foo was upgraded and new member was added, so, boost::foo from 1.33 and 1.35 are not ABI compatible. So, user of libXYZ must use Boost 1.33 or recompile libXYZ with Boost 1.35 (that may be already had broked some API in way XYZ would not compile).

Note: I'm talking about UNIX/Liux OS with ELF where dynamic linking is similar to static linking, so you can't link with two different versions of libraries because symbols would interfer.

One, sutiable solution I may think of is putting Boost in some other private namespace. So, libXYZ would use ::XYZ::boost::foo instead of ::boost::foo. This would prevent collision with other version of Boost that user may use.

So, the libXYZ would continue to work with Boost 1.33 statically or dynamically linked with it withing other namespace, assuming, that it:

  • Would not expose Boost API outside.
  • Would keep stable private version of exposed API.

Is there any way to do such things with Boost?

Edit: Finally I decided to create a script that would rename all boost symbols in the source to some custom symbol.

Rationale: simplification of build process, independent of compiler visibility support, also, it visibility works on dynamic libraries only, for static this does not work, so I need separate builds and dependencies for each type of libraries.

The script is available there: http://art-blog.no-ip.info/files/rename.py

Edit 2: Latest version of Boost BCP supports namespace renaming.

+8  A: 

Basically, just make sure the public interface to your library does not expose Boost. You can always use it however much you want internally. Generally, having the interface of a library depend on another library is bad (unless it depends on a standard library like STL). Boost almost fits into the "standard" library category, but its ABI changes so much that that your interface shouldn't use it.

To make sure you don't expose the Boost symbols, there are a few things you could do:

A. compile with -fvisibility=hidden and mark all public symbols with __attribute__((visibility("default"))). you could use a macro to make this easier:

#define ABI __attribute__((visibility("default")))

B. do something like this:

#pragma GCC visibility push(hidden)
#include <boost/whatever.hpp>
#pragma GCC visibility pop

You should also wrap this around all other internal symbols that you don't want exported, or declare this with __attribute__((visibility("hidden"))). Again, you could use a macro to make this easier:

#define INTERNAL __attribute__((visibility("hidden")))

Of these options, I like A better, because it makes you explicitly think about which symbols are exported, so you don't accidentally export things you don't want.

By the way, you can find a lot more information about making DSOs in Ulrich Drepper's How to Write Shared Libraries.

Zifre
This is not enough, even if I do not expose Boost API, because user may use different version of Boost that would interfer with internal symbols used in this library.
Artyom
You can fix that with the linker.
Zifre
How can I do this? How do I filter symbols I want to expose and thouse I don't? Reminding, there is not __dllexport like options.
Artyom
I added some more information about how to do that.
Zifre
One Last Question: Is there any way to "hide" dynamically lined library from others? Meaning something like g++ -shared ... -o libfoo.so -Wl,hide-these -lboost_regex -lboost_XYZ -Wl,stop-hiding? I just can't find. Or I have to recompile boost_regex and boost_xyz with -fvisibility=hidden?
Artyom
@Artyom: It's not a good idea to have a DSO dynamically link with another DSO. You can't just recompile the libraries with -fvisibility=hidden, because then your library wouldn't be able to access it. You should probably link in other libraries statically with -fvisibility=hidden.
Zifre
Thanks a lot! You had really helped me!
Artyom
All good advice, although personally I'd be wary of exposing even STL in interfaces (having had to supply libraries to customers using an unusual STL implementation). And don't let me get started on compatability issues in windows world with MS's "iterator debugging" version of STL...
timday
@timday "And don't let me get started on compatability issues in windows world with MS's "iterator debugging" version of STL", Fortunately I do not develop for Windows and Windows compilers.
Artyom
Finally I decided on Boost renaming script... Too much troubles and correct build issues.
Artyom
A: 

You should be able to do something like this:

namespace XYZ
{
#include <boost/my_library.hpp>
}

And it should dump the boost headers into namespace XYZ. Note, however, that this will only work with the header-only libraries.

rlbond
This would not work because, if my_library uses, for example std::string and includes <string> it would be putten inside XYZ namespace wich is not correct.
Artyom
namespace just surrounds the include of boost
Gaetano Mendola
+1  A: 

In general you can't rely on any type of ABI in C++ beyond standard C bindings. But Depending on how many assumptions you make, you can use more and more of C++ in your interface.

I found this great article on steps to make your API's turn into a stable ABI. For instance, never pass Standard C++ Library (or Boost) datatypes across your interface; it might break even with a small bug fix to the library.

Some examples of the issues to watch out for when publishing an ABI compatible API are:

  • Windows Debug heap. You have to be sure that all allocations and deallocations are on the same side of a "module" (that is, executable or DLL).
  • Fragile Binary Interface problem. Even if both sides of your system consistently use the same compiler and libraries, you have to be careful in C++ about what you publish in your .h files, and where allocations happen.

If you follow the linked article, you will find solutions for these and other issues.

Edit:

I also found an interesting article published by Microsoft which describes how COM interfaces work, by taking a C++ project and turning it into COM. It's my belief that one of the primary reasons that Microsoft developed COM was to solve the Fragile Binary Interface problem that C++ has, so they can ship DLLs with publish object oriented APIs.

Jared Oberhaus
A: 

To avoid unexpected ABI breaks may be used tools for static comparison of old library code with a new one, such as free ABI-compliance-checker from http://ispras.linuxfoundation.org/index.php/ABI_compliance_checker