CppCon 2019: Herb Sutter “De-fragmenting C++: Making Exceptions and RTTI More Affordable and Usable”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

I had an alternative idea for x86 or x86-64 ABI, instead of using CPU flag and adding branching after every return as proposed here.

I thought about how branching could be avoided, and how the normal (non-error) path could be kept as fast as possible, but without resorting to table-based exception handling.

How about using a long NOP instruction added after every function call to sneak in the offset to the error path?

CALL foo

NOP [rip+0x1234]

Here if foo returns normally (with RET instruction), it does not require any branching and its only overhead is execution of a long NOP instruction. It's at least 7 byte long, and can encode an arbitrary 32-bit value in it (a relative offset or an absolute address). If foo wants to throw an error, it can always read the return address from the stack and decode 0x1234 from the fixed offset within the NOP instruction.

👍︎︎ 19 👤︎︎ u/Nekotekina 📅︎︎ Sep 23 2019 🗫︎ replies

I hope they get it into C++23.

👍︎︎ 40 👤︎︎ u/CrazyJoe221 📅︎︎ Sep 23 2019 🗫︎ replies

Why do committee members largely oppose on try statement? ( 1:08:00 on video )

I knew that poll results from P0709 paper, but neither the paper nor this talk explains why they're against it.

👍︎︎ 14 👤︎︎ u/sequentialaccess 📅︎︎ Sep 23 2019 🗫︎ replies

Excellent talk, probably my favorite keynote of this cppcon. We do need to simplify and unite what we have before we end up with too many dialects.

👍︎︎ 10 👤︎︎ u/epiGR 📅︎︎ Sep 23 2019 🗫︎ replies

When Herb talks about "fail fast std::allocator" being globally opt-in, does that mean a (shared) library can't opt-in even if:

  • That shared library is just a bunch of C++ functions exposed to a language like Python.
  • That library has no consumer other than one specific Python library.

In the case of such library, there's no concern that some consumer of the library would be against the new default - all use cases are well known. How can such a library opt-in if it is not a full program containing main()? Are we talking about a compiler flag or some global variable?

 

EDIT: What about global objects that allocate memory? Can we opt in to have a fail fast allocator early enough?

👍︎︎ 9 👤︎︎ u/[deleted] 📅︎︎ Sep 23 2019 🗫︎ replies

Recently I tried Rust Result<T, E>, and I found functions which return, or consume Result<T, E> generate bad code(stack write/read) when not being inlined. But Swift could place the pointer of the error object into the register.

What will the code gen of herbceptions be? Could we define an optimized ABI for functions which are marked as throws?

Also, IIUC, std::error only contains an integer error code? What if I want to add more info for my errors?

👍︎︎ 14 👤︎︎ u/LYP951018 📅︎︎ Sep 23 2019 🗫︎ replies

Outstanding talk as usual! Its interesting that whenever we discuss breaking changes in the language someone brings up the concern that "dialects" may develop within the language. Well... As seen in this talk: we already have dialects! Whether its embedded or games industry; They all use a subset of the language and effectively create their own dialect by disabling (sometimes crucial) features of the language.

👍︎︎ 7 👤︎︎ u/emdeka87 📅︎︎ Sep 23 2019 🗫︎ replies

no subtitles?

👍︎︎ 7 👤︎︎ u/ryouj888 📅︎︎ Sep 23 2019 🗫︎ replies

Really, a strictly static RTTI (with type_index?) Would really solve a problem I'm currently facing. I basically had to roll my own to generate a unique id for a type.

And with static exceptions, I may start to use them. Simply having an open-ended set of types is making me uneasy.

