Creating Library with Backward Compatible Abi That Uses Boost

How to Compile boost with GCC 5 using old ABI?

To my knowledge, there are no prebuilt Boost packages for the old ABI in the official Kubuntu repositories, so you will have to build Boost yourself. The building process is documented here.

Make sure you're building the same Boost version that was used when your library was built. If there were any Boost configuration macros defined, you will also have to define them the similar way. Otherwise you may encounter ABI incompatibilities between the library and Boost you've built.

In order to switch libstdc++ to the old ABI you will also have to define _GLIBCXX_USE_CXX11_ABI to 0, as described here. For example:

b2 -j8 variant=release define=_GLIBCXX_USE_CXX11_ABI=0 stage

You will also need to define the macro when you build your own code that uses Boost and the library.

The define property, along with many others, is documented here.

C Interface as an API with Boost backend

If you're going to try to define your own delegates to wrap C++ member functions, free functions etc., I suggest you take a look at "5 years later, is there something better than the “Fastest Possible C++ Delegates”?. The discussion is quite nice and the accepted answer proposes a neat trick to reduce all callbacks to a pair of void* and some free function R(void*,...) that invokes a member function or free function and whatnot.

I've used a similar technique to implement wrappers for Win32 API callbacks. Here's an example (take a look at the Timer::function<> and Timer::method<> inner types). Using this technique, you can expose a C interface like:

/* type of callback functions passed through the C interface.
Obviously, C++ templates won't be of any help here, you need
to typedef all the function signatures. */
typedef void(*Callback)(void*,int, int);

/* Example function that uses one of the callbacks. */
extern "C" void* createTestClass(Callback,void*);

// Enhanced interface for passing object methods bound to
// a specific instance instead of the basic C-style callback.
#ifdef __cplusplus
template<typename T, void(T::M)(int,int)>
inline
void * createTestClass2(T& object,method<T,M> method)
{
// '&object' converts to 'void*' and 'method' object
// converts to 'void(void*,int,int)' function pointer.
return createTestClass(&object, method);
}
#endif

The role of the proposed mechanism is just generation of the void(void*,int,int) functions that wrap member function calls so that they have the right signature. Everything else is just plain C function pointers and can be freely passed to your C API while preserving the ABI.

Just be careful exceptions don't leak from your C++ callback functions. See "Passing C++ exceptions across a C API boundary" for a more detailed discussion of the topic.


Update (working example)

Here's a full working example (tested with Visual Studio 2008).

library.h (public interface for C or C++ clients with stable ABI)

#ifndef _library_h_
#define _library_h_

#ifdef __cplusplus
extern "C" {
#endif

typedef int(*Callback)(void*,int,int);

void* createTestClass(Callback,void*);

#ifdef __cplusplus
}
#endif

#endif /* _library_h_ */

library.c (library implementation)

This demonstrates a C implementation, but you could have a C++ file instead and use std::function<>, boost::function<> or other to use the callback.

#include "library.h"

void* createTestClass(Callback callback, void* context)
{
callback(context, 1, 2);
return 0;
}

library.hpp (enhanced interface for C++ clients, builds on public ABI)

This is the enhanced interface for C++ clients. It adds support for using member functions instead of plain int(void*,int,int) free functions. If you only plan to support C++ clients, then this can be in the same file as your C-style interface.

#ifndef _library_hpp_
#define _library_hpp_

#include "library.h"

template<typename T, int(T::*M)(int,int)>
class MethodCallback
{
static int wrapper(void* context, int x, int y)
{
return (static_cast<T*>(context)->*M)(x, y);
}
public:
operator Callback () const
{
return &wrapper;
}
};

template<typename T, int(T::*M)(int,int)>
void* createTestClass(T& object, MethodCallback<T,M> method)
{
return createTestClass(static_cast<Callback>(method), static_cast<void*>(&object));
}

#endif // _library_hpp_

main.cpp (test program)

Here's a simple demo on using the interface:

#include "library.hpp"
#include <iostream>

namespace {

class Foo
{
public:
int bar(int x, int y)
{
std::cout
<< "Foo::bar(" << x << "," << y << ")"
<< std::endl;
return 1;
}
};

}

int main(int, char**)
{
Foo foo;
void* instance = ::createTestClass
(foo, MethodCallback<Foo, &Foo::bar>());
// ...
}

You should expect this program to output Foo::bar(1, 2) since the registered callback function will invoke foo.bar(1,2).

How to handle changes in class structure and be backward compatible with data/objects serialized using boost?

Boost's serialization supports versions. See the following links: Link #1, Link #2 and an example.

Static analysis tool to detect ABI breaks in C++

I assume that you are familiar with this tutorial: Binary Compatibility Issues with C++, if not read it!

I've heard about this tool:
http://ispras.linuxbase.org/index.php/ABI_compliance_checker, however never tested or used one, so have no opinion.

Also this may interest you: Creating Library with backward compatible ABI that uses Boost

Using multiple versions of boost

You could split your build process: first compile the MongDB C++ driver into a separate .lib while linking against Boost 1.42.0 Then in a second step you compile your own application, linking against the MongoDB library and Boost 1.48.0. This should work as long as the MongDB .lib does not expose any of the boost functions such as boost::system::throws. See this question for how to limit the public interface from exposing Boost symbols.

Alternatively, try compiling your entire application with only a single Boost version (either 1.42 or 1.48).

will static compilation break the ABI?

If you compile statically with boost you won't have any ABI problems with future versions, since nothing is called "externally", you also won't be using anything from future versions without recompilation obviously.

I've statically linked with boost for years, mainly for easier deployment, without any problems.



Related Topics



Leave a reply



Submit