I've put together the following description of C++98/11 ABI
(in)compatibility, so people can tell which libraries need to be
recompiled. This is useful when you've bought a library that didn't
come with source code, and you're trying to figure out if you need to
buy a new version. I think this belongs on the Wiki somewhere, but I
wanted to run it by the list first to make sure it's accurate. You can
probably skip the first section, since it's just a description of how
to use the subsequent list.



This page explains how to identify when your pre-compiled library (.a
or .so) defines a symbol that has a different enough definition in
C++98 vs C++11 that it could cause runtime problems if it's linked
into a program compiled for the other version.

First, get a list of demangled symbols from your .a or .so:

$ (find . -name '*.a'|xargs nm -f posix; find . -name '*.so' | xargs
nm -f posix -D)|cut -f1 -d' '|LANG=C sort -u|c++filt|sort

(There may be better ways to get this list.)

Next, find instances in this list of the ABI changes listed below.
For example, you might find:

  std::_List_base<FooBar, std::allocator<FooBar> >::_M_clear()

Since std::_List_base::_M_clear() destroys nodes, it's affected by the
addition of the _M_size field and can't be used by C++11 code if it
was compiled for C++98, or vice versa.  If it's possible that code
outside the library would use this instance of _List_base, then you
have to recompile the library. On the other hand, if FooBar is a type
used only inside this library, then code using the library is safe.
If FooBar is defined by the library but exposed in one of the
library's headers, then the library still needs to be recompiled,
since code using it could wind up including the other version's
implementation.


=== ABI Changes ===

complex::{real,imag}(), std::{real,imag}(complex)

The non-const overloads go from returning _Tp& to _Tp.
[Since gcc-4.4]


std::list, std::_List_impl, and std::_List_base

New _M_size member, meaning any method that adds or removes nodes or
inspects the size has a new ABI.
[Since gcc-4.7]


std::operator-(reverse_iterator) and std::operator-(__normal_iterator)
may return a different type if
reverse_iterator<_IteratorL>::difference_type (respectively,
__normal_iterator<_IteratorL, _Container>::difference_type) isn't
accurate.
[Since gcc-4.4]


map::erase(iterator), multimap::erase(iterator),
set::erase(const_iterator), set::erase(const_iterator,
const_iterator), multiset::erase(const_iterator),
multiset::erase(const_iterator, const_iterator):

Return type changes from void to iterator.
[Since gcc-4.5]


_Rb_tree<T, T>::erase(const_iterator), _Rb_tree<T,
T>::erase(const_iterator, const_iterator), _Rb_tree<T,
pair>::erase(iterator):

Return type changes from void to iterator. these are instantiated from
map and set.
[Since gcc-4.5]


vector::data()'s return type changes from pointer to _Tp*

This is a no-op with most allocators, but any allocator that defines a
non-default pointer typedef will be incompatible.
[Since gcc-4.6]



Probably safe: istreambuf_iterator::reference changes from _CharT& to _CharT.

This could affect return types if they mention 'reference', but they
appear not to mention it when istreambuf_iterator is involved.
[Since gcc-4.7]


Probably safe: map::erase(iterator, iterator),
multimap::erase(iterator, iterator)

C++11 uses const_iterator, which doesn't collide.  Other versions of
gcc are unlikely to have defined this overload in C++98 mode, and
C++11 is unlikely to have defined the iterator version.

[Since gcc-4.5]


Probably safe: Types with node allocators, like deque and tree

C++11 uses _M_get_Node_allocator().construct(node, ...), while C++98
uses get_allocator().construct(node->_M_value_field, ...).  The node's
constructor forwards to the value_field's constructor, so this works
by default.  Can this cause problems with some mix of C++98/C++11
allocator compilations?




I haven't analyzed the debug and profile headers.




I found these differences by grepping for GXX_EXPERIMENTAL_CXX0X
inside libstdc++, and examining each instance to see if there was an
#else clause that had different behavior in C++11 vs C++98.


=== ABI non-changes ===

libstdc++'s binary component is nearly ABI-compatible between C++98
and C++11.  Most incompatibilities are in the templates defined in
headers, but the complex<> stream operators call the real() and imag()
methods that change return type.  If these calls aren't inlined, (and
they're likely to be inlined), then libstdc++ (which is compiled in
C++98 mode by default) could cause problems when linked into C++11
programs.  (http://gcc.gnu.org/PR53429)

There have been some claims that the change in the definition of POD
types causes an ABI incompatibility, but apparently it doesn't in
practice: http://gcc.gnu.org/ml/gcc/2012-01/msg00056.html.

Reply via email to