👍︎︎ 7 👤︎︎ u/gracicot 📅︎︎ Sep 24 2019 🗫︎ replies
Captions
so now I'm going to introduce someone who again as most of our keynote speakers and he's little introduction but it really cannot be overstated how much herb does for C++ not just all of his work on the standards committee all the wonderful papers he writes the contributions he gives to various other projects also all the work he does to this conference all the work he does for this community making a better place to be so please join me in giving a very warm welcome to herb Sutter thank you well thank you for that warm welcome thank you for that introduction and thank you all for being here this week and here we are Friday just about evening and I hope you enjoyed the week I certainly enjoyed so many of the talks and the panels the breath and and differences in people that we could enjoy no matter what sector we're in what way we use C++ where we live in the world or anything else that we have a lot in common here so if you enjoyed some talk sometime this week possibly multiple in the same timeslot where you're going to get the next one YouTube please join me in thanking all the speakers all the volunteers and everyone who made this conference possible it's such a delight so thank you very much for that let's talk for a moment about a topic that is very general this is a lead-in to the topic so specifically of exception handling and the runtime type information which are the two main things that we'll talk about about 70% and 30% in about that order but first it's useful to have some perspective because it's very easy to solve a point problem I see a problem I'm going to attack that but no problem exists in a vacuum and there are usually many and often principled reasons to address a problem is so I want to set the stage for why I think these are important because there are many other problems we could be trying to solve and and as you know I'm working on several different proposals but why these particular ones so first of all Andre how many of you enjoyed Andres keynote I cannot promise to be as entertaining as ondrea's I don't know if that's humanly possible for anyone else but he is such a troll sometimes so he he trolled you with some assembler said you have 15 minutes to recognize this common algorithm and then after about 5 seconds he said if you have 15 seconds then after about 5 seconds he said no it's just some random random assembler just trying to get your attention this is not that this is a real piece of assembler and you will get 15 seconds and I'm curious to see what you think of it please raise your hand when you think you recognize what this is doing did I see missiny hands I see a head just yell out what you think is doing a couple of words say again is space no but that's a valiant effort anyone else want to shout out one more dynamic I think I heard that did I heard dynamic cast what in five instructions are you kidding me well kind of sorta if I take out the redacted parts you'll see that the first two loads are about D table pointers and then we do a range check to do a cast this is in five instructions ignoring there's a you can also do a null check which is two more instructions than the top and that can often be elided this is a five instruction dynamic cast that cannot do the general dynamic cast but that can do down casts not cross but down only and not across a virtual inheritance link turns out if that's all you need we can do that way more efficiently than today we'll come back to this example at the end of the talk so to set the stage this is the context I was talking about last year I showed this law about what is C++ it was actually several slides and this was sort of the culmination and working from the bottom up it was about saying what things are not core to C++ like any particular syntax we add and syntax all the time tedium lack of good defaults just ugly this is not core to C++ it's just something we put up with sometimes and we try to fix it and also the middle part backwards source compatibility is not really core to C++ but it's very important to adoption adoptability migrating code bases forward but it's really the top part and especially the first bullet I want to focus it on for today the zero overhead principle that I couldn't write zero overhead abstractions it is true there are no zero cost abstractions because zero overhead does not mean zero cost the people often make this mistake so let me be very clear what zero overhead means two things there are both on the slide you don't pay for it if you don't use it and if you do use it you can't do better by hand and we know your C and C++ programmer so you're gonna try but if you do use it you can't do better by hand what those two things add up to is there is no valid technical reason not to use the feature and as we evolve C++ one of the things that we're observing happening before our eyes is largely we are moving to be a more static language we are doing we still have virtual functions we love them but instead of the 90s when everything was about oo we are doing more and more templates since the nineties and it's only been ramping up from there we are doing contracts for example soon they almost made sequel so sweaty hopefully sequel sauce 23 which yes could be about runtime checking but they're also about static checking enabling static analysis and so this talk is talking about how static can help us because very often when we find we have a performance problem in the language especially often the root is the word dynamic and if we can make dynamic be static that is often a good solution path to fixing the problem that were faced as we evolved C++ historically and you can quibble about sort of the sizes of these I'm gonna give sort of a ruff-ruff priority we've been very good at adding things we just are shipping a sea fossils 20 standard next year that has many major features it's the biggest future release since C+ 11 so we're very good at adding features sometimes we fix things and by this I don't mean the sometimes we fix things by adding features but in this category I'm specifically saying we take an existing feature and say we add an incremental knob on it or something like for instance enabling this capture and move capture and lambdas we already had lambdas but certain cases were hard to use so we had to go and fix it so we do do some of that but I perhaps not as much as we're good at adding new shiny things and rarely in part because of backward compatibility rarely do we actually simplify something in place now often we simplify by going to the top thing by adding a new feature that subsumes the bottom one but that means we now have a language that has both of them so we have using aliases are much more general than type deaths but type tests are still there that's the code still compiles so what can we do to simplify we haven't maybe done as much there and in part we in fairness we couldn't because of backward compatibility so I have a modest proposal what if in future c-plus us evolution we reversed the amount of effort we put into those three things is that something you would be interested in seeing as c-plus involves in the next five or ten years well that's heartening now easier said than done but you know first we have to have an aspirational goal and notice there are some arrows on the screen because sometimes we do need to add things so some of you I'm sure are thinking but herb you're talking you're about metaclasses which is now a thin layer over reflection and generation you want to add reflection to the language uh-huh yeah that's a big thing but it greatly simplifies the code we write that's why I want to add it so when we add things or when we fix things it should be with a view especially to having a simplification target and so I'm going to present the things especially with exception handling that I'm going to present in this context with the focus on simplification in this talk were mainly focused on fixing two things and then seeing also how they help us simplify code there are notoriously only two features in the entire C++ language that every compiler has a tool to switch to turn off every compiler and in the case of the macro has exceptions some standard libraries and those are exceptions in our TTI and in case you're not close enough to see the slides that is not a love emoji that is a heartbreak emoji according to emoji pedia because we love exceptions but for some of us they've led us down and we love our TTI but for some of us they've led us down and the problem is actually already on the screen the arty in our TTI what does that stand for run time it's dynamic there's that dynamic word again and it's a feature where the data has to be available at runtime a lot like dotnet and Java metadata in case anyone ever asks for it even if they don't and other languages are all in on that and we only have it for one or two features that which is already better but that's a symptom and so I'm going to give you a sneak peek of the whole talk so if you have to leave early to catch a flight here's the spoiler today's exception handling requires throwing type erased values that means you're going to dine we allocate when you throw it means that you're going to do a form of our TTI when you catch that's overhead whether you ask for it or not even if you throw 42 and catch int and our TTI requires binary system metadata strings of type information a hash values of types whether the program ever uses them or not now you will find people who defend this saying well we could optimize this better and they are perfectly correct but you can't get rid of them by construction because some code might ask for it so they must the information must be there and those optimizations are really I will characterize they would probably disagree but I will characterize as they are digging ourselves out of a hole that we already jumped into just a suggestion how about not jumping into the hole unless we need the information unless we need the we know there's treasure right here in the field instead of digging up the whole field and jumping into every hole oh no only the time that we need to get the treasure chest we'll go and we'll dig there it will opt-in and so a pay for what you use principle to keep in mind for the rest of the talk make things static compile time by default and dynamic is still there we're not taking anything away from you but it's only if you ask for it and make it easy to ask for making convenient but dynamic by opt-in static by default dynamic by opt-in so let's take a look at the first part of the talk which is about exception handling the first thing to do is establish the problem then we'll talk about what is an error for a couple of slides just to make sure we know haven't reminds what a runtime error is and then I'll show the proposals that I'm bringing forward and again I should put into slavery here I do chair the Standards Committee but this is just me talking as an individual the Standards Committee may or may not like any of this I'm not speaking for them but as I go through the talk in a couple of places I will show straw poll results as to what the committee does think and I'll put context in it from the cologne meeting two months ago about this proposal where it was seen for the first time in the full language evolution working group but let's now first establish the problem let's say we have this what do you think of this code just shout it out to two fours I've ever had like something unprincipled n't quite hear a fireable offense perhaps maybe and maybe not quite that bad but it's like driving you're behind a driver who's white lining it and drifting a little and your choices are say behind that is you can't pass on the left or on the right or call 9-1-1 they probably wanted consider the latter if you're in most countries in some countries this is normal so they they know what to do but they haven't decided the author of this function hasn't made a decision that the author of a function has a duty to make when I design a function or a class part of my responsibility as a designer is to specify the air-handling and decide on the air handling policy here if I say yeah sometimes we'll throw something sometimes we'll return something I have abdicated my responsibility as a designer now what's harder than getting your collar to do air handling getting them to do it twice in two ways now this is a real problem because today we are bifurcated and some libraries may return to widget air some libraries may return may throw an exception and how am I ever going to write template of code that can call either of these and do something sensible because in one case even if this this function this template doesn't handle the error just to propagate it it has to know do I do nothing and let the exception propagate because that's nice and automatic or do I have to say if error return air I don't know this is also an example why not agreeing on a common air handling policy means I'm effectively bifurcated in the community I am effectively programming in a language dialect and incompatible one because I can't compose these two libraries easily in cases like this so I took a survey and this is the part of the ISO C TV Norg survey I talked about this when I gave this talk this part of the talk at ACC you this spring and about half of those projects ban exceptions in whole or in part and these are the numbers from about 18 months ago there was another survey done about about 12 months ago notice the numbers are very much the same and then the most recent one which was done this spring the good news is there are now in this could be measurement error will keep measuring this but there are 3% more notice we went from 49 to 52 percent three percent more that allow exceptions everywhere but you might have noticed there are also three percent more that say they're banned completely so that is not improvement that is a further bifurcation you're getting less middle ground and more the polar ends and that's a shame because if we ban exceptions we're not using C++ and as I said at ACC you that may sound like an ideological statement but I think it's a factual one not ideological we're not using standard C++ because standard C++ requires the constructors that fail throw an exception there is no other reasonable way to report errors and that's what the standard library does if you overload an operator there's no space of the signature the signature is defined by the language if your operator plus you take two things and return our thing where do you stick an error code you can't so if you're in a an F no exceptions world you're not using constructors you're using factory functions generally if they're if they can fail if they can fail you're not using operator overloading using name functions you're likely not using the standard library except maybe for the algorithms because they generally don't throw especially if you've disabled about Alec most library functions standard library algorithms don't throw that you're not using the containers at least not the standard ones you may be in a divergent version of the STL that's not compatible that does something else when the when exception handling is turned off and this is fragmentation interestingly when you take these same numbers and you look at them by the three kinds of error reporting that we polled which was exceptions numeric error codes and sort of expected outcome results style types one of the most interesting data points I found is that air our codes have the strongest support of those three that's the community voting with their feet the expected outcome types which are totally non-standard even though some of them are in boost but many of them are custom are allowed everywhere almost as much as exceptions which are blessed in the c+ off standard and have language support and finally perhaps most disturbingly every one of these is banned outright in more than 10% of projects according to these surveys and that is a measure of how fragmented the world has become because that means there are projects that cannot talk to each other at all they cannot share code at all at least not code that has air handling and I hope that most of our code does or aspires to when you look at these same numbers there's the original there's six months later and here's the gross recent one from this year you see a very similar pattern so this is pretty stable across surveys so standard C plus L says we have exceptions enabled we use constructors and operators normally we use the standard template library and then there's dialects and there's not just one there are many of them because we keep inventing new ways to not use exceptions in to report errors some other way and question which of these the state file system use and the answer is two of them so throughout the now-standard file system library pervasively there are overloads one of which will return an error and one of which will throw an exception just in case you're in one of those worlds it the the C false community is so bifurcated that the Standards Committee feels they need to support both of these dialects and they will probably not say it in those terms but I will we are supporting what is effectively becoming two languages and that is not a good thing and we have pressure to do this with our next round of libraries like networking and other libraries that are coming to do the same thing just so C++ developers can use them that's a problem that needs a solution and what's the root cause today's exception handling is not zero overhead so summarizing the reasons as I've gone and surveyed people and teams over the last five years or so and longer than that but especially systematically in the last five years I've been hearing the same three things over and over two things and then an optional one the first is I can't turn on exception handling at all and you ask them why it's standard we recommend it it's beautiful is it but look what it costs and they show binary size or they show the the other expense of it because just turning on exception handling incurs space overhead on every implementation whether you throw anything or not and this is regardless of whether you're using table-based exceptions or non table-based exceptions it is even with the optimizations that have been proposed and reported in this year standards committee mailings and this violates the first part of the zero overhead principle which was you don't pay for what you don't use I should be able to turn on exception handling and if I don't throw an exception I should pay approximately zero a nice round number and we're not there today the second problem is okay even if I have a H enabled in certain code I may not be able to throw an exception and you ask them why and there's two sub reasons for this one in real-time systems or memory coentrão constrain systems where they have to be able to predict for a call tree including for reporting an error how much space and or how much time it will maximally take so compute an upper bound on either space or time or in some cases both can't throw an exception because I could not statically compute an upper bound for how much space or time might be needed and it's because I have to do dynamic allocation to throw and a form of our TTI to catch even if they're not in that kind of constrained system where the performance of an the deterministic performance of exceptions is important they may say yeah I could throw but look I returned and expected and I can prove it's a hundred times faster in my scenario and that may matter even when people say well you know you should exceptions only in exceptional circumstances we're gonna get a better definition in a few minutes but that's not always true you need to throw an exception when the function couldn't do what it advertised and that usually is rare but there are some functions that actually may fail often and that is a legitimate failure imagine parsing XML part or parsing something that you got off the web it may be quite common to say parse int and like you hear that but that's the word hello is not an int sorry right it may be common to get errors you may get errors in 10% of your calls and you may not be able to predict where those are this violates the zero overhead principle part two when you do use the feature you generally couldn't reasonably do better by rolling your own and then finally the bonus problem is sometimes people say well I can't throw through this code because they may like the fact that exceptions propagate automatically but not invisibly they can't see look at their read their source code of their function and see the control flow has because of throwing exceptions where as you can easily see all the others the is the while those are visible you can grep for them I can't grep I can't even see the exceptional powers so that's something that people volunteered as a problem they regularly face so these are the problems that I heard most often when I presented this for the first time I presented it to subgroups last year already but this was the first time in two months ago in Cologne where it came before the full evolution working group that's really responsible for recommending the design of the language and I actually asked them to pull each of these complaints I can't afford to enable it I can't afford to throw and I can't throw through this codes I can't see the code path and we actually took a poll not on the solution I'm going to show I'll show those polls later but just on is this an important problem specifically the poll was of the form it said for each one of these is this a problem that is worth this committees time to try to solve you will notice that for all of them there were some people who thought it wasn't worth the committee's time to solve them but fortunately the large majority of the evolution group felt yes these are problems that would be worth trying to solve that doesn't mean we can find a solution we'll accept but in principle yes we would like to invest time in solving these problems so that's very heartening let's for a moment define what is an error if you go to the dictionary merriam-webster is a good one they define error as an act that fails to achieve what should be done so if you call a function that says parse ins and you pass it a string if it can't get you an integer out of that string then it can't achieve what you asked it to do that's a simple example basically it comes down to can the function achieve its success post-conditions can it do the thing it advertises and I'll say let's use the term error and to be crisper a recoverable error a program recoverable error means a function couldn't do what it advertised only those should be reported as errors to the caller and notice I did not say anything about it whether you use exceptions or result types or error codes this is a very general principle that doesn't it is agnostic to the actual mechanism you use to report the error is it you shouldn't even be telling the calling code about it unless it's in this category this has some consequences there are certain things that the calling code cannot do anything about so abstract machine corruption is a very simple example let's say you stack overflow not the online one but you know actually really trying to make a stack array of a billion or something oops that didn't work if we treated that think about if we treated that as an error code or an exception we were report to the calling code well first of all what on earth is the calling code going to do about it it cannot programmatically recover because you may be in a corrupted state if you run out of stack you may actually not be in the consistent state anymore because you might have spilled some of your rights or corrupted memory but more to the point if we did that there would be no no except functions every function could throw because any function could be the last one who's got one bite left on the stack when you try to call it so this is not a kind of error that we should report to the calling code because the calling code can't do anything about it and if we tried we would do damage to our programming model we would be losing no except for practice all practical purposes for example so reporting that as a runtime error would really be a category err it's not a runtime recoverable error the same thing is true of a programming bug so if we have an assertion in our program or a precondition or a post condition and it's a statement about the expected state of our program if we're using assertions in that way then by definition if we violate one what does it mean it means the program is in the state that the code was not expecting and is not designed to recover from now there are techniques for if you can partition your memory and have memory isolation and you have you can throw away parts of your program and restart them are there are techniques to try to recover but it's not by normal stacking unwinding and fixing things on the way back usually it's by abandoning parts of possibly corrupt process and then resuming restarting these also should not be reported as runtime errors so out of bounds should never ever be an error code or an exception I know that that is gonna raised hackles with some of you because we're used to that you'll say the standard library does that I will show you that we talk to the standard library folks I believe I have the poles and a few slides and they agree unanimously yeah that's a problem and we're going to gradually migrate the standard library away from basically the logic underscore error which you might say is inherently a logic error that shouldn't exist so here's our texana me if we corrupt the abstract machine we need to stop we might terminate doesn't mean right away we might have some last-ditch you know try to save some state before we terminate but we can't really continue we have to tell us this hop or somebody like that and we are telling a human being because we need more space the program failed and you need to restart it category B if there is a programming bug like an assert violation or a precondition violation we express that using assertions log checks hopefully soon contracts and we tell also a human being but this time we tell the programmer this is category B we want to come out at unit testing and system testing and we go identified a bug please go fix your bug category B we don't throw an exception because the program can't fix itself we can't we don't want to tell the code we want to tell a human being and finally we have our coverable error like host not found or know that string did not contain an in please try again and there we could throw an exception or use an error code and we're reporting to different species to code and the calling code should recover so that's the taxonomy I want to to highlight because it's part of these four coordinated proposals and it comes up when we talk about number two in particular at number three so here are four coordinated proposals which are about trying to solve the problems we identified at the top each one of them could be its own paper but I deliberately and normally I encourage people to write narrower papers instead of big monolithic omnibus papers with a jumble of features but this isn't just a jumbo these are related it could actually be a problem if we tried to solve these problems in isolation because we might get a local maximum and we might say oh here's a solution for a and here's a solution for B and for C and for D and then you stand back and we'd standardize it and lived with it for 10 years when we realize if we only had a time machine how often that we said that we'd go back and if we actually thought about ABCD together we would have designed C a little differently which was just as efficient and just as useful but now it doesn't cause a problem with integrating with the other three so that's why I think it's important to look at these three together and I think by the end of this section I hope to convince you that if we do all of them the sum is greater the whole is greater than the sum of the parts in terms of what we get to achieve so let's look at the first part of the proposal the first part of the proposal and the one that probably gets the most notice is that we want to address the zero overhead of throwing exceptions so inherently today we are throwing objects that have to be type erased and dynamically allocated and we catch using a form of our TTI it's not dynamic cast that you can spell yourself but it's an equivalent form that's built into the compiler that's basically a close cousin of dynamic casts that you can't spell yourself except as catch based reference there is a there's a different kind of exception classification going on there so the proposal is well if dynamic is the problem static may not be the answer but it ought to be the first place you look so let's try to see if static is the problem to this to the solution to this dynamic problem if we can throw values of a statically known type that means we can return it by value and if we catch by value that means we don't need to allocate and we don't do a heap allocation and we don't need to do our TTI because we know its type it turns out that if you do it this way and this is basically the same as many languages have invented with a result type such as in rust and go also a the same thing boost expected does an outcome if you do that the result is isomorphic to error codes but one of the benefits of baking this into the language is the language can basically overlap your success and failure returns and use the same registers or the same channel for either one because it's only going to be one or the other you're either gonna succeed and here you go here's your your int or you're gonna fail and here's your error right and you can overlap those if you're the compiler and that could be a lot more efficient the thing is we want this to be backward compatible so because C++ already has dynamic exceptions we want to keep all of the exception handling we have today the in terms of the programming model try catch but we want to be able to opt in therefore this is not a breaking change to say oh by the way for my function mr. compiler please go and implement this using the fast path because I promise to only throw a stood error so here's how it would operate it's as if you returned a union of success and error and the boolean discriminant could even go in a register or a CPU flag that's an implementation detail the idea is it can be made efficient but the nice thing is we still have the exceptional programming model we still have throw and catch we still have automatic propagation but we have the same implementation as error codes it's just the compiler does it for us so we're really doubling down on value semantics you see how going static lets us double down on value semantics and c-plus class just like we've done with every major release of the standard and see fossils 11 we doubled down on value semantics by and became move a major part that integrated throughout the whole language and throughout the type system and that made our code faster because we said we're about values and we will optimize for those and we will embrace that that's what this is saying so now from a sales point of view let's put on our sales hats for a moment let's say you love this proposal as much as I do and you want to tell someone about it and if you're talking to somebody who loves exceptions you can say this is great just makes exceptions more efficient it's the same programming model you throw you catch but now all the unwashed masses who thought they couldn't afford it they now they can use it and we've won them over if you're talking to somebody in the aforementioned so-called unwashed masses who is using expected outcome and knew very well why they were using it because they couldn't use exceptions you say to them you know what we're taking over the world we're building expected an outcome into the compiler in that grade and they're going to use it with their their throw catch oh and by the way you know how when you use expected and outcome you always have to write if air return air if air return air we can get rid of that automated automate the propagation for you so you're getting expected outcome baked into the language but better because you don't have to automatically propagate it the compiler will do it for you if you're talking to a C programmer who says I you will never take my return code out of my cold dead hands you'll say it's okay what we're doing is we're convincing those newfangled C++ people to adopt error codes you're really automating error codes they just don't know it it's giving a nice syntax it's just a syntactic sugar for error codes by the way you know how when you use an error code and you return an error code when you use it as a return value you don't get to actually have a return value they say well yeah that's kind of annoying and if so what if you do want to return the result well I pass it by an out star parameter yeah so if you have a return value and you want to add an error type well then I add an error code star parameter yeah yeah I know wouldn't be nice if you didn't have to do that wouldn't be nice if you could just return your result or an error and the compiler do that for you and you didn't have to have these out parameters so you didn't it didn't have the error code type monopolize the return channel think about our folds and for you oh that's pretty good oh by the way well propagate them for you so you also don't have to write if error return an error all over the place now there I start to get warmer and barrier start to break down so here's the core proposal summary the dark green the try-catch no change this is still c+ exception handling as we program it as we know it the only thing is different is the word throws on the function f and function G in this example where the function is opting in to say by the way I but when I do fail I may not always fail but when I fail I will throw a student : : error type which by the way is trivially capo copyable and moveable it's actually even a little bit better than that so I to throw I need no dynamic memory allocation I can bit blit it all the way back to wherever it's being handled even if it has a non-trivial destructor see the paper for the magic and see the other related papers this is not just a little bow pod type this is a type that could have a destructor but I don't have a bit litt move so that if it does have one at the catch side I can destroy it properly it's totally typesafe but totally efficient no allocation no RTC I still with destructors we're doubling down on value types and for compatibility let's say that I call something that like return quote X Y Z Z Y quot s now that is a string literal that is a stood string in the standard and then I add plover another string so I'm creating a temporary stood string object and I'm through and I'm returning that if I call something that throws something could throw something else because that string concatenation could throw I can translate that into a stood error and we'll see where particularly bad alec we would like to treat differently anyway but that alec isn't gonna go away it's just gonna get greatly reduced we hope that this applies to all of the standard errors so we can still call the existing code that uses dynamic exceptions but if they throw something and we don't catch it that's fine it still propagates automatically we'll just convert it to his stood error so they would have still had to allocate it and incur the cost they had so no change to their status quo but as soon as it hits us we are adding no extra overhead we just zip it right along that's the model and again it's fully backward-compatible it does not require change to the ABI it does not require changed in the semantics of any existing function because you're opting in and then we can take the fast path and it converts to and from as you cross boundaries from the old sow exceptions to the new stop functions that use the new style ones so because of this because it is isomorphic to error codes it has all the overheads of error codes which is minimal typically constant time and it's at urbanistic because with error codes we have static tools that can compute upper bounds on how much time it may take to return an error code those we still need I still need to validate this with implementation my expectation is that with very minimal if any adjustments those will direct the work with these exceptions whereas today we do not have tools that for today's exceptions can compute upper bounds but we will for these why because they're isomorphic to error codes by the way a nice trick when you're designing a new thing if you can say it's isomorphic to something else that is well known and you're not just saying it it's actually true you can demonstrate there's an isomorphism right yeah I see here the Chuckle yes okay sales hat off yeah I forgot to do that before technical hat firmly back on it over the ears if you can show it's isomorphic to something else all your proofs just come right over everything that was proved about that is now true for you too so there's a great power and saying it's isomorphic to error codes so in pseudocode to summarize here's what it looks like on the left to throw my exception I have to essentially new up you can do this a bit more efficiently but essentially new up I need some sort of dynamic allocation that is not stack allocation and if you're thinking but windows does it on the stack no it doesn't CP zero seven zero nine appendix au it cheats by using stack memory and interfering with the stack by using a weird stack based allocator that will make you run out of stack faster but it is not a stack allocation it is not a lagaye so you need a dynamic allocation and then at the catch side at the bottom-left you need to do a form of RTT i to say is this void pointer a basestar you need it basically a dynamic cast that goes from a void star which dynamic cast the standard one does not do to a particular base pointer whereas the proposal on the right hand side is simply return a value that is basically two words return it by value and catch it by value and that's where the efficiency comes from so what are the benefits I hope if this succeeds and this is a long-term thing like please do not ask is this in C plus 20 or C plus 23 this is a long lead endeavor that is still in its early phases we still have to for instance do a prototype implementation that does not yet exist not a full one anyway but we hope that what this will achieve is that there is no longer any valid technical reason not to enable exception handling of this form even if you still disabled the old exceptions you may still disable dynamically typed exceptions but there should be no reason anybody ever asks their compiler vendor for a switch to turn this off because it's zero overhead and it hopefully will let all code report errors using exceptions even real time code even embedded systems that are very space constrained because I can't reasonably write it better than this even if I'm using a C error code and C developers one of the things that I do like about C I don't use it regularly I use default house because I like it better but one of the things I like about C is that you see what you get you can see the costs of things to some degree so memory allocation etc if the memory hierarchy is still opaque but the costs of things are more visible than they are in C++ but even in those for those C programmers we can do better than you can do by hand because you can't fold the error code and a meaningful result into the same return unless you return to Union all the time and do your own discriminant like I mean you have to do a lot of work by hand to get an equivalent semantics so no it fails the test of you couldn't reasonably write something better by hand and then we get if we can get to this then we can get to the bottom part of that pyramid that I was proposing and we get to an actual simplification in teaching we could start to teach a rule as simple as every function should be declared with exactly one of no except or throws all new code this has happened before as the last bullet says we now teach now that we added override and final we can do something we could not do in C Fussell's 98 we can and we do in the core guidelines teach that every virtual function should be declared whether it's the base or derived should be declared with exactly one of virtual override or final and you can show that this people can argue about styles they haven't I'd like to say too but you can show no you don't need to this is perfectly clear and consistent the same thing can be true of exception handling that you have to write just one of no accept or throws and that's the simplification of the way we teach C++ but now let's look at the second and third parts of these coordinated proposals because these are about throwing fewer exceptions the fact that's on the screen is actually language-independent go has discovered this dotnet and Java all of them have discovered this here's a quote from Joe Duffy where he points out who's a dotnet expert and worked on a systems a version of c-sharp that was used within Microsoft for a while and he pointed out that you know once you have contracts and this is irrespective of language but he specifically ties it to a java.net something like 90% of all of the exceptions specified in the Java and.net standard libraries become preconditions and you don't throw them no more remember if an assertion or precondition fails it indicates there's a bug in the program so you shouldn't be reporting it as a dynamic error to the calling code as if it could do something to recover it can't by definition your program is in a state that that code was not intended not designed to handle instead this should be happening during testing and you reported to a human being the developer and say please go fix your bug and that means that it could get rid of something like 90% of the fun of the exceptions in Java and.net and the same thing is true in C++ so the summary of the proposal is as I alluded to before in the standard library let's actually with a transition path because this is a multi-year multirow least transition path start especially for new things in the library start moving to a world where we no longer make preconditions throw exceptions and if we do that suddenly many functions in the library can start becoming no accept and so far the library evolution working group is a very strongly in most polls unanimously behind that direction and so the uses of stewed logic error should eventually turn into things like assertions or preconditions or things like that but it's a long migration and with compatibility in mind and that brings us to the third part what's odd here what I first saw this I thought it's kind of odd that this astronaut is playing their guitar in a vacuum but if you think about a fictitious scene which maybe isn't the most productive thing in the world but if you think about this fantasy scene there's no audience so that's okay and the guitar is in physical contact with the suit so it may not be a very clear audio but they can probably hear the guitar and then in their microphone maybe the audience on earth can hear so yeah okay you can squit and make that make sense but it looks kind of odd similar to today here are two oddities numbered 1 & 3 because they're oddities oddity number one is exceptions must be dynamically allocated today we talked about that already oddity number three is that dynamic allocation failures are reported using exceptions what's wrong with this statement with putting those two statements together actually two different things the first is well we already saw it would be nice and exceptions didn't have to be dynamically allocated so we already addressed that part but also many allocation failures don't need to be reported as runtime recoverable exceptions for example if you knew int a million or let's say char or byte that's because that's a better raw buffer let's say you knew a buffer of a million bytes and that fails does that mean you've exhausted the limits of the machine no you can still write a normal code in fact you might say okay give me a but for half that size and let me try again you may do fall backs so that doesn't necessarily mean that you've corrupted your machine but if you try to do new of a single int and that fails you are in deep deep trouble because you have pretty much exhausted the the abstract machine the actual machine you may not even be able to return from the function which means you can't throw an exception or return an error code reliably why not because to return means first before you can even deliver the error code or the exception you have to unwind by running the destructors destructors our normal code normal code often allocates unless you know that nothing not one of those destructors or anything they transitively call down to a million function calls deep will perform an allocation you have no guarantee that you'll even be able to return so you cannot continue executing and so termination semantics anytime I see say fail faster termination sure you'll there you'll want to have to have a fail fast handler or last-ditch a little itty flush what I can to disk and hope for the best you know to rent data loss but I can't just continue on as if I can recover from this and nothing happened so where we are now is we've we think we've addressed the first one with having lightweight exceptions but so now what do we do about memory failures sometimes I'll say out of memory and that's fine but that's a shorthand for a broader term that is allocation failure so sometimes people who were rightly say not every failure to allocate really means I'm out of memory and that's true because there could be fragmentation it could be just at that particular allocator or pool is full it could be because it was a big thing and you're trying a big buffer allocation request and you can try a smaller one but I think we know that when we say whom so sometimes I'll say is a shorthand for all of that but a more technically correct term is allocation failure so the direction from the Cologne meeting two months ago is one that I actually strongly agree with it was that's the proposal I walked into the room with it was one of the alternatives that we had talked about but it was not the one I walked into the room with I think it is way better than my previous idea so thank you library evolution working group because I I love that old that'll say there's that isn't from South Park I'd ever seen South Park so I only see the memes I think it's from there that none of us is as dumb as all of us yeah that that's fun to say none of us is as smart as all of us so this proposal would not be as good as it is without the help of many many people dozens of people acknowledged in my paper as certainly here I did not come up with this very simple and nice idea thank you to Niall and Titus and many others who came up with it the idea is let the allocator decide we're gonna have allocators advertised in a no accept queryable way so it's part of their static type hey if I can't allocate do I fail fast or do I report the air throw about Alec that's all simple idea and then add a conditional no accept to every function in the standard library where allocation failure is the only possible failure and if you're using a fail fast allocator that doesn't report that function can be no except now you have great control and can make large parts of the standard library know except as actually used in the field and that leaves only one major question what about the standard stood allocator that everybody uses by default like when you have a vector event you're implicitly using stood alligator which uses global knew basically there's several global news but as a shorthand I'm just gonna say this the default global knew the third poll that was taken actually a second in order but the third I'm showing on the screen is okay so now we categorize these two kinds of alligators on allocation failure they either fail fast and abort or they throw about Alec and report the air they're aborting or reporting what should the standard one be library evolution working group with only one weekly these numbers are strongly in favor weekly in favor neutral weekly against strongly against except for one person who was weekly against that group that day was unanimously in favor - one person of changing the global default on everybody even though that would be a breaking change does that scare you or delight you this applause for if that scares you okay applause for if that delights you this is a good play if you look pardon me a tangent we'll come back to this in a moment this is a very good place to to use an object lesson to give an illustration of what consensus means we just had roughly by decibels the AV people could probably measure it accurately I don't know something like 4 to 1 in favor of yeah this delights us let's go do it but if we just did that we took that result and said say this was in the committee and we were deciding it we took that result and said great has changed and we run over those people who clapped first that is called tyranny of the majority you can do it but it's it's not consensus so sometimes people ask well what is consensus nice oh it is not tyranny of the majority it's not because you got the votes even though that's greater than the 3 to 1 in favor that we usually look for it also includes the absence of sustained opposition so the majority is required to listen to the minority you can't just take a vote and say I won terrible to be you know you have to listen to the concerns of the minority you have to actually try to change the proposal to satisfy their concerns and now if after you've tried that you can't satisfy their concerns then you take a vote as 41 and and you win and you go home and you do it anyway but not without first listening to them so that's the first of the two parts of consensus it's not a tyranny of the majority the second part is it's also not a tyranny of the minority the minority can object and must be listened to but after they have been listened to and a real solid sustained good-faith effort has been made to take into account their objections and then the majority still decides no we just we just can't do that they've been listened to they cannot prevent they cannot veto it forever by simply saying no no we have to have unanimity that would be tyranny of the minority so it requires a real collaboration and a real amount of work so that it's not just a numerical thing so sometimes people will ask you what's the numerical pole and yeah the numerical pole matters less than the effort to converge and to work together to solve the problem in the way that addresses sustained opposition so sorry for the diversion but it's something that I live all the time and my job as chairing the committee and so and it's something very important to how the committee operates and so is a nice lesson so those of us who clapped for this coming back to the slide library evolution working group agrees with you those of you who clapped first saying you're kind of uncomfortable evolution working group half of them agree with you and so the language evolution working group was split right down the middle on this poll which would be a breaking change for existing programs now if you think about what this means if we suddenly start saying that global knew fails fast by default you can argue that's not actually a breaking change for most programs and the way you argue that is as follows most programs today and I say this with more words in the paper and examples but most programs today already aren't safe for bad Alec they aren't some are there are programs there is code that is that is safe for allocation failure but most of the code you think is safe for allocation failure when you actually look a very like a super common result way over 90% of cases I've seen personally and worked with people they're sure their code is right and then they realize those actually that code couldn't even execute it we never even tried that path why did they get away with it for reason number two why this is really a breaking change for many people is because bad alec almost never happens in a real practice they practice if you are on a lazy commit system allocator unless you have specifically worked with your user commit settings say on Linux or your or your using a pool based allocator that can throw an exception when you exhaust that pool you can go decades and never actually see about alec even if technically could be thrown if you're on a system that has virtual memory you may thrash and run slower and slower and slower before you ever get to the bat Alec you'll be executing yeah not executing your program overload of the word executing you'll be killing your process however so you could say that many wouldn't even notice okay let me give just a couple of anecdotes so I actually created a with Marshall Clow and the help of others but especially Marshall after ACC you we talked about this and on the plane ride home we created something called bad ba BB you can find it as a project on my github it's a repo and under github.com H Sutter and or herp cetera I forget whatever my handle is on github and the the point of it is it's a very simple little fault injector that you can instrument into the edge of your global allocator or to your custom allocators that will inject random false with a determine frequency and and profile so I asked the power point team well first of all I asked the compiler team to say could you try this and see if the visual c++ compiler team could you try this and see if it finds any any errors like we're throwing a bib a Ballack that we inject at an allocation point to make it appear to fail might take down the compiler and the answer was no we can't do that of course it'll take down the Kamata we don't even try oh okay well okay well should I do well you might try the libraries team next door or literally next door the next team room over so I walked to the next team room over and talked to STL who might be in the audience here today and I said so can we try it against our standard library and instrument it and I'd be able to do that I don't know what you learned from it cuz we don't I mean the standard library implementations are probably the most battle-tested exception hardened C++ code in the world well I wouldn't be surprised if we just fall over so I tried I went home that afternoon created a new project in Visual Studio created LinkedIn included Bab dot H and all I did was create a test loop which did push back into a vector of string and ran it with random faults being injected like once every once every thousand allocations or so see the paper for the longer story short version is it fell over it crashed even though it should three because even though it through an exception and there was a try-catch block around the vector string push back push back loop the exception never made it why because you might have need to allocate throat exception no it just fell over and I did not get I think they don't get to try any other examples with lib c-plus ellipse stood c-plus boss that example worked on those implementations but that to me was strong data that none of our customers who ever build in debug mode and use the STL are actually hitting bad Alec ever and I know that because the the actual root cause is something that is on by default in our debug in our debug mode /od and it is something we know from Salemi tree that ninety percent of customers do not turn off so we know if you're ninety percent of you if you're using visual c++ and you're you're running in debug mode you would be in this situation and we have had like I forget the number it was like four or six bug reports in the last three years about this hard but it's the very first thing I tried vector a string pushback just falls over if allocation fails that is data that people don't write bad Alec Hardin code or they would have been testing it in fact the bug reports were people who were testing for bad alec hardening and most of the time people aren't encountering about Alec in the first place so that explains why library evolution working group was willing to take this change because they figured a lot of people will just never noticed because it just doesn't actually happen but it will let's make a swath of the library no accept but then we come back to the first group who applauded there is code there that relies on this behavior and so the updated proposal I'm going to make is basically the same thing except instead of saying unilaterally that these default global and standard allocators fail fast and a Bourdon failure make it a configurable option to let the programmer decide for their whole program what they want the behavior to be that way it is not a breaking change but anyone who wants to adopt the new behavior can start seeing no accept performance improvements in the standard library as we roll this out if we go this direction some a few years from now so that's where we are right now so it looks like that's the path we're gonna go to but it's going to require to make sure we just don't break the world because that would create hard opposition even if it's minor opposition that's important so I think just to switch to let the programmer decide which kind of allocator the global allocator should be should be enough here so our taxonomy we can extend that saying a failure aborting allocator is considered in category a corrupting the abstract machine and a failure reporting allocator while it reports errors so what are the benefits well we can turn we talked about writing preconditions from exceptions into static bug reporting to the developer and putting these two things together taking assertions and preconditions and much out of memory allocation failure and making them not be exceptions will eliminate something like 90 95 % of all exceptions there are and will let us make most code most functions be no accept and that is a performance carrot because no accept does I know you're gonna hear people who say it doesn't you can go out bolt it it does make your code faster as a bonus it will eliminate about 90% of those invisible control flow up has because those are the exceptional ones the ones that are hard to reason about and if you look at guru of the week number 20 I showed a four line function with three normal control flow paths and 20 exceptional invisible ones we can do a great simplification by getting rid of many of those invisible ones that makes our code cleaner easier to reason about a more robust finally in the exception handling section what about supporting an explicit way to see where my exceptional powers are so if you are familiar with Swift or if you looked at ago doesn't have this yet but they recently announced that they're working on it many languages have this idea of at the call site to write a word like in the NGO proposal I think is checked in swift it's try here it's try on the expression or statement they call something that might throw if I do that notice that exception propagation is still automatic there's no catch here necessarily it's just say I'm annotating with the word try just its signal in the source code I can now see and grep and reason about this line of code may have an edge out out of the function I can reason about my control flow now today we could not possibly even if we had this at a language we could not possibly require it because almost everything can throw that's the default that would if we required people to write try on everything that could throw there would be mass rebellion and there would be a new switch among the first two for exception handling an RCT I saying turn off or try requirement because you'd be writing it on almost every line of code and it's like highlighting every line on a page in a book it defeats the purpose nothing stands out anymore all you have is a yellow or a green page now that makes the text a bit harder to read but if we are in a world where most functions don't throw and certainly as it says in the text bullet in the middle if we required this in new code in such a world where you didn't have to just annotate every single function call now we could get to a world where we can actually give compile time guarantees that there if there is no try or throw there is no exception and that's a very interesting place to go so what are the benefits of this convenience we still get automatically propogation no change to that we're just making it visible in a lightweight way so we can see the paths where the exceptions might go and if you put all these four things together that I've just said two and three enabled that most functions are no except two three and four together enables us to require writing the word try every place that could throw now I could take C code and compile that C++ and either I manually or a linter tool could immediately piont ID tool could just put tri in the things that could throw because one of the big objections to taking C code and compiling at a C++ in a C puzzles project is now it's probably not exception safe it's C it was not designed by the author of that code with exceptions in mind it's not probably doing ra íí- that's the language of no destructors it probably has its F close without a catch cuz there's no catch right so if I take that C code and I can compile the C++ but like today but now automatically putting in the word try at all the places that could throw and that's the minority of places now I can actually look at that code and reason about whether it's exception safe or not and know where to look to do tweaks to fix it to make an exception say if it's not I can now migrate converge C and c plus those code with more confidence so I showed at the top of the street in the top right the poles on these three problems are they problems worth committee time to solve the Committee agreed yes this is worth committee time to solve that's not that's not the same thing as saying that will like your particular solution to those problem to that problem so for the things that I've just shown you I've already shown you the votes for number two and three for number one enabling the throws state the throws decoration to have the efficient value based exception handling there was large supports don't mind people against but there was large support for continuing to pursue that direction which basically means they said no promises herb we may not ever accept your proposal but we agree you should do more work just to be totally clear that's what these votes mean at this stage but it was also implicitly a promise that if you do it we will look at it again so it was an incur it was encouragement but not a promise that they'll ever take it for number four there was not consensus there was mostly opposition to try expressions and so I may decouple that from the proposal and that can be separated but I'm still going to design the whole thing all four of these parts together they just may not all be off adopted at the same time or some parts may not be adopted in standards he process in the foreseeable future we'll see but the core of the proposal numbers 1 2 & 3 seem to be going well and that's a status update for that now we're going to talk about a totally unrelated thing using almost the same slides cuz it's the same pattern first we establish the problem today's RTT I notice how I just changed eh to our TTI if you compare slides they're identical violates does your overhead principal I can't afford to enable it means I'm because I'm paying for what I don't use I can't afford to use dynamic cast that used to say throw an exception but it applies to dynamic cast because I can write in a more efficient one by hand so here's a code review what do you think of this code anyone say again should use a reference yeah okay but this is supposed to work with pointers too right so if you want it to be null there was another comment in the front no what's wrong with the code even using pointers static cast because it's a downcast emoji pedia does not say that this is called downcast is just called it's sad face but it should be called downcast because that works better for this talk wait that's an uncheck downcast nobody would ever do that right how many of you not not doubting yourselves now but how many of you like know somebody like maybe a friend or colleague who might have written something like this once before keep your hands up if it was you know just kidding if you were in the talk by Joe and Shane on Wednesday and thank you Joe and Shane for letting me use this slide they were talking about a different part of the security vulnerability landscape well one of the things that we've discovered over that I've learned over the last five years or so and I've been thinking about this particular problem for the last five years or trying to make dynamic cast more affordable because we hear about bounds problems as a source of security vulnerabilities buffer overruns and so we should do something about that we did we standardized spam so all don't use part of my take always use span it pretty much solves bounds safety and things like don't reinterpret cast to a larger type which also is a way of going over bounds and some of these vulnerabilities are about use after free so see my previous talks see the the lifetime profile which is now being up streamed into clang and has already found bugs in chromium and a low VM Thank You gob or and Mattias for your help there Kyle Neil and those who have helped with that but I'm gonna focus on a different part of this chart now notice type confusion type confusion vulnerabilities now these are security exploitable problems that can cause an attacker whether a board kid or a nation-state to use your machine for things you did not want and to do bad things right you could hold it hostage they can sit there for a long time all this stuff that blackhat conferences enjoy talking about type confusion in particular include several things but one of them is downcast from base to derived accept guess what it wasn't a derived hahaha isn't that funny well no it's not when an attacker uses that to cause an exploit and that damages the reputation and the security in fact of C++ that does harm to our reputation but also harm to our use of C++ in fact and here you'll see that in the middle of the screen that's not an it's a not insignificant part not all of those hundred and fifty two vulnerabilities known vulnerabilities that were identified in the last two years there were type confusion where static downcast but a good chunk of them were we're in fact what happened was going back a couple of slides in fact that is exactly what the code did the vulnerability was caused large class of those by doing essentially a static cast downcast why would otherwise smart people do that well as shown in the the smaller sub bullets I found as I talked to these people a couple of patterns the very first one sounds a lot like the exception handling one well I can't use the batma cache because our TTI isn't enabled because it's too expensive just turning on our TC I even if I never use it even if I never use type ID or I never use the dynamic cast bloats my binary size and look here my numbers I can prove it in fact our TTI is essentially banned in Windows for that reason exception handling is being allowed more about our TTI's Bend that largely the second category was a people who did have our TTI enabled so they already had the binary bloat and of their executable so that didn't bother them but they didn't use dynamic cast because dynamic cast itself was too expensive and I know some of you will say but we know that Microsoft's implementation does string comparisons yeah we're fixing that but it wasn't just that it's also on other implementations that people were doing the same thing and saying no I can't use dynamic cast because it's too expensive and then the many of them did say the same thing at the end which was and we tested so we knew the down cast was safe remember we're having this conversation because this was an exploited security vulnerability no it wasn't safe you may have tested but it was still a bug oh and by the way if you look at the ISO CDP survey from this year about a third of projects ban exception except been RCT I in whole or in parts of their projects so the fragmentation is not as great as with exception handling but it's still very significant it's this is not a trivial part of the community and again if you bucket these by dynamic cast a virtual Curie like a comm query interface or just something custom like a type tag that people often invent when they can't do their own dynamic house and you say how how many of you are allowed to use each of these all of the non-standard methods like calm and custom type tag I rolled by hand last night are allowed everywhere only a little less than an amicus they're widely permitted and the worst part this is the place where this is worse than the story with exception handling remember with exception handling a great measure of the fracturing of the community was that more than 10% of projects Bend each one of them like every one of them was banned by 10% or more of projects with our TTI it's 20 percent or more every single one of these is banned in 20 percent or more projects this is an even greater fracturing so again this violates it's because our TTI violates the zero overhead principle in two ways people say I can't afford to enable it because just turning it on and curse face overhead and then they say well even if it's enabled I can't afford to call dynamic cast because I can do better by hand and that violates don't pay for what you don't use and it violates when you do use it you can't write it better by hand reasonably again it's the Z overhead principle the language violates it and so users vote with their feet and say no we're not doing that so what do we do type information is not bad I like type ID and type info what I don't like is that all of their information gets baked into my executable whether I ever look at it or not I want it when I want it so again if your problem is dynamic that doesn't mean that your solution is to use something static but it's a good place to start so let's start there prefer static type information the first observation is once we have static reflection which is making its way through the sanitization process and perhaps a few years from now it will become more commonly available we don't need runtime type information at all it is totally subsumed totally obsoleted by static reflection which gives you much more information than the runtime one does and before you say yeah but the runtime one is available a runtime if I need it what can you do with any static information store it somewhere and it's dynamic information and in fact as soon as you say yes as soon as we have static reflection you can easily create very pretty simply create your own runtime dynamic reflection in fact I did not know this until his talk but Andrew Sutton and his talk on Wednesday mentioned that yeah in the last two weeks he wrote one of those because once you have the reflection and he owns through the one of the remains or reflection prototypes what you can do this once you have the reflection information all you need to do is store it somewhere and now it's runtime available but you only get the things stored at runtime that you asked for a compile time so you only pay for what you use what you actually use so the proposal to solve this part of the problem is I believe we should adopt static reflection and compile time programming both of which are already in progress and then we could just get rid of type ID and type info but I think type ID and type in for not bad like type info can give you a hash that that's useful but make them constable make them available only a compile-time and if you really want the value at runtime store it so the second part is dynamic cast it's also great when you need it but that Emma cast is really powerful they can croc cast across inheritance hierarchies to sibling that are bases that are under otherwise unrelated but happen to be inherited by the same derive type it can navigate virtual base classes with ease and do backflips at the same time it's also super expensive because it can do all those things and if you ask for a dynamic cast you are asking for the ability to do all of that just like we tell people if all you need is the lower bound the the lowest value in an STL container that has this value don't ask for the equal range which gives you the lower and the upper you're asking for more work it'll be slower same thing with dynamic cast if all you need is a down cast don't ask for a dynamic cast it'll be slower because you're asking for a more work there's a sweet spot I think and this actually hits all of the type all of these type vulnerabilities that I have seen that we're root caused by not using dynamic cast if you are using need a down cast that doesn't cross a virtual inheritance link so they can be virtual inheritance above or below you but you're not crossing the in between the basin drive there isn't a virtual inheritance Lane turns out we can implement that super efficiently and here I'm going to present other people's work not my own my contribution was just to say hmm seems like that should be able to be done faster and then other people pointed out yes we've been doing it here our results and so I'm pleased to report their work so here is work done by many people but particularly by Peter collingborne costea and Joe and Jim and I'm going to talk about the windows cast guard but it is derived from clang CFI so even though I'm giving the windows numbers kudos to the clang folks to Peter cost you to everybody who worked on the clang CFI it requires it's it's super compatible you don't have to change the ABI have to change the tables doesn't use our TTI everything I'm about to say works with /gr - on Windows with no type ID type info support it you do a simple arrange check you saw the code earlier the way it works is the super smart insight and I think the primary person here is Peter calling born for this insight thank you Peter is that if you take your v tables and you don't change them but you arrange them in memory in a certain order then if you are given dynamic pointers to two objects you can look at their v pointers and know just by doing a rain check on the V pointers if the if the hopefully derived objects V pointer is it a certain range is in the range of all the V tables of types derived from that that base with a simple bounds check on the V pointer which is constant time and requires in general two memory accesses one of which is likely already in cache you can compute in five instructions and two memory accesses whether the downcast succeeds with perfect type safety and fidelity this is a very important result now it's intra do L but so it doesn't work across dll's the preliminary results on trying this with spec and with Windows like is that there is very low impact negligible impact on binary size and on runtime the highest overhead we have measured so far that Joe has measured so far one of the windows is largest dll's is 1.5 percent bigger because of checks performed and function calls injected checks because the current one all it's doing is saying oh I noticed you're doing a static downcast I'm going to inject a check for you so you don't actually change the code to down underscore cast it's just noticing you're doing a static downcast and injecting a chapter but that 1.5 percent is not compared to our TTI it's compared to doing nothing at all it's compared to static cast because our TTI isn't even allowed in Windows because of the binary blow and the dynamic cast I should say in particular so the proposal is for standardization that I would like to propose is to provide a way to ask for only what you want cast down from base to derived not across a virtual inheritance link and then I can know at compile time give you an error if you don't do it because I can statically know whether you're obeying whether you're in that sweet spot but if it compiles is guaranteed to be five instructions or so which is way different than today and you don't pay unless you use it and you cannot write it better by hand for example Joe did a test for a simple single inheritance hierarchy where he instrumented dynamic cast and we can about whether it's the best possible that Emma cast but this is just actual data from the field and he looked and did an instruction trace so he's not this isn't source code this is going through just the instructions actually executed and that I am a cast was not this not this not this but there it was that much that many instructions the cast guard version was this much code most of which is comments and I'll let you see it a bit better now so the first two instructions so the first half of that which is all comments and two instructions as you see in the comments is a null pointer check which we would have already done with static cast anyway so that is not overhead compared to static cast so you can already ignore the top half of this what remains is five instructions where I load to the pointers basically and that you arrange check I do a sub comp that's it if we can standardize a down cast that people can use then we're in a world where there is no valid reason not to use the standard cast that is type safe and is zero overhead don't pay if you don't use and you cannot do better by hand so this talk has been largely about fixing things fixing exception handling in our TTI sometimes it's a bit about adding a knob buts adding a knob like throws to an existing thing it's not like inventing a new thing like concepts which is the add things bucket but by fixing things we can simplify the programming model and the language and I'd like to suggest that it's the right hand side is a future worth considering if we inverted this pyramid so that in the future we had much more emphasis on simplifying the C++ we have and also on fixing the C++ we have in ways like I talked about today is that something that you would like to see happen with C + S evolution so we're gonna try and I'm I will try to convince more people than especially the simplify part is worth doing remember a really good rule of thumb static by default dynamic by opt-in that gets you to don't pay for what you don't use and when you do use it it's as efficient as you could write by hand that is c plus plus that is c plus plus ik so to say and like it says in the top right our historical sitting strength is static and every time that we have added a feature in the last 20 years that has markedly improved the language nearly always it has been doubling down on our static nature concepts for static typing templates for static polymorphism and now static exception typing static our TTI with reflection so let's make exceptions in our TTI more affordable and usable let's actually do work to fix c++ it's no wonder that the only two features in the standard that are not zero overhead are the ones that every compiler has a switch to turn off people are voting with their feet let's fix it and unfragmented e and then also besides fixing things as we go into the future and in later talks in later years and later cpp cons and other conferences i would love to see more talks more proposals about actually aggressively simplifying the language that we have today to get rid of pitfalls get rid of corner cases and the more we can do that the more we can benefit c++ now and in the future thank you I know you John I know you want to make some announcements do we have any time for questions take two all right we have I'll take what over here because I know who you are and you're on the committee so I'm gonna I'm sorry I'm gonna down vote you because I can only do so sorry Peter I'll buy you a beer later places and you said we have to be conditional he also said well the future new code would either be annotated no accept or the throat attribute would it mean that the library also needs a conditional throws so the conditional so the conditional no accept is a conditional throws in that world so if we are in a world where we're teaching people for new code right exactly one of no acceptor throws there's no room for conditional no accept to compute it if you can compute it as a simple example if you're using a vector instantiated with an alligator you know doesn't throw then you know accept your conditional no accept will become no accept true otherwise no accept false would be equivalent to throws that's the idea yeah I see what you mean so certainly in a world where we didn't have the existing dynamic exceptions at all what I just said would be true but but you are correct that in the conditional case you want to say where if it's not if it's not true if it is true which one is it say yes you're correct you would need in that world a conditional throws as well I think I have that in the paper I I like to live in the future and fast forward to the world where it's just one or the other but you're right can we do one at a time sorry thank you very much for your question but we only have time for one or two more please you said there were some Co bases that bandage and there were even more where you were not allowed to use our TTI but that confused me because I thought exceptions relied on my TTI so if you look at first of all if you look at the numbers I if I said it backwards I apologize the actual data on the slide showed that there were it was like two thirds of projects enabled our TTI whereas only it was only about half of exceptions so there is more our TTI use in the wild there is less banding of our TTI then of exceptions however yes you are correct that exception handling does require our TTI so that that order is natural the the thing that however is the compiler can generate a dependency where you must have our TTI to enable exception handling and that's common theoretically a compiler could also just implement exception handling with just enough AR TTI to do the void star to base cast to base star cast yes I'm gonna cheat one more but then we have to go sorry God thank you so one thing that came to my mind is and are you worried about that these two words of information that you can Conway using STD arrow like most of the languages that use modern languages that use error codes usually have some kind of type where you can put in anything and um are you worried about that if they propagate they would it would be hard for the programmer to always choose different codes and be sure to handle the right arrow so it's a good question that will have a long answer the short answer to take a look at that section of the paper and also the related papers like Nile Douglass's in particular that talk about the error code the short answer is the error code is two words because one of them is like the payload usually an integer but it could also be a pointer to a dynamic exception if you're wrapping one of those and the other one is a discrete is a category which is usually a void star to a singleton the to the category of air that can represent any conceivable air in posix in Windows in carbon and calm and all of those things and allow customization so including as I mentioned it can wrap a dynamic exception which is how we can go from from if we call a function that throws a dynamic exception and we're a throws function a new style value throwing function we can wrap the dynamic exception in a stood air and then propagated with bit blit so that's the short answer but please see the paper for the answer thank you thank you again and I hope you enjoy the rest of what John has to say thank you John [Applause]
Info
Channel: CppCon
Views: 71,487
Rating: 4.8583269 out of 5
Keywords: Herb Sutter, CppCon 2019, Computer Science (Field), + C (Programming Language), Bash Films, conference video recording services, conference recording services, nationwide conference recording services, conference videography services, conference video recording, conference filming services, conference services, conference recording, event videographers, capture presentation slides, record presentation slides, event video recording, video services
Id: ARYP83yNAWk
Channel Id: undefined
Length: 93min 12sec (5592 seconds)
Published: Mon Sep 23 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.