CppCast - ABI Stability

Episode Date: May 21, 2021

Rob 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)
Starting point is 00:00:00 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?
Starting point is 00:01:21 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.
Starting point is 00:01:41 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++
Starting point is 00:02:15 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++.
Starting point is 00:02:44 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
Starting point is 00:03:00 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.
Starting point is 00:03:26 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,
Starting point is 00:03:45 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,
Starting point is 00:03:52 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,
Starting point is 00:04:01 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.
Starting point is 00:04:20 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.
Starting point is 00:04:47 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
Starting point is 00:05:34 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.
Starting point is 00:06:15 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.
Starting point is 00:06:33 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
Starting point is 00:06:52 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.
Starting point is 00:07:29 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
Starting point is 00:07:58 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.
Starting point is 00:08:47 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.
Starting point is 00:09:11 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
Starting point is 00:09:25 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.
Starting point is 00:10:08 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
Starting point is 00:10:29 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.
Starting point is 00:10:39 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
Starting point is 00:11:18 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.
Starting point is 00:11:42 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
Starting point is 00:12:26 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,
Starting point is 00:12:53 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
Starting point is 00:13:13 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
Starting point is 00:13:40 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
Starting point is 00:14:08 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
Starting point is 00:14:27 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
Starting point is 00:15:01 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,
Starting point is 00:15:15 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.
Starting point is 00:15:45 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.
Starting point is 00:16:06 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.
Starting point is 00:16:49 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
Starting point is 00:17:37 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.
Starting point is 00:18:30 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
Starting point is 00:19:05 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.
Starting point is 00:20:04 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.
Starting point is 00:20:31 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,
Starting point is 00:20:50 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.
Starting point is 00:21:30 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,
Starting point is 00:22:28 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.
Starting point is 00:22:53 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,
Starting point is 00:23:11 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
Starting point is 00:23:55 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
Starting point is 00:24:42 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
Starting point is 00:25:13 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.
Starting point is 00:25:30 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?
Starting point is 00:25:54 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.
Starting point is 00:26:26 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
Starting point is 00:26:59 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.
Starting point is 00:27:30 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.
Starting point is 00:28:03 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.
Starting point is 00:28:36 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++
Starting point is 00:28:54 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.
Starting point is 00:29:11 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?
Starting point is 00:29:31 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?
Starting point is 00:30:02 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++,
Starting point is 00:30:35 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.
Starting point is 00:31:02 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
Starting point is 00:31:53 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
Starting point is 00:32:19 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.
Starting point is 00:32:37 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.
Starting point is 00:33:27 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.
Starting point is 00:33:50 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
Starting point is 00:34:18 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.
Starting point is 00:34:50 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.
Starting point is 00:35:10 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.
Starting point is 00:35:54 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.
Starting point is 00:36:37 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.
Starting point is 00:37:33 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
Starting point is 00:38:20 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
Starting point is 00:38:53 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
Starting point is 00:39:53 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.
Starting point is 00:40:35 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,
Starting point is 00:41:03 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,
Starting point is 00:41:19 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,
Starting point is 00:41:41 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.
Starting point is 00:42:07 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.
Starting point is 00:42:31 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.
Starting point is 00:43:09 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
Starting point is 00:43:52 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.
Starting point is 00:44:14 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.
Starting point is 00:44:32 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++
Starting point is 00:44:58 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,
Starting point is 00:45:30 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.
Starting point is 00:45:50 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.
Starting point is 00:46:08 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.
Starting point is 00:46:39 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.
Starting point is 00:47:01 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.
Starting point is 00:47:35 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.
Starting point is 00:48:02 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.
Starting point is 00:48:30 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
Starting point is 00:48:53 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,
Starting point is 00:49:22 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
Starting point is 00:49:48 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,
Starting point is 00:50:03 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,
Starting point is 00:50:45 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?
Starting point is 00:51:28 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
Starting point is 00:51:55 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
Starting point is 00:52:31 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.
Starting point is 00:53:00 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
Starting point is 00:53:33 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?
Starting point is 00:53:58 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.
Starting point is 00:54:23 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.
Starting point is 00:55:00 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
Starting point is 00:55:18 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,
Starting point is 00:55:36 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.
Starting point is 00:55:58 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
Starting point is 00:56:40 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,
Starting point is 00:56:55 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.
Starting point is 00:57:10 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...
Starting point is 00:57:45 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.
Starting point is 00:58:08 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.
Starting point is 00:58:23 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
Starting point is 00:58:49 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
Starting point is 00:59:18 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++.
Starting point is 00:59:58 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.
Starting point is 01:00:36 Theme music for this episode was provided by podcastthemes.com.

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.