CppCast - ABI Stability
Episode Date: May 21, 2021Rob and Jason are joined by Marshall Clow. They first discuss some bugs Microsoft found using ASAN in open source projects, and new libraries. Then they talk to Marshall Clow, longtime maintainer of l...ibc++, on his perspective on the C++ ABI, and why stability is important. News Finding Bugs with AddressSanitizer: Patterns from Open Source Projects RmlUI 4.0 Release Not Enough Standards, my C++17/20 library for cross-platform utilities Meeting C++ 2021 Announced Links What is an ABI, and Why is Breaking it Bad? - Marshall Clow - CppCon 2020 Sponsors PVS-Studio. Write #cppcast in the message field on the download page and get one month license Date Processing Attracts Bugs or 77 Defects in Qt 6 COVID-19 Research and Uninitialized Variables
Transcript
Discussion (0)
Episode 300 of CppCast with guest Marshall Clough, recorded May 18th, 2021.
Sponsor of this episode of CppCast is the PVS Studio team.
The team promotes regular usage of static code analysis and the PVS Studio Static Analysis Tool. In this episode, we discuss some bugs found with ASAN and new libraries.
Then we talk to Marshall Clow.
Marshall talks to us about the C++ ABI and why stability is important. Welcome to episode 300 of CppCast, the first podcast for C++ developers by C++ developers.
I'm your host, Rob Bervink, joined by my co-host, Jason Turner.
Jason, how are you doing today?
I'm all right, Rob. How are you doing?
Doing okay.
I guess maybe we should make a little note to the listeners
that we're recording two episodes today,
and we actually recorded next week's episode two hours ago.
So we need to try to not refer to it.
Otherwise, listeners will be very confused.
I wasn't going to refer to it.
You just did, though, by bringing it up.
I know. I guess so.
Well, next week, listeners can look forward to hearing about SPAC, which is a new package, or not new,
but a package manager they probably haven't heard about yet. New to you. Yeah, new to them.
New to many. Okay, well, at the top of
every episode I threw a piece of feedback. We got this email
from Ryan saying, hi,
Rob and Jason, if you want to host a guest who promotes ABI
compatibility, you should invite Linus Torvalds. He probably doesn't care about it in the C++
context, but he's very passionate about ABI compatibility as a concept. And we do not
have Linus on today, but we do have another guest who cares a lot about ABI compatibility.
I might be interested in having Linus on sometime, maybe.
What do you think?
No, I don't know.
I mean, he's famously antagonistic towards C++.
That's true.
It's also been, you know, like 20 years or whatever since he made his worst comments about C++.
Maybe his mind has opened
a little bit since then, I don't know
maybe, something to think about
well, we'd love to
hear your thoughts about the show, you can always reach out
to us on Facebook, Twitter
or email us at feedback at cpcast.com
and don't forget to leave us a review
on iTunes or subscribe on YouTube
joining us today is Marshall
Clow, Marshall is a long-time
LLVM and Boost participant. For many years, he was the code owner for LibC++, the LLVM standard
library implementation. Until last year, he was also the chairman of the library working group
of the C++ Standards Committee. He is the author of the Boost Algorithm Library and maintains
several other Boost libraries. Marshall, welcome back to the show. Thank you very much.
Glad to be back.
And I'm honored to be chosen for a big round number episode.
300.
Wow.
People have been asking us for like 30 weeks.
What's your plan for 300?
And we didn't have a plan, but we developed a plan.
300 is impressive.
I mean, once a week,
that's six years almost.
Yeah.
Well,
and we missed a few weeks,
so it is six years.
Okay.
All over the past two years,
it's been,
we've been much more consistent.
Yes.
But in the first,
what we had missed one out of every six or something like that,
maybe back in the day.
Yeah.
Yeah.
That's a,
that's a lot of work.
I'm impressed.
Thank you.
Yeah. I am curious, uh, that's a lot of work. I'm impressed. Thank you. Yeah.
I am curious since you said that for a long time you were code owner of Lib C++. I'm just curious,
when did you get involved with Lib C++ ish?
Um, 2012 ish. Yeah.
About 12.
I started contributing to Lib C++ about that timeframe.
And the principal author of Lib C++ at the time was Howard Hinnant, who was working at Apple.
Ah, okay.
And Howard has been doing C++ standard library work for a long time.
He, too, was a standard library chair.
He was a standard library chair when C++11 was shipped.
And he's the author of Move Semantics in C++.
Right, right.
And so I started working with him on libc++.
And then he left Apple and went to work for Ripple and didn't have time to do libc++ anymore.
And so I stepped up and basically led that project for five, six years.
So in that, I'm sorry, go ahead.
Oh, and, you know, Howard is still around. He doesn't contribute much anymore. But he
answers questions about why is this thing done this way? And then I kind of burned out
towards the end of the C++ 20 death march. It was like, you you know doing doing committee work to get c++ 20 out the door and
working on libc++ and uh i had some turmoil in my work life and it was just like i need to
need to shed some of these responsibilities it's understandable is there um was when you joined it
was libc++ fully functional or did you ever find yourself going oh shoot i don't have that you know whatever feature and we're still like in the bootstrapping kind of phase we were
not in that bootstrapping kind of phase i mean there were bits and pieces that were not implemented
and obviously for c++ 14 and 17 and 20 there was a lot of functionality added um but no it wasn't
very much like there there were large chunks missing.
Howard did a really nice job.
It helps that this was, I believe his third standard library implementation.
Um, I don't know where the first one ended up.
Okay.
I haven't even implemented one yet.
But he had, uh, he wrote the one for Metro works while he was an employee there.
And that was back in the nineties.
So he's done this more than once.
Wow.
All right.
Well, Marshall, we got a couple news articles to discuss.
Feel free to comment on any of these,
and we'll start talking more about the C++ ABI, okay?
Okay.
All right.
So the first one is a blog post on the Visual C++ blog,
and this is Finding B bugs with ASAN patterns
from open source projects.
And we've talked a lot about how Microsoft now has ASAN
as a built-in analysis tool in Visual C++,
which is really great that they were able
to bring this in from Clang.
And they just ran it against a couple open source libraries and uh
found a few bugs and were able to uh you know show those to library owners and i think all
these have been fixed that's what it looked like um i'm really happy that microsoft has this
in uh in libc plus plus that's a great thing or not excuse me in visual studio
um i'm i'm a little amused that they're talking about it as if it's new in 2021.
Yeah.
I went back and researched, found an old blog post I wrote,
basically running the libc++ test suite under ASAN to find bugs in ASAN.
libc++.
That was March 2013.
This article, yeah, made me a little sad because these bugs that
they're finding in open source projects i'm like wait a minute are you telling me that open ssl
does not currently run their full test suite with an address sanitizer enabled that's like
world ends kind of problem if if open ssl has uh an unknown security flaw or known by a small group of people security
flaw. You know, an out of bounds read or write. Yes. Yeah. Now, to be fair, that bug that they
found, if I read that correctly, was a bug in the test suite specifically. Okay. But still,
they should have, it should have been caught sooner. Yeah. And when I ran ASAN for the first time against Lib C++, yeah, I found a couple of bugs in the Lib C++ test suite.
Right.
I will mail you the link to that blog post.
Or you can search the LLVM blog archives for like March 2013, and you'll find it.
And it's like there was one real bug in libc++ that it found.
And it had to do with something deep in IO streams,
where when you're starting up a stream,
well, it would allocate a zero-pipe buffer on the heap
and then write one byte to it and then resize it.
Nobody ever saw any problems with this because, you know,
on macOS,
heap allocations always get rounded up
to a multiple of 16 bytes.
Even a zero byte.
Even a zero byte.
And so it never did any harm,
but it was wrong.
On macOS, it would never do any harm.
You would never see any ill effects from it.
But it was still wrong.
And if you took it to some other operating system
with another allocator that did not have that behavior, yeah, you could get bugs. You could get
incorrect behaviors. And so I was quite impressed when I did this because it was like, whoa, yeah,
I had never found that bug. That's a fascinating one, too, because the first time you run a tool
against your project, you're both thinking, oh, well, my code's perfect. Surely it won't find anything.
But on the other hand, you kind of hope that it finds something because otherwise,
if you don't already know the tool and don't already know that you can trust it,
then you're like, well, did it actually work? If it didn't find anything, did it run on my code?
Yeah. Well, the good news is, is that at the time the LibC++ test suite took about 20 minutes to run.
And when I enabled ASAN, it took 90 minutes to run.
So clearly it was doing something.
Right.
Okay. Go ahead.
But yeah, I'm really happy that Microsoft has got this technology in their compiler now.
And even happier now that people who exclusively use the Microsoft compiler
can turn this on.
And I wouldn't encourage everybody to do it
because the way ASAN works,
it pretty much,
the idea of something being a false positive
is just not in its vocabulary.
Every time ASAN goes off,
there's a bug there.
Awesome.
Yeah.
All right.
The next thing we have is,
this is actually a update of RML UI, but I don't think
we've talked about RML UI before, but it's a HTML CSS UI library for C++, which is pretty neat.
I just spent like five minutes staring at this to even understand. I'm like, wait a minute.
Why am I writing HTML withml with c++ what is
this is this related to inscripted no no it's it's their own ui kit and they have a couple like
little examples here on the github page so it's like it's mostly for for video games if you want
to have like a menu page with your game this is maybe a really easy way to put that together. Or if you just like HTML and CSS, you can use it as your interface description language.
Or that's a bad, it's not an IDL, it's a UML.
No, that's a bad word too.
What is it?
What's the word am I looking for?
It's one of these...
Markup language.
It's a markup language for interfaces.
Yes. it's a markup language for interfaces yes yeah it looks like it's really easy to do
data binding between your uh your you know html code and c++ so that seems like it's
pretty powerful and sprite sheets it looks pretty it's it's pretty crazy yeah um i've never used
anything like this so i don't know how it compares to anything else. But the data binding looks pretty slick.
Yeah.
Although, you know, model view controller kind of stuff is really, really hard to get exactly right.
It's really easy to get approximately right.
Just as a side note, Sean Parrott did a whole bunch of research on this back 10, 12 years ago
and ended up writing a couple little libraries called Something in Eve.
I can't remember what the other one was.
I don't think it was Adam.
And it was all about expressing constraints in dialogues and hooking up models and views
and proving that there were no feedback loops and so on.
The kind of thing that's really hard to get right when you're using a GUI.
When you're writing a GUI, that is.
He was inspired because basically every release of Photoshop,
there were a couple of dialogues that they kept rewriting
because people would report bugs against it.
When I do this, this thing over here changes and it shouldn't.
And he ended up discovering that the
reason that was is because the way
it was specced was inconsistent.
And so now he writes the constraints
in some little domain-specific
language and it generates the UI elements
and the interrelationships automatically.
And in the
domain-specific language, it
lets you, it will complain
if they're inconsistent or if their
feedback loops but anyway i digress sorry that was good digression okay and then the uh last
or not last but the other uh library we have here is called not enough standards and this is a little
uh c++ 17 and 20 header-only utility library.
And it looks like there are a couple neat things in here,
like process management and shared library loading.
So just a couple things that you're not going to find in the STL,
but might be common enough that you want to have from a library.
Yeah.
The process tool really caught my eye
to just be able to very easily launch something
and then get standard error and send it out from it cross-platform.
And I know there's boost process and queue process,
but you know what?
For the project I'm currently working on,
I don't want boost or QT in there right now.
It's a little too heavy.
It's a bit much.
And what's funny is just like last week I was googling around like I know
there are other process libraries out there and I couldn't find any
until I stumbled upon this one. Yeah, although
what you said is actually a fairly common thing. Yeah, I don't want
Boost in there because Boost is huge. And I understand that. But really
Boost is a
collection of libraries some of which are very large and some of which are small and so um and
they're not all completely interdependent you know there's a core group of a few libraries that
everybody depends on like the boost configuration library and things like that but you can use
little bits of boost without having all the booster around.
Yeah.
Yeah.
And,
uh, I had not yet on this particular project,
I had not yet pulled in any kind of package manager or anything like that.
And I have now,
so it would be relatively easy for me to just say,
I just need with Conan,
I just need boost process and it would sort out the details.
Yes.
Just to boost process a long time.
Just saying that's,
that's a little bit of a hot button for me. Yeah. Like huge yes it is but nobody uses all of it right all you need is
boost algorithm everybody knows that okay and then the last thing we have is an announcement
uh for meeting c++ 2021 and it will be held online uh November 10th to 12th.
Submissions are now open until the end of June.
And you can also go ahead and get your tickets now
or register as a student.
So that's coming up in November.
I think it's probably worth pointing out too
that NDC Tech Town, which is in October,
has officially said that they're planning
to be an in-person conference.
And there hasn't been any official press release or anything that I've seen from CppCon,
but the website is officially updated to say that CppCon at the end of October is going to be both virtual and in-person.
Right.
I don't know about other conferences at the moment.
I'm sure we'll be seeing more announcements like this, and I have a like we'll see a lot of mixed virtual and in-person ones this year yeah the problem is is that you know
these kind of conferences they have long lead times long planning times yeah we're having to
decide now in may or actually probably last month in april about things in november yeah and you
know you're just kind of guessing yeah and there's, yeah, all of your catering and all that stuff you have to start setting up.
Right.
Okay.
Well, Marshall, we've talked a lot about the C++ ABI on some recent episodes.
And Jason put together a video on C++ Weekly as well where he had some of his own thoughts on C++ ABI.
But one thing I'm still kind of surprised by is whenever I see a discussion on Reddit or something like that, I actually still see a lot of comments about people who just don't know what the ABI is.
And like a quarter of people who think they know that it is are bringing up things that have no relevance it seems like often yeah so so maybe to
start off the discussion we could get your explanation of what the c++ api is and why
it's important um okay well i'm i'm going to actually go a little bit off script and say
not that we have a script um let's say that abi is really a really lousy name for this. There are multiple things that when people
talk about ABI that they mean, and the discussion that we're going to have really applies to none
of them. But let's just start with ABI stands for application binary interface. And it's really a,
it's a way of defining, you know, how parameters are passed and, you know, how values are returned and structured layouts and how exceptions are handled and so on.
And that's what, you know, technically what ABI means. And usually that's a function of the platform or maybe the compiler on the platform. So Windows has an ABI.
Linux has an ABI.
macOS has an ABI.
Android has an ABI.
iPhone, iOS has an ABI.
ZOS has an ABI.
And for Linux, for example, and for macOS,
there's this really nice document called the Itanium ABI specification,
which you can Google for and find, or Bing or
DuckDuckGo or whatever. You can search for it. But that's not what people really want to talk
about when they talk about standard libraries and ABI breaks. Okay, and that's what really the
discussion that people are having right now. And what that means is really, we want to be able to change things in the standard library
in a way that is incompatible with existing compiled code.
I gave a talk about this at, well, I gave a talk like in March,
a very late CppCon talk, and then one just two weeks ago
at an expanded version at C++ now about what this all means.
And I spent probably 35 minutes talking about the one definition rule in C++.
For those of people who are not familiar with the one definition rule,
basically C++ says that if there's a place where you have two different definitions of the same type or class or struct and they differ and there's some place you can see both of them in your program, then your program is with a delightful acronym, IFNDR, which stands for ill-formed, no diagnostic required.
This is a horrible thing.
Okay. ill form, no diagnostic required. This is a horrible thing. IFNDR means that your tool
chain is allowed to put out an executable that can do anything. It's undefined behavior when
you launch it. It's not undefined behavior maybe if you do a particular thing. It's undefined
behavior when you launch it. And there's no warnings or feedback from your tool. No warnings,
no feedback. And there's a reason for that.
Let me run through three scenarios and I'll show you.
Suppose you have two definitions of a struct, okay?
Different layouts.
One of them has three fields.
One of them has two, say.
Okay?
They're in the same translation unit, right?
Fine.
The compiler can notice that and can give a warning or an error, right?
And most compilers do.
There are different translation units,
and the linker puts them together. The linker creates an executable from these two object files.
It's theoretically possible that the linker could, in fact, tell you that if all that information was embedded in the object file, which it's not. Really, what's embedded in each object file is
the way to deal with, we're going to call this struct pair, just for fun.
Right? There's one pair here that has two fields, first and second. There's another pair over here
that has second and first. Just saying. Right? Something like that. The linker could, in fact,
if it had all the information, it could warrant it. Third scenario. Okay? You have two different
translation units, one that gets linked into an executable, one that gets linked into a shared library.
OK, they're different and they get passed back and forth.
So there's no way your linker can tell you this.
There's no way the library, the compiler can tell you this because the program is not assembled until you launch it.
The program loader would have to do this for you.
And all that information is gone at that point. And so this is why, you know, this is why it's IFNDR. There isn't any place, any one place that you can, in fact, catch this. of shared libraries and so on and plugins and whatever until the program is launched and your
and your tools may not even be on that machine you know in the case of commercial software right
you have an app you sell an app you give it to a customer puts it on his machine he launches it
there's shared libraries that this this app uses that are on his machine you know you're not
involved at all anyway so um you know talk i basically I went around and gave a bunch of examples of ODR violations,
some of which are obvious, like a struct with two fields named first and second and second and first,
and they're different types, right?
I think we can all agree that if you pass these back and forth, what happens?
One of them, code of this thing will say say I need to access first. It says great
it's an offset 6 in this struct
and it's 4 bytes long.
And this one says no it's an offset 0
and it's 5 bytes long.
You're going to get the wrong answers. You're going to be confused.
If they're different sizes
and you have an array of them
or a vector of them and you try to
access the elements of the vector
they're going to be in different places and you're going to get confused.
But there's lots of examples of that.
That's just a really obvious one, you know,
making something bigger or rearranging fields.
And so there's not any way to catch this.
That's the ugly part of this.
There are some people, you know,
there were some papers at this last last in-person standard committee meeting in Prague a year ago, 15 months ago, something like that, talking about changing the ABI of the standard library.
And Titus Rinters wrote one proposing, here's a whole bunch of things that we could do if we could change the standard library ABI.
And some of them are minor. I mean, they're all improvements. Don't get me wrong. Some of them are minor.
Some of them, a couple of them could lead to major performance improvements for certain classes of
programs. We know now in these days of machines with big caches, we now know that it's much more
important than we thought 25 years ago to make
sure everything's cache aligned. And so the unordered containers could be made much, much
faster if the data structures were rethought and relayed out to always hit on cache line.
And Google's Abseil library does a bunch of this um that's you know that's the big performance advantage but
there are lots of and then there are a lot of little things too um and don't get me wrong these
are all good improvements these are all the kind of things that we would like to be able to do but
um we don't want to just break every c++ application in the world actually there are
some people who do want to do that. I have seen
people arguing that users who won't rebuild their software every three years are holding the C++
community back. Really. My easy response to that, of course, is that users have their own deadlines
and own timetables. And besides,, and besides, you know, I could talk
about my mother, but she passed away six months ago,
so I'll talk about my daughter instead. My daughter
uses a bunch of software written in C++.
You tell her she has to rebuild it, she'll
say, what? Rebuild?
I didn't build it, right?
You know,
the assumption is
among the people who say that, who say that
they should just rebuild their software every three years,
that basically everything you have
you can build from source.
And there are people who live in that world.
Okay? And for them
a stable
ABI is
of much less value.
I just want to clarify
or ask, if
the C++20 ABI had been completely destroyed, why would that affect your daughter's software at all?
She has binaries with libraries that are already installed on her computer that it knows how to find these libraries that are stable.
Why would that affect her?
What happens when she gets a system update from Apple that includes a new standard library
dial-in? I mean, I have like 18
of them from Visual Studio installed on my computer right now.
Yeah, Apple has one.
So Apple doesn't have any way of versioning their standard libraries.
They have chosen not to do so, That's correct. I see. Okay.
Let me give a very specific example.
Lib C++, because I'm very familiar with Lib C++.
Lib C++ has two different versions of
standard basic string in it, and they're ABI incompatible.
And the reason for this is because after Apple,
after various people, including Apple,
shipped libc++ for several years,
some people at Google discovered that you could make changes
to the way standard basic string is laid out in memory
to take advantage of cache alignment.
And this was a surprisingly large win
for things that did a lot of string manipulations.
Interestingly enough, since Chrome kind of lives and dies on benchmarks,
they discovered that this one change to way down in basic string
got them a 2% gain on their JavaScript benchmarks,
which is a really, really big number.
I don't know if you follow those, but, you know,
two-tenths of a percent is a big win.
Two percent is ten times that.
And so libc++ now has two subtly different versions of basic string in it,
and they're controlled by an ifdef, a set of ifdef.
And people who ship the library can choose which version of basic string they ship.
I can tell you that Apple has continued to ship the old one,
the original one, in names of compatibility.
But every time they define a new
ABI, i.e. when they create a new platform, which effectively lets them define an ABI,
they switch over to the new and improved version of basic string. The first one of those was when
they introduced the 64-bit iOS devices. The second one of those was just this year
when they introduced the ARM-based Macs.
Those kind of things.
But, yeah, Libsyn Postbox has a dozen?
Just a second.
Going on 15 now.
Different macros that control ABI changes.
Wow.
Yeah, that was the first one, was alternate string layout.
Okay? And,
you know, there are people
like, say, the Chrome
folks who embed their own version of LibC++
inside the Chrome binary,
inside the Chrome package.
They turn them all on, because they want them all.
Because they don't care about
stable ABI. It is of
no value to them
because all it has to be is has to match the build of Chrome
that the dial-in is inside.
But let's see, I see alternate string layout,
allow incomplete types in deck,
get rid of a bunch of undefined behavior,
a space optimization.
Get rid of a bunch.
I'm sorry, did you say one of the flags
is get rid of a bunch of undefined behavior?
That's a build option?
There's like four of them
and the reason is because there were some
obscure calls in
like remove node pointer
that was introduced in C++17
that had some undefined behavior in it.
And to get rid of them, we had to change some structures in the existing containers, which was ugly.
So just out of curiosity, though, if I just download libc++ and compile it right now, does it default to binary compatibility?
Or does it default to no undefined behavior? It defaults to binary compatibility. It defaults to binary compatibility or does it default to no undefined behavior?
It defaults to binary compatibility.
It defaults to binary compatibility. Okay.
Yes.
And is that part of these binary compatibility things to be binary compatibility with libstdc++ also?
That was in fact, well, no. Okay.
Okay. There were a couple of goals back in the day for Libstead C++ compatibility.
In particular, there was the goal that you should be able to build code with Libstead C++,
build code with Libstead C++, link them together with both of those libraries, and have it work.
And in particular, you should be able to throw exceptions from one side and catch them on the other.
Okay.
But that's about the only part really there where there's compatibility.
So that does...
You can't pass a std string, for example, from lib std c++ to lib c++.
You can't.
Cannot.
Okay.
But you can pass a std exception because we did a bunch of that was
intentional yeah that's i sound like i feel like that should be worthy of a pause here for a moment
because you know like like like most things right we get stuck in our habits and we um you know oh
well i heard once that libs c++ is compatible with Libs Stood C++.
I am sure there are people listening to this podcast right now that are linking both into their application, either intentionally or unintentionally, without realizing that they probably have some sort of ODR violations of some sort going on.
Well, except that they really don't. And the reason for that is Libstd C++ puts all their
symbols in namespace std. And Libstd C++ puts them all in an inline namespace called
std colon underscore underscore one. Right. Okay. So you link them together, they all have different
names. Okay. Except for the exception types, which all live in namespace std. So that's how you can do that.
You can mix code with both of them
and link against both of them.
It's fine because the std colon colon basic string
will be the lib std c++ one
and std colon colon underscore underscore one
colon colon basic string
will be the lib c++ one.
And the compiler and the linker
knows that they're different.
It looks like a duck, quacks like a duck,
but it's not a duck in this case.
It's not a duck.
Sometimes an ABI break is really, really subtle.
And it's very annoying when you discover this.
In C++ 03, we had pair, right?
It's used in map.
It's used in an ordered map.
It's pair, right has two uh two fields first
and second and the copy constructor of pair is was defined in c++ 03 as assign first to be right
hand side dot first and assign second to be right hand side dot second that's the copy constructor
done somebody got the bright idea of um for c++11 because we had this spiffy new language feature called equals default that we should redefine pair's copy constructor to just say equals default because the compiler will generate, it'll copy assign first, it'll copy assign second, and we're done. It's shorter, and it's obvious that that's what's going on,
that there's no games going on here.
And in fact, the compiler will generate the exact same code.
It's all good.
Except that this opens up a new possibility.
And the new possibility is that in C++11, when you say equals default,
some specializations of standard pair
are now trivially copyable.
Like a pair of ints.
A pair of ints, right.
Or a pair of shorts or something like that.
And on some platforms,
Itanium,
a trivially copyable data type
which can fit into a register
is passed as a parameter in a register instead of on the
stack. And so if you had, say, a pair of short short, and it had a non-trivial copy constructor,
which is what C++03 had, it would get passed on the stack. And if you had one with a trivial
copy constructor, as in C++11, it'd get passed in a register. And needless to say, if you had
some code compiled with C++03 and some code compiled with C++11, it'd get passed in a register. And needless to say, if you had some code compiled with C++03
and some code compiled with C++11, they would get horribly confused because they're looking on the
stack for something that was in the register or vice versa. I have a question that's not directly
related to the overall topic of conversation, but I just found myself wondering while you're
describing this problem, why did C++98 standard pair have a user-defined copy constructor at all?
What should it have been, then?
It should have been left out.
The compiler generates the copy constructor for you
if you don't define any other special member functions.
I'm not sure that's true for C++98.
But anyway, I don't know off the top of my head.
Okay.
The aggregate rules keep changing, so don't know off the top of my head. Okay. The aggregate rules keep changing.
So don't know.
Okay.
That's fine.
But yeah, in C++11, yeah, equals to bold is definitely the right thing.
Right.
But libc++ goes to some work to actually make sure that unless you flip a particular compile time thing,
that no matter what, pairs are not trivially copyable.
Because we don't do that. The tool detects errors in C, C++, C Sharp, and Java code. When you use the analyzer regularly, you can spot and fix many errors right after you write new code.
The analyzer does the tedious work of sifting through the boring parts of code.
It never gets tired of looking for typos.
The analyzer makes code reviews more productive by freeing up your team's resources.
Now you have time to focus on what's important, algorithms and high-level errors.
Check out the team's recent article, Date Processing Attracts Bugs or 77 Defects in Qt 6, to see what the analyzer can do. The link is
in the podcast description. We've also added a link there to a funny blog post, COVID-19 Research
and Uninitialized Variable, though you'll have to decide by yourself whether it's funny or sad.
Remember that you can extend the PVS Studio trial period from one week to one month.
Just use the CppCast hashtag when you're requesting. Remember that you can extend the PVS Studio trial period from one week to one month.
Just use the CppCast hashtag when you're requesting your license.
You mentioned how at the the Prague meeting that there were a bunch of proposals mentioned that,
you know, were we able to do an ABI break, we could get these performance improvements, such and such has there been any discussion about
how abi breaks could be handled in a safer manner because you're talking about some of these
bugs that are you know really difficult and they only happen at runtime um is there any way we
could be better about catching these sorts of things that they're not just awful runtime bugs that would crash your application um i wish i really wish and to me that is like the key the key of the whole matter
um if we had such a way to do thing do that you know i think a lot of the objections to
changing the abi i'm going to continue to use that term even though it's the wrong term
yeah you know changing the changing the binary layout of things in the standard library is going to be really hard.
The idea of just, yeah, okay, people's programs will crash and that's their problem, is to me just a non-starter.
Obviously, this can't be done at the tool chain level because the tools aren't involved
when it needs to be detected.
Um, one of the suggestions that people, somebody has made, but it's just a suggestion that
nobody's actually, I haven't seen anybody actually try it to see how, how well it would
work or how it would scale is that the change the name mangling for uh for things compiled with
say c++ 26 so that i mean it's basically the solution that's the standard libraries do that
you were just talking about you're hiding it in a different symbol basically yes but then the um
then you've basically bifurcated the c++ community. You have old binaries and new binaries.
And people have to actually make a choice for compatibility
or whatever the benefits are.
And the other thing is that we need to,
if this is going to happen,
there needs to be a good solution here,
but there needs to be a lot of people involved in discussing this.
People on the committee can say things and write things into the standard, but if standard library implementers
don't implement it, it doesn't matter. And even better, if system vendors don't ship it...
I'm curious if there was a historical change here. There was the era before C++, well actually before 2013 or whatever, that Visual Studio broke ABI with literally every release.
And GCC also used to break ABI with every release.
Okay, the second one of those I have some knowledge about okay if you google up you know gcc avi breaks
you will discover that there's a list of like a dozen of them and none of them only one of them
had to do with the standard library all the rest okay um uh yeah there's like five or six of them
where we changed the name mangling for null pointer because we had a wrong last time but most of them were
changes to name mangling of very specific things but we do have an example from from the from
libsted c++ and that comes from c++ 11 okay in c++ 11 the stairs committee knowingly changed
the specification of basic string to make it so that copy on write strings were not
actually standards compliant they didn't say you can't implement copy on write strings but they
specified it in such a way that um basically you couldn't do it you couldn't implement copy on
write strings in a standards conforming manner. And there were good reasons for this.
Right.
People were this was right about the time I joined the standards committee.
There was, you know, multi threading was coming.
Multi threading was here in C++ 11.
And copy on write strings do not play well in a multi threaded environment.
You know, you have all sorts of race conditions,
or you have to be really, really aggressive about copying things
on the possibility that something might write.
And so, for example, calling begin on a string,
on a const string, calling begin and getting back an iterator,
not a const string, but calling begin on a string,
should require a copy because you might write through that injury.
And then suddenly begin becomes,
you can't make begin no except
because it's got to allocate memory.
Right.
Anyway.
And so Libstud C++,
the people who work on this,
sat down and thought really hard about how to do this.
And they implemented a second version of basic string
that was C++11 compliant.
And then they modified their compiler
and did a bunch of very strange things in the Snare library.
And don't get me wrong, when I say strange, I mean unusual.
I don't mean wrong or anything like that.
But they did a lot of very strange and clever things,
both in the compiler and the library,
to allow people to use either one of those string implementations,
or both, even in the same process.
And then told people, this is how you get the old behavior,
this is how you get the new behavior.
And let their users choose when or if to upgrade.
And this was still kind of a disaster.
Okay.
I still see posts on Stack Overflow
where someone says,
I wrote this program using Webstead C++
and it crashes all over the place.
And every now and then it turns out,
oh yeah, you have this standard library
that's built against the copy on write strings.
And this and your program is built about the non copy on write strings and you're passing them back and forth.
And it just doesn't. The last time I saw that a question like that on Stack Overflow was May of 2020, 12 months ago.
This I know two organizations that steadfastly refused to turn on the non-copy-on-write strings.
One of them actually did last year.
It took them to 2019.
Another one is like, yeah, we're planning on doing that next year.
So are they using C++ 17 right now?
Are they still using C++ 98?
They're using a whole bunch of different versions.
They're working on several different systems.
Some of the stuff gets compiled as C++98.
Some is 03.
Some is 20.
Some is 11, 17, 14.
But yes.
But those systems that they're using, Libsyn C++, they're still using copy on write strings. Um, they're planning on switching over eventually. Um, this has been, I mean, it's kind of down to a dull roar at this point. Um, but this has been going on, you know, there, there have been this strip, this more or less steady stream of people running into this problem for
the better part of a decade. If you don't mind, I'd like to jump back to the story
about pair. What was the conclusion of that?
Does that mean on some platforms pair of trivial objects is
not trivially copyable still? Yes, like on macOS.
And the reason for that
is specifically to defeat
the... ABI break problem.
Yeah, the ABI break.
Yes. Okay.
Libc++ and Libc++
unless you
set the particular ABI break
macro, it inherits
from an empty base class with a
non-trivial, but empty copy constructor.
Right. Just to give back that behavior.
To get back that behavior. Exactly.
So it's okay. Yep. All right.
Yes. So I want to give you a scenario that I think is interesting. And that is to a thought experiment. I talked about this in my
C++ Now talk.
Suppose that
the
pick on Apple. Suppose Apple,
the standards committee says, we're going
to make an ABI break for C++
23. We're going to change the way
foobity-bobby-bo works.
And it's an incompatible
change. And apple says okay
fine we'll ship that and so we go to somebody who is a graphic artist they use photoshop every day
right and they're working along and they get their their notice that oh there's a new version
mac os 11.3 whatever it is weed maced, since Apple always uses it at their announcement.
At first they claim that the next version of macOS is going to be macOS Weed,
and then they say, well, no.
But anyway.
And so they say, okay, well, fine, we'll go and I'll go and upgrade this
because it has a compelling list of features that I want to use.
Fine.
And there's a note there that says, oh, yep,
make some changes to the C++ standard library.
You'll need to update all your programs.
Okay, fine. So
they update their system, and then they go
and they go to Adobe and say,
I need a new version of Photoshop for
this, and Adobe is right on top of it.
It says, absolutely, because you have a subscription.
Here's the new version. We're all good.
And they open up one of their Photoshop files to start working on it.
If they're really lucky, none of their plugins load.
If they're unlucky, moderately unlucky, Photoshop crashes on launch because it's trying to load all the plugins that this person uses.
And they have the old ABI. If they're really unlucky, Photoshop will compare to work just fine,
and eventually they'll do something,
and it will crash or corrupt their document or something.
And so they say, oh, I have to upgrade all my plugins.
Great. How many do I have?
About 40.
I checked with some people at Adobe,
and it's a perfectly reasonable number of plugins
for people who use Photoshop every day.
Forty plugins from, say, 15 different vendors.
Okay, I have to go contact all 15 of these vendors and get upgrades for every single one of them.
And some of them will say, oh, sure, here.
And some of them will say, yeah, I have a new version.
That'll be some upgrade fee.
Some of them will say, oh, yeah, I probably do that.
I'll put that on my to-do list.
Some of them will say nothing because they don't answer.
A surprising number of Photoshop actions, Photoshop plugins come as a result of, you know, somebody's master's thesis.
But that's not an experience that Apple wants.
That's not an experience that Apple wants. That's not an experience that Adobe wants.
Um, that's not really an experience that the lib C++, you know, as a, as a standard library
implementer, that's not experience I want either.
I want to, you know, we don't want people thinking that software written in C++ is
inherently unstable and it's liable to break any time you change anything on your system.
I guess to push back a little bit, though,
I mean, Adobe comes out with new versions.
Yes.
So if they come out with a new version
where they might be making API changes or adding new APIs,
that could be the time to upgrade to the latest change in the ABI.
It could be, but the question is that if Adobe makes,
Adobe has traditionally been very careful
not to make incompatible changes to their plugin API.
Existing plugins continue to work.
Right.
You know, I would like to see some way forward of evolving,
you know, evolving, you know, things in the standard library.
And some of those are binary changes.
Some of them are source changes and so on.
But there's a lot of people who have stakes in this.
I hate the word stakeholders.
We'll leave it out.
But there's a lot of people who basically are, you know,
between the standards committee and the users,
and they all have users, and they all
have opinions, and they all have their own motivations. And so, you know, to get something
from the committee to the users requires cooperation of all of those people, all of
those organizations. And so they all need to be on board. So do you see, I'm sorry. And I think we need some way of,
I guess, evolving the standard library.
And the Java way to do it is just to create a new class,
call it da-da-da-da 2 or something like that.
And say, this old one, don't use that anymore.
The last time I programmed seriously in Java was in like 97.
When they were doing that,
it felt like every month as they were coming out with new things.
Right.
If you talk to Jonathan Wakely and the rest of the Lipstead C++ people,
you'll find that they come down very, very strongly on the side of compatibility.
They're still shipping TR1, okay?
Technical Report 1 from 2005.
I think Visual Studio Standard Library also has that hidden away in there.
They still have
HashMap, which was, again,
a pre-2011
unordered container.
They are
very, very big on compatibility.
So do you say, and
I don't want to put words in your mouth, but until we
have a solution,
is there any way at all to break ABI and the standard library to move forward?
Like, would you say, no, there's no option here until we have a good solution in place?
Or would you say, sure, you know, in 2035, we can do it or whatever?
It depends on the situation.
It depends on the people involved or the organization that's involved.
I mean, obviously, like I said earlier, when somebody defines a new ABI, right, you have a tabula rasa.
You can do whatever you want, right?
When you have a limited user base that can be responsive to changes in an ABI, go for it. The Linux people can do that,
although they still run into problems
with things built for, say, Red Hat 6
and trying to run them on Red Hat 8
because you have pre-C++11 strings, say.
But the Linux where you build stuff for a major release, you build everything from source, you can do that.
For Google, for example, where famously every single build of their software is everything from scratch, a stable ABI has no benefit.
So they can change it every single build.
So does Boost have a stable ABI?
I guess that would be no, if you have to think about it, right?
Well, it's a little more nuanced than that.
Boost does not promise a stable ABI.
In general, it has a stable ABI
unless there's a good reason to change it.
So for example, the Boost Outcome Library, I think it was, right,
announced three releases ago, a year ago,
that there was major changes coming down the road
which would involve basically an ABI break.
They announced it a year ago, and then they announced it two releases ago,
and they announced it last release,
and then this release that was last ago and they announced it last release and this lot
then then this release it was last month had the new code in it so so the short answer that is no
but you know they don't the abis of things don't change absent a good reason okay what makes boost
then how is that different from the standard library is, I guess, the question that I want to ask.
Well, I'll give you one easy answer, and that is that you can
rebuild. You have the sources to Boost. You can build
it. You can get the sources to libc++,
but unless you know how it was built at Apple, you're going to have
kind of a detective job to figure out exactly what options were used.
Which you'll never find out because it's Apple.
You can do it by inspection.
There's only, what, two to the twelfth of them.
And you can eliminate many of them off the tick.
I guess what I'm hearing is with Boost, I can still choose to use the older version of the library as long as I want to.
Yes, you can.
And you can build old versions of the library.
Yes.
Right.
On whatever compiler I want to, against whatever standard library, whatever.
But if you replace the standard library
implementation on my Mac with something that has newer features. And Howard wrote a nice article
about it basically saying, yeah, that's a great way to make your Mac not boot.
Replace the standard library with something you just built that's if it's exactly
the same, great. But if it's exactly the same,
why are you replacing it? And if it's
different, have you investigated
all the places that use the standard library
in macOS and determined that
your change isn't going to break anything?
Right.
That's not a
recommended thing to do.
But, no, I wish we had a way to evolve the standard
library that was better than the Java way, which is basically give things new names,
new functions, new names. That's the only one I know about
that I can think of off the top of my head that isn't just change
things and if stuff crashes, well, that's not my problem.
Lots of libraries do that in general
they decide they're going to make a major break they'll just call it they'll change not just the
version number they'll change the library name and entirely right and you'll have two two yeah
three four yes that's one way to do it um you know apple has spent a lot of effort over the years shipping various things they call fat binaries.
Yes.
That contain multiple versions of object code.
And I suspect there's a solution there, but, you know, that's a germ of an idea.
That's not a solution.
Right.
And, you know, the other thing is that the Toro has a proposal
called C++ Epochs
that
looks like it would also solve this but
again at the cost of basically fracturing
the C++ community this would
fracture it like six ways
98, 03, 11, 14, 17, 20
when you build something with
C++ 17 it lives in an epoch and it only links
against code that's built with C++ 17.
So if you need
a shared library like, say,
oh, I don't know,
Neil Holman's JSON library,
right, and you had code that was built with
C++ 11 and 14
and 17 and 20, you'd need four copies
of the library. Disk space is cheap, but
it's not that cheap.
And disk space in the last three years has gotten more dear
because everybody's moved to SSDs.
And those 16 terabyte SSDs are still too expensive.
Anyway, so I am, just to sum it up,
I am sympathetic to the idea of improving things in the standard library.
I like to do that.
But the idea of just changing the behavior of things or the layout of things in the standard library and saying, if it crashes, it's your fault, I am very much opposed to.
Right, right.
And the idea that, you know, you could just rebuild your software is pretty much a non-starter for everybody who doesn't live in the open source world well i appreciate uh you bring that perspective and i certainly think there's
some things you went into that we haven't really talked about in our past discussions on abi and uh
definitely helps understand what some of the ramifications were i i definitely agree with
you that we need to figure out some way to evolve i I'm just not sure what that's going to be,
but hopefully the standards members are all thinking about it and trying to
figure something out.
I don't have any good ideas either.
I mean,
I have,
I have a couple of suggestions,
which,
you know,
could evolve into proposals at some point,
but,
but they're,
that's all they are just suggestions.
You know,
maybe we should look over there and think about doing things this way.
But that's not a solution.
That's an idea about maybe this would work.
It does sound like we do need some sort of standard proposal that will solve this problem.
Like the standard has to define something that will handle this.
I don't know. There's a bunch of things that the standard does not talk something that will handle this i don't know
there's a bunch of things that the standard does not talk about and this is one of them currently
is one of them yeah i mean by but you are talking about it by not talking about it
something something has to give at some point the standard doesn't talk about how compilers
implement virtual functions it It talks about...
Yeah, but that doesn't stop the committee
from changing virtual functions.
No, but all the standard does,
it talks about this is the behavior of virtual function.
If you add a virtual function to an existing class,
everybody implements the same way.
You have a static member variable, which is a pointer
to a bunch of function pointers.
Right? The V table. Where does
that go? Does it get...
It goes on the end of the table?
Does it go in the middle? Are the
functions in the V table ordered in some
specific way? The answer is
clearly yes, because it needs to be
consistent. Right.
But it could be alphabetically, it could be in. Right. But it could be, you know, alphabetically.
It could be in order of declaration.
It could be reverse alphabetical.
It could be inside out, you know.
But I happen to know that for GCC and Clang, it's in order of declaration.
But that's done that way for compatibility with existing code.
There's nothing magical about that, except there's a lot of code out there that expects it.
And so these are things that are not specified
anywhere in the standard at all.
The ABI documentation, the Itanium ABI doc
that I talked about, not part of the C++ standard.
Right, right.
And it's not even an official standard anywhere.
It's this collaborative documentation doc
that implementers refer to and edit when they find mistakes in it or things that are unclear.
When I had to implement the exception handling stuff in Lube C++ ABI, I referred to that a lot
and asked people questions and said, this is unclear. Shouldn't this be like, yeah.
Well, Marshall, it was great having you on the show.
It was great talking through this. I really appreciate
your perspective on this and hopefully it starts to move the conversation
forward. Hopefully. Any other questions for me?
I think we're good at the moment. Thank you. No problem.
If you've got any questions, just send me an email. You know how to get a hold of me. Rob knows how to get a hold of me. I think we're good at the moment. Yeah. Thank you. No problem. You got any questions, you send me an email. You know how to get ahold of me. Yeah. Okay. Thanks, Marshall.
You're welcome. Thank you. Thank you. Thanks so much for listening in as we chat about C++.
We'd love to hear what you think of the podcast. Please let us know if we're discussing the stuff
you're interested in, or if you have a suggestion for a topic, we'd love to hear about that too.
You can email all your thoughts to feedback at cppcast.com.
We'd also appreciate if you can like CppCast on Facebook and follow CppCast on Twitter.
You can also follow me at Rob W. Irving and Jason at Lefticus on Twitter.
We'd also like to thank all our patrons who help support the show through Patreon.
If you'd like to support us on Patreon, you can do so at patreon.com slash cppcast.
And of course, you can find all that info and the show notes on the podcast website at cppcast.com.
Theme music for this episode was provided by podcastthemes.com.