Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Library with backward compatible ABI that uses Boost

I'm working on a 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 a 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 from interfering with a 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, a user of libXYZ must use Boost 1.33 or recompile libXYZ with Boost 1.35 (that may be already had broken some API in a way XYZ would not compile).

Note: I'm talking about UNIX/Linux 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 interfere.

One suitable 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.

like image 700
Artyom Avatar asked May 07 '09 20:05

Artyom


2 Answers

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.

like image 155
Zifre Avatar answered Oct 31 '22 11:10

Zifre


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.

like image 5
Jared Oberhaus Avatar answered Oct 31 '22 09:10

Jared Oberhaus