JAABIBP (Just Another ABI Blog Post)
ABI breakage is a hot topic. Let’s look at some ways C++ handles it, and how that compares to the WG14 _Alias
proposal.
NOTE: This is probably the least educated article about ABI breakage yet. You should really watch Jason Turner’s youtube video or read JeanHeyd Meneide’s blog posts🙃
These views do not in any way represent those of NVIDIA or any other organization or institution that I am professionally associated with. These views are entirely my own.After reading about transparent aliasing in this blog post from JeanHeyd Meneide, I had to play around with it in Godbolt and rave about its coolness on the Cursed Bird Site.
Sean Parent rightly pointed out that this super neat proposal from JeanHeyd acomplishes pretty much the same thing as inline namespace
in C++.
Isn't this what inline namespaces are for in C++? (prior, they were \_\_strong namespaces). I'm surprised these aren't even mentioned in the proposal. Maybe an abbreviated form of inline namespaces for C could be used. ;-)
— Sean Parent (@SeanParent) March 14, 2022
Let’s talk about that.
Problem Formulation
Let’s say you build executable A which dynamically links against library B.
Library B might look like this:
// B.hpp
namespace B {
int answer();
}
// B.cpp
#include <B.hpp>
int B::answer() {
return 42;
}
while executable A might look like this:
#include <B.hpp>
#include <iostream>
int main() {
std::cout << B::answer() << "\n";
return 0;
}
Some time has passed since library B was released, and now the authors have decided that answer
can be 30% faster if it uses long double
instead of int
s.
How cool!
// B.hpp
namespace B {
long double answer();
}
// B.cpp
#include <B.hpp>
long double B::answer() {
return 42;
}
Wow, so fast! 🚀
If you rebuild B without rebuilding A however, A will be expecting the answer
’s return value to be an int (4 bytes on my system) even though B::answer
now returns a long double
(8 bytes on my system).
When dynamically linking to the original B library, A unsurprisingly prints 42
.
When dynamically linking to the updated B library however, A prints the following:
$ ./A
83575344
$ # 😨 uh oh...
This disagreement between the program and the library at the binary level wreaks all sorts of havoc.
This section of JeanHeyd’s post gives a much better illustration.
Comparing inline namespace
with _Alias
and co
Why do some want to break it?
Q: Why don’t you just rebuild after an ABI change? A1: Are you rebuilding the standard library too? Many people will recommend not passing standard library types around, and not throwing exceptions across shared library boundaries. They often forget that at least one very commonly used shared library does exactly that… your C++ standard library.
On many platforms, there is usually a system C++ standard library. If you want to use that, then you need to deal with standard library types and exceptions going across shared library boundaries. If OS version N+1 breaks ABI in the system C++ standard library, the program you shipped and tested with for OS version N will not work on the upgraded OS until you rebuild.