CppCon 2018: Andrei Alexandrescu “Expect the expected”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

There is one small detail I do not like. expected should not throw on *result. I think it should be unchecked to be consistent with optional and raw pointers. Maybe a result.get_value() or similar should be more appropiate for the checked case.

👍︎︎ 1 👤︎︎ u/germandiago 📅︎︎ Nov 13 2018 🗫︎ replies
Captions
- Well, it's kind of, it's funny that it was unexpected which segues into the title of this talk. So, what happened was I was in St. Petersburg at a conference and I was walking with the organizers back from the restaurant; it was like 12 AM in St. Petersburg. And we walked down the street at 12 AM and, you know, I naturally ask, "well, is this safe? Are we safe here? What's going on?" And he's "ah, yeah, it's safe. It's good. "It's totally safe, totally fine." Like, "Well, how about all those videos I see, "you know, with, you know, the guys who "throw axes at each other?" "Ah, never happened." "How about the woman that carries a drill on her back?" "Ah, never happened." "How about the train thing that, "thing that crosses the street and stuff?" Like, "Ah, it's just like, maybe once." (crowd laughs) Now I'm okay. Cool. So, okay, this is not a joke. It's a story. It's a true story. So, three minutes later. So we walk down the street and three minutes later there's a fender bender, like maybe, I don't know, 20 meters from, from us. Sorry, this is America. 66 feet. (crowd laughs) Okay? 66 feet from us and a half. So it's like, there's a fender bender and we see in real time, four guys jumping out of one car. And three guys jumping from the other car. And without any introductions, they start punching each other in the face. This is God's honest truth. They start punching each other in the face, like. But, you know, that's funny. There was no, it was like choreography. Cause they like jump out of the cars in like, in a ballet motion, you know, they start, you know, they start hitting each other. Like, how about like, "Come get a piece of me." All that trash talk. There's no trash talk, right? So, they just started. It was like on a cue they started punching each other in the face. So now, it turned out by some happenstance, it turns out I was the tallest and the biggest guy in there. You know, there are like two more guys and two women. So, it so happens that I was the tallest, the biggest guy; so the women hid behind me. And what I said was, "don't worry, you're with me." What went on in my mind at the time was, "Oh my god! They're punching each other in the face!" And what I should have said was, "Young lady, you should know I've never "thrown a punch in my life. "So if you ever threw a punch at your brother "when you were five, you're more qualified than me." (crowd laughs) "And by the way, the 20 pounds I have over these two other guys, that's not muscle." So, anyhow. So, I do recommend visiting St. Petersburg in spite of the story, I think it's a great city. And it's actually, you think, "okay, what? So it's a city, right?" So, it's a nice city somewhere in Europe. You go there, you know, 30 minutes later. So I got this guide and 30 minutes later I was like, "Oh my god! This is awesome! "So much good stuff here!" Which brings us to Expected. Alright, so, today we're gonna do a little experiment. Which would be reinventing exceptions. And this project started-- actually, let me, let's actually discuss exceptions a bit. As a means to handle errors and then we're going to get into expected. So, most of us, I remember when I learned about trying catch, it was the days when the syntax coloring for the first time in editors. So, like, you type trying it becomes blue. That was amazing, remember? Like, "oh my god, it's blue! This is awesome!" "I'm gonna try and catch in every single function I write!" So I was very happy with using Trying Catch. I didn't think about it critically, right? So, then there's all of that, you know, all of the ordeals that the C++ committee went through to discover it's way around exceptions. Right? So, most of us don't do much in criticals so here's, you know, Trying Catch, and you just use it. Have a good day and everything. But, you know, we don't know what, you know, what's, what are their goals and how do they compare against Ultimate of Missover handling. What were their intended use cases? How does their semantics support what we want to get done? And what are the consequences of the new set up of the world? So, most importantly, how do we write code that is a nice exception, you know, compliant if you wish? Well, there are so many means to choose from. Right? Well, you know, there's a new one too. Yes? Herb Sander's proposal? Alright, raise your hand, I'm not seeing anything but raise your hand, please, if you've heard of it. Okay, I see nothing, but, you know, thank you. You should, if this was, like, 30 years ago and everybody smoked, you'd be, like, you know, raise your highlight, you know, your lighter, there, and. Anyhow, so, essentially, what's funny about exceptions and error handling in general is that, it's the, the meaning of what's exceptional and what's not. Depends a lot on the context. The same issue may be a completely expected matter in one set up and something that is completely not allowed in another context. Right? So, we want to learn once and use many. We want to learn one mechanism and use it many times for error handling in general. We want to minimize what's called soft errors and maximize hard errors. So, soft error would be an error that doesn't get detected immediately. Right? So it's a, what would be an example of a soft error? Please shout. (man shouts inaudibly) I didn't hear that but I assume that he has said dangling pointer. (crowd laughs) Did I? Was I? Not even close? Okay. (crowd laughs) File not found. Okay, so we have a dangling pointer error and it's gonna, sometimes the application keeps on trudging along and it kind of almost works. Right? So that would be a soft error. And a hard error is a, it's detected immediately upon, upon it happening. So, that's what we're looking for, right? We don't want those metastable states in which, essentially all of the guarantees provided by the system are gone. Right? We don't want that. And then, essentially we have no guarantees. So it's kind of the application is like, really like on skits there, right? Not nice. Well, we also want to do things like centralized handling but also local handling. We want everything and we want to pay nothing for it. This is C++, c'mon. Right? We want to try whenever I want local, I want it to be nice but, you know, when I don't care about local I want the centralized to be nice and you go design that. Right? And, by the way, I want it cheap. Right? So, these are issues, right? These are kind of difficult, difficult design issues for our program language. We want to be able to transport from wherever the error happened to wherever the error is handled however much information we want. For example if it's an out of memory error, we want to transport four gigabytes of memory. (crowd laughs) Yeah, thank you. That was a joke, yes. Thanks for laughing, yes. Thank you very much. So, we want little cost and a normal path. There's gonna be more about this later. And we want to make correct code easy to write and incorrect code difficult to write. So, starting with a simple example, which is actually, will live in infamy; atoi. If you google for atoi, I think the first hit is like, "avoid atoi." Why? Because it returns an int. So what? If it returns, if atoi encounters any error, btoi doesn't look like an integer. Or maybe it's too big of an integer and doesn't fit. It's going to return the most unlikely integer ever. Zero. (crowd laughs) So, then it returns zero and they're like, "uh-huh, this could be a legit zero. "Or it could be an error; so I gotta check." So then what you do is, maybe use a regex. Which is like, you know, just like one or 56 times more complicated than atoi. So, you're better off just re-implementing the god damn atoi yourself. (crowd laughs) Right? Not nice. And the problem with atoi is illustrative because atoi covers all of the values of an int. It's a surjection, they call it. So every int is covered by atoi. There is some, there's a string that represents every int; there's no "null int." Right? There's no int that doesn't exist. Right? With flow, there is a flow that doesn't exist. It's called "not a number" right? But with int we don't have that luxury. So, not nice. So what do we do about this kind of stuff, right? Well, there's always errno. Which, by the way, atoi is not guaranteed to set. (crowd laughs) Did you know that? I read it. So I gotta be prepared for this stuff so I can make fun of everybody right? So, with errno, it's a whenever something goes wrong it's eternal and pray that somebody's gonna check it at some point in the lifetime of the application. It's going to be fairly general, it's going to minimize soft errors, no. Because where was the last time it checked the result of print f? (crowd chuckles) It could fail! I mean, come on, right? Invalid file handling and whatever, right? But nobody checks print f. So, we want to have centralized error handling, which errno supports nicely because it's centralized and we want to support local handling, which, it kind of supports. But we can't transport an arbitrary amount of error, it's just an integer. And, essentially, you know, the worst of errno, which is, you gotta make sure you allocate the values in the integer appropriately so it don't step over over the operating system's reserved values and all that nonsense, right? And all the headers and all the stuff, it's in an industry there. You know, I sell errno values for cheap! Right? So, not nice. And, you know, errno is not famous for making correct code easy to write. You know, so it's not the best choice. So, there's a lot of red on that slide is all I'm saying. Well, there would be a special value scenario which is coming along in this nice, oh, I have this fancy thing here. This nice strtol long function; which takes a pointer to character and a pointer to, a pointer trick character. Which is going to be filled with wherever the conversion stopped. Right? So, it's filling the, it's giving information about where the conversion stopped. So then you gotta kind of track back and try to do things and, you know, figure out what the hell happened and that kind of stuff. It's kind of difficult to distinguish things like there was an overflow versus it wasn't a number. So, it's kind of not nice. And r would be the radix. So, not very cool. So, it's not very general, it's not very nice. Let me point out, let me kind of insist a bit on the returning especially in here. Like, we have an atoi; it's returning a special value which is zero. The special value thing would work if you return pointers. Because there is one pointer that is unlike any other pointer. And that would be? (crowd responds) Thank you very much, the null pointer. Right? So there's a singular pointer that doesn't point to any value, et cetera, et cetera. So it could actually, at the minimum, say well there's a single one, there's a singular value of the point I'm on. We can't do that. But for most other values you can't. If it's a date, you can't really return. Actually, you could if you really wanted. But it's not general, there's no generality there, right? So, it's not gonna minimize soft errors and it's not gonna do good centralized handling. It's only good for local handling. So, not very nice. So, we have this unpleasant situation, too. So, now, let's actually design together, a system that would work. And I'm thinking, you know, thinking naively, or just, you know, again, like, clean the slate. You know, clear up your mind. Take a yoga pose right now, okay? So, just think of, "how would I design atoi "if I had, like, complete freedom "and I could design my own language, "invent keywords, and whatnot?" So all those sound like, "well, "there must be some type that "represents there's an invalid input going on." And then I'd say, "well, atoi returns." There's an order, you see? It's some sort of an option, you know, it's kind of, well, it's either an int or it's an invalid input. Right? But, like, this is not C++, right? I mean, I don't need to tell you. Right? It's kind of, I have a dream. Right? So then, we have, we invent a keyword because we can. Typeswitch and depending on the result of atoi, we're going to say, "well, if it was an int, "I'm going to handle the happy case. "And if it was an invalid input error, "I'm going to handle the unhappy case." And some languages do quite that, actually. Right? So, that's a nice thing. (clears throat) Awesome. Well, there's something; there is a rub here, however. These two types are not, um, symmetric. They don't, you know, here looks like, well it's an int or invalid input and it looks like the branches have equal weight. See what I'm saying? Rhetorical question, I know, because, like, again, I can't see nothing, right? So, um, rhetorical, you see what I'm saying. So, it's either an int or this guy but they have, sort of, equal roles except the int comes first. But, what I'm looking for is some sort of I'm expecting it so it's a probability thing, almost. Friends, right? Well, I expect an int. Or I don't expect a punch in the face, right? I don't there, it's something rare. Statistically speaking and, you know, kind of, correctness, whatever speaking. So that would be, that would convey the fact that these two, these two types have an ability and a symmetry as far as the function atoi is concerned. Right? Right? Thank you, thank you, I hear, I heard a yes. Thank you. So, local code should just simply just blow through it and go with a happy case. Just, you know, I'm gonna just go happy case and if anything bad happens, you know, u-type system, please take care of me. Right? That would be the socialist-type system, as it were. Right? It takes care of you if you do something wrong, no problem. Right? I told you I'm gonna do a political joke. Okay. So, great. So, now we have a function that has, like, an over-return type and one or more covert return types because many things could go wrong. But only one thing could go right, as it were, right? So the question is, okay, well how about those unexpected things; where do they go? Well, you know when you call main there's gonna be function call and there's a stack there and very nice and you gotta return out the stack because there's no other point you can return. Cause that's the execution, you know, that's the trace you got. You can't go to, kind of, in a new place. That's not on the call-stack. So, it's gotta be on the call-stack. You see, I feel like a con-man here because I'm gonna con you into, like, we're gonna go, well, in we invented exceptions. There's no, you know, there's no output. There's no chance we don't do that. Right? But let me, let me con you. Right? So what, there's gotta be a return for the, these unexpected things. And certainly, there must be some, you know, some handles placed somewhere in the invocation stack that are going to take care of this unexpected things. Right? Thank you for the yes, and thank you for the yes. I heard no "no." So now, the callers mark must plan some handling spots in the execution path and, you know, the covert returns, kind of, jump automatically to those handling spots. And guess what we just invented? Exceptions, friends. Thundering applause right now but, you know, let's keep that because that was happening, like, 30 years ago, right? So, awesome. We just, you know, by essentials, we didn't invent much as acknowledge situations. And the situation leads you to this thing. Well, you gotta put some handles on the call-stack because the call-stack is all you've got in terms of flow, right? I mean, you gotta go somewhere on the call-stack. And, therefore, you have all these tries and catches that plant, well, exception handlers. So, well, let's see how exceptions go as far as respecting our desires. Well, they have, there's a lot of blue. There's a lot of blue. They're pretty general. Well, there's a question mark there we're gonna discuss a bit more though. But they do centralized handling awesomely. You put in main, you put the try, you put the catch, you catch everybody sees right there. You're good. Right? Don't forget to catch my reference. So, then you have a, you know, you have an arbitrary amount of error information; you can put anything in exception if you wish. No problem at all. There's little cost on the normal path, we can talk about that more. But they don't do local handling very well. Like, if I want, like, call a function and get the error or whatever, I gotta write, like, five lines of code, which is ridiculous. Right? Agreed? That's kind of the poor case, the poor scenario in which, "well, I gotta try this right now "and do a try and catch." It's a very heavy syntax for achieving a simple thing. And, as far as making correct code, you know, easy to write, it depends quite literally on the year you're asking. Alright, so, you know, skipping back to '98; yay, we got exceptions and they are blue too! That's amazing, that syntax color! Try Catch, all that stuff, throw! It's all blue! But, you know, there's the Tom Cargill article, I'm sure, those of you who can, you know, who have the age, can remember it. And there's, like, a lot of churn about what's going on with exceptions and there's like, you know, difficulties with the simplest transactional codes. So, 2008 was kind of the Dark Age of exceptions, if you wish, in C++. I think that's where the market failed. Quite bad. I think there must have been some causation there. And nowadays there's a big maybe. Because, you know, as a Charley Bey, a famous Boost contributor, mentioned in a report on Boost there, he said, "error handling "idioms and practices do remain contentious "and confusing within the community." There's only one way that everybody likes and recommends. Right? So, well, today we're gonna, we're gonna introduce one more means of handling errors. As if there are not enough. You know, the joke with the standards? I like about standards that there are so many to choose from. Same way about error handling in C++. Now we have, like, error codes, we have errno, we have exceptions, we have Herb's proposal, which is pretty much, uh; kind of, it's a special value thread that through automatically. And, you in the room that's ready to change history, right now, friends? (crowd laughs) So, this is what's gonna happen right now. So, let's recap what the issues we have with exceptions. We have this whole Metastable states, which is, I like fancy words, and this is, this is my sin here in saying Metastable. All that stuff is like an application that went off the rails, my friends. Right? It's just, "oh my god, no body knows "what happens next." As I'm sure you know, user must ensure the transactional semantics by hand. And we have, you know, we have destructors, we have ScopeGuard as possible solutions. We have the whole local error handling issue, which is unpleasant and inefficient. By the way, plenty like, make a quick parenthesis right now. You should know that just enable exceptions in your project is going to make it slower. Even if you don't use exceptions. Thank you for the nod, Andrei. No, there's a guy Andrei, it's not, There's actually a person called Andrei who's nodding yes. I do see the first row, I gotta confess. The thing is, with exceptions enabled, the compiler must generate the entry and the exit sequence for each function a bit differently and more, you know, bulky. It's more bulky. So, again, we've seen at Facebook, I remember we've seen things like, well, seven percent. And seven percent could be, like, quite a lot of power. Right? So, not nice. And people have measured. Like, it could be, like, two percent, could be, like, unmeasurable. But it could be, you know, quite a few percent. So, you know, when they say zero overhead exception handling, right? That's not really zero. It's a floating point number. It's just kind of, you know, a bit more than zero. Right? So, anyway, parenthesis closed. So now, you know, the whole exceptions and Herb Sander has this famous examples with like, a five line function that could go through, like, five million exceptions, you know, from five million different paths and all that good stuff. So, it's kind of a difficult proposition right now. And the, the least nice of all is that composition with exceptions is tenuous. It's really, like, bad to do. Well, I have two exceptions and I want to collect them in a vector of exceptions and you can't. All you are, one exception can be active at any moment. But you know I'm lying, right? Because more than one exception can be in flight if you throw within a catch handler. Because the exception is not handled unless, you know, until the catch is done. So if you have a catch and inside you call a function and it has another exception and stuff, there are several exceptions active. And that led to things like, you know, on-code exceptions as opposed to the useless on-code exception. Right? Right? Raise your hand. Alright, alright, thank you. On-code exceptions. So, you know, that leads us to, welcome to my talk. We are just starting right now. So, we're going to do with exceptions, we're going to do local handling, we're going to do minimize soft errors, and we're going to do improvements on making correct codes easier to write. But we must start with a few background items. Background item number one, is my Wikipedia page. Yes, I have a Wikipedia page. And I have more United miles than you have. But I don't fly United anymore, by the way, it's just that they, they punch in the face, right? I don't want that. So, I quit. So, I was visiting my Wikipedia page which I never contribute to, I just let other people, kind of, destroy me. So, I was watching as, there's this change at some point. I really, I literally visit every year, a couple of times. I don't follow. But anyway, at some point, there was somebody who deleted a lot of stuff. And what they did was they added something. And it was Andrei is accredited with creating the expected type for C++ which is being standardized. And I was like, "huh, I should check that out!" (crowd laughs) Honest to God, I had no idea. So what really happened was, like, back in the day, a few years ago, I had this idea, which I gave a talk about that. C++ and beyond seminar. And a couple of guys thought, "oh, that's pretty cool so let's standardize that." So, the rest, as they say, is history. The second background piece (clears throat) would be std variant. Or boost variant which is the perfect implementation engine for, remember, we had the overt and the covert type. We have the expected and the unexpected. It seems like they could live in a variant. Agreed? Right? It looks like this variant is the perfect place to store such a thing which can be one of two things but never simultaneously. Right? So now, we have, kind of something that's really close to what we need which is std optional. Which is either something or nothing. But we don't want something or nothing, we want something or something else; which would be the exception. So, it's closed but just, you know, related but not really it. And we can go to other languages and does the maybe/ either monads which also are nearly there but not quite yet. Not quite there, right? So, everything of this, all of this is for us to anchor in our mind the devices that we're going to use for implementing this. There's one more; which is also, like, a funny, kind of very related. Promise and future in C++, yes? Promise and future are mechanisms for transporting things and exceptions and whatnot from one thread to another. So they focus on the threading aspect. And again, they are very close to what we need. So, I'm not gonna cease too much on this union types and then stuff. But it's got a variable for illustrations. So, we're going to have a discriminated union type which is pretty much one of two types. But you're u and we have the class either, let's call it. And the union is going to store one or the other in the boolion. The boolion, sadly, is going to cost us a whole word. Cause of alignment, right? So, it's going to be like the maxing of t and u plus alignment plus the boolion and that kind of stuff. Fine. So, let's define the type. Std expected. Which takes a t and an e. And the t is the expected type and the e, well, it should have been like e and u. Yeah, should be like e, expected, u, unexpected; but it's t and e, sorry. (crowd laughs) So, you know, whatever. You're smart. I mean, you're the best in the world, right? So, t would be the expected type and u would be the error. You know, the unexpected type. Which is kind of the bad guy in the story. And once you expose this union above the overt and covert type and in the nice, happy case, there's a valid t in there. And in our happy case, an e is there explaining why t could not exist. Kind of, I think it's neat, really. If at this point you think it's not neat, please go to another talk! No, you're gonna hate the rest. So, it's either a t or "honey, I can explain." Yes? I can explain. Why the t could not be produced. See a proposal by Vicente and, you know what JF stands for? It's Jean-Francois. But, nobody can pronounce his name. So, he said, "I'm gonna just call myself JF. "Stupid American." (crowd laughs) I can make fun! I can make fun of Americans and Europeans, I have double citizenship! (crowd chuckles) I have a Romanian citizenship, which according, at least to some sources, is Europe and I have American citizenship; I'm good, I can criticize! I'm one-eighth Greek; I can criticize Greeks! They have big nose! Right? I have no mercy. Equal-opportunity offender, okay? So don't get, don't come near me, okay? So, these folks took my idea and they actually went through the ordeal of making it into a proposal. The proposal is making nice progress; I am going to discuss it. By the way, Jean-Francois, please stand up! Oh, of course, he's the last seat in the back! (crowd laughs) Of course! And I told him, I'm gonna stand him up! Thank you very much! Give him a hand, please! (crowd clapping) Thank you. Alright, so he got the aura, the light on his head. And he's like a mystical event. So, their proposal goes as follows. So, we want to unify the local and centralized error handling by the following means: you're going to return and std expected of, for example, int, and some error type. And that would be the result of, let's say, atoi ++. Right? So, if you're local you're going to look, well, result has value or not? Say again, this is gonna be a boolian if, that tells you whether it has a value or not. And the simplest idiom that you can use for local error handling is if result use star result. Right? And that would mean, even though it's not the pointer, they expect it behaves like a pointer. You can test it with f and you can actually be referenced it can use the arrow. And so, it's kind of a pseudo-pointer, if you wish. Now, if centralized, you just use result dot value. There's a mistake on the slide. You should use result dot value. Because, in it's infinite wisdom, the standardization committee said "if somebody's going to use star result, "and the value is not happy, undefined behavior." Because, apparently there's not enough of it in the C++ language. (crowd laughs) I'm hearing some laughter, it's not the laughter that I was hoping for. It's like, "hahaha, yeah I know, "but I'm gonna kill you after this." Because there's many committee members in this room and there's many, you know, many folks who are pro- the committee members, so let me criticize. There is enough undefined behavior in C++ and you should not have done that. You feel the tension? (crowd laughs) It's like really nobody see. Alright, so anyway. Essentially, it's like this. If you say result dot value, it's going to throw the exception if it's not good. If you say star the result, undefined behavior. Which, actually, Jean-Francois was very smart to say, "you know what, if it's undefined, "on a good implementation, may as well "throw the damn e." (crowd laughs) Right? And it's all good in the world. Thank you! Alright, okay, this is awesome. I got some good golf-clap. "Oh yeah, okay, that was okay." Thank you. Good! Well, let's look at some characteristics of expected t and I'm gonna actually go a bit into the implementation. So, it associates-- this is the most beautiful thing because it's like, in locking it's good to associate the new text with the directs protecting and that kind of stuff so this sort of encapsulation is very, historically, it's very productive. And very, kind of good. Like, you know, motherhood; apple pie. So it associates the errors, the computational goals that I want to produce with whatever else may be created in the process of attempting that production. So they're encapsulated together in this expected thing, which is beautiful; I like it. It naturally allows multiple exceptions because the exception is produced but not thrown. This is really the key to understanding this whole idiom; which is, "wait a second, I'm actually creating "the exception without constructing whatever." So the exception exists but it is not yet thrown; it's created now, thrown later, if at all. So it can actually do error handling with exceptions without throwing exceptions. That would have been, actually, a great moment for the golf-clap that was before but, you know, anyway. So, this is the awesomeness of it, yeah, you can actually, yeah. So now we can have a million exceptions if we wanted. We could have a vector of exceptions or you could have std map of mapping strings or whatever you want. Right? And then, you know, we can transport them across threads; no throw, whatever, across time, save enough for it later, et cetera, et cetera, et cetera. Collect group, combine exceptions. Actually, compose programs that use exceptions. Because you can't compose by throwing. You can't build by destroying. It's basic, right? It's basic I don't know what. Right? Okay. So, let's look at the implementation. (clears throat) So, I have a, as expected, okay that was an unintended pun. We have a template with two arguments, t and e and we have a union containing a t and an e, yay and nay. And we have a boolion, okay, which we initialize the optimistic way. We go with the notion that, by default, they expect that it's going to happen. I mean, even the vocabulary goes that way. Very nice. The focus structure is going to create a default initialized t because t is expected, friends. Thank you very much, right? Of course. There's been, actually, debate about this. A default construct is expected, said the pessimistic people. I'm not sure if it should contain a t because I didn't tell it to create a t. (mumbles) And now we have, the construct that takes a const t, of course it's going to copy the t into the thing. Well, now there's a third constructor here. (speaker trails off) Okay, I pressed the wrong button. Okay. So, I have the third constructor here, which is expected in terms of const unexpected of e, reference rhs. Where did that unexpected come from and what is it doing? Any, if you have an idea, you can raise your hand. I'm not gonna call you out but raise your hand if you have an idea why that could be the case? Why do I need a wrapper around the e type that's called unexpected? I see a couple of hands. Awesome! Okay, I have news for everybody else. Here's why. Because you should be able to have a template that is, has a, you expect an int but the error code is also int. Int is a very, like, it's a classic error code. Return a result. So, I could have an, I expect an int but the error code is also an int. So then I need to distinguish between the constructor of the happy case and the constructor of the unhappy case. So I'm going to wrap that integer completely unremarkable type which is called unexpected. But wait, you ask. What if I want to expect the unexpected? (crowd laughs) That's where the paragraph comes and says you can't expect the unexpected; that's illegal use! Bam! By law, it's forbidden. So, that's very nice, they thought of everything. But you've gotta have the unexpected type, it's completely unremarkable and just wraps a type and it has, like, give me that thing; and that's the only interface it has. It's important just to tag the value of e. Or the type of e. Alright. Anything more interesting here, let's take a look. We have the move constructor and everything with universal reference here. I said universal, not forward. I'm not gonna comment anymore about that. We have, kind of a, it's getting more interesting by each slide, so we have, kind of a, okay, so let's copy this thing and if okay, then no need to copy that thing and otherwise I'm gonna copy the other thing. If the move constructor, which is along the same lines. And here's the disaster scenario. Yes, I put in a comment. If you're a good person, you uncomment that line. (crowd chuckles) Yes, please. Please comment that line. Oh, by the way, the argument was efficiency. So, apparently all that work on branch prediction, all that good stuff is not, not good enough. Anyhow, so we have like a number of overloads and all the jazz. I think, actually, in a real, in Jean-Francois' presentation like eight of them, quite a few now have to operate the dereference operator and we have, give me the error please. So that would be, I'm fetching the error. And again, I think it has undefined behavior if there's no error there. And, I have a query. Which tells me, "well, do you have "a value or not?" And is going to simply return whether the value is happy. We have the bool operator which allows us the eve test. And we have the value, give me the value. And this, actually, does throw the exception if it's good. So you know, value's the right thing to do. Agreed? Now, there will be a side discussion that I would like to have. Which is, what's the deal with exceptions? It's not the pointer, but it's, looks, you know, it acts like a pointer. It has the start, has the arrow, and has those pointer-like characteristics. There's a precedent to that. Which is called? (crowd responds softly) Loud. (crowd responds loudly) Now, there should be, like, a choir here; optional! Optional behaves like a, looks like a pointer, behaves, it's actually an optional value, but it looks like a pointer in the sense that it can put star on it, you can put arrow on it, and it can achieve undefined behavior as much as you want. (laughter) There's like any amount of undefined behavior you can get from optional. And, it's a precedent; there's no other type in the standard library that would look like a pointer but not be a pointer. And it's kind of, I would say it's an improve on design. I would say it's, the jury's still out on that one. But, you know, it was already standardized and it was there; so they said, we already made a couple of bad decisions there. How about we implore them to expect it as well? This is the true story. And optional was like the perfect legal precedent that they said, "oh! We have all of this bad things here, "all of these awful things we're doing here. "Oh, you have this proposal? "Please do this thing and expect it, as well. "And we're gonna accept it. "By the way, you have no power to protest." This is not approved yet, probably after this talk, it's never gonna. (crowd laughs loudly) But you can actually, you can actually, you know, call your local representative and tell them, I would like some more defined behavior, if possible. I'm hearing something, I'm gonna ignore it. (crowd laughing) (Andrei clears throat) At any rate, the most interesting function I had to write and actually found a bug in Vicente and Jean-Francois' proposal. Which is the swap function, which is very funny because you must swap two values that may have actual different types because one might be bad and one might be good. So let's go with, looking at swap, I'm going to skip the signature for now, friends. So I'm going to go, see, this like long enable f t with a complex, you know simple, quite complex boolian condition, here. But let's ignore that for a moment. And let's handle the easy cases. Let's handle the easy cases. The easiest case is both are good. If okay and there are just okay, then let's do good values and we're going to swap the yays. With me? Thank you. So, if one is, (Andrei fumbles for words), this is kind of, I'm simplifying my whole life here because here I say, "ah, I'm gonna do this later." You know, I'm swapping the swappees. I'm saying I'm gonna solve this later, you know in this else case, it means the first is good and the second is bad. I am going to say, "oh, let me handle it later, "so I don't duplicate code." Then, if my, me, this is not okay. And rhs not okay, again, I just swap the nays. So that's, again, easy. And else, here comes drag ons. This is the difficult part, the dot, dot, dot here is the difficult part. Because I need to swap, I know that, one object is a t and the other object is an e and I need to swap them really carefully. And I look, it's like two drivers, two cars driving, you know, 100 kilometers, sorry, 60 miles an hour. And they need to kind of jump each other and you know, there's two different drivers in two different cars; it's a difficult proposition. So, let's take a look. And kind of, you know, it takes you a bit of like, you gotta think of it a bit. And it's not easy. So what I did was, "well, let me, "actually, create a type where "I'm going to move from my lane." I'm bad, the other guy's good, right? So, I'm going to move away from nay so I free up my object this. The next thing I'm going to do is, careful here! I still need to call the destructor of nay. Even after I move, you still have the civic duty to call the destructor. For absolutely no reason because nobody's gonna be out of their mind to leave something interesting that needs to be destroyed carefully. But you don't know that. So, being politically correct here, you're going to call the destructor. After move the object is in a destroyable state, et cetera, et cetera, yada, yada, yada; correct? Okay, now we gotta destroy that guy. So, at this point, this, my object, is empty and ready to receive new content. What we're going to do now, fine I'm going to put true in okay, so I'm gonna make it a good object. Right? And I'm going to, sorry, before okay, I'm going to transfer the content of the good object from rhs into this by means of a move constructor and a placement new. I'm sure I'm being followed here, but I'm talking in a vacuum. So, please gimme like, a yes. - [Crowd] Yes! - Okay, Jean-Francois didn't say it. (crowd laughs) I heard you not saying it. So, now, I'm going to, after the move constructor has succeeded then I can safely adjust the flag to true and at this point right after the key goes through, this is in a good state. It has the final state that I'm looking for. After that I'm going to say, "well, let me go on the other side." And at this point rhs is, you know, it's ready to receive anything so I'm going to call the t destructor as I said we gotta do that because that's the nice thing to do. And then we're going to initialize it from, by moving from t the temporary we just had on the stack. And at the last, as the last stack here, we're going to set the flag to false. And at this point the swap is done. You know, you gotta figure that if any of this was an exception, not good! It gets pretty tense, right? Therefore, getting back to the signature, which I think that this is the bug I found, the signature was not restrictive enough. So, you know, it can't really do what I want. So, enable if t is enabling the function to exist. If and only if t is moved constructable and swappable and e is move constructable and swappable. All of these conditions must be met. Hence the conjunction and otherwise there's no swap; you can't define it. That's my, this is my, it's not, it's not impossible somebody may find the, kind of, a number of methods and tricks and, kind of, careful copy and move combinations that are gonna work better, but this is the best I could get. So, again, it's a very good homework for you all to consider looking at. So now, you know, we have the we have the section very nice a typical use, you go, expect a double runtime error good and assert star good is 100; it all goes through. Although nobody should compare double numbers by equality; you know, it's funny, whenever I give a Tech talk, you gotta qualify pretty much every statement you give, you say. Cause there's all these complications. Unexpected disambiguates the bad case so we have the expected constructed with an unexpected of runtime error. So Jean-Francois and Vicente did a very nice thing of having this unexpected thing be on the same time of functional type, which is clever. So it's simply just call unexpected here, without any type or anything it just works. That's beautiful. Very nice and easy to use. And there's actually, there's some details in flux, you know, in flux about that particular bit. A typical use, let's imagine we define a relative function that takes two doubles and returns the relative ratio of b to a. And you, you know, if one is zero you can't divide by zero again to return unexpected. And otherwise you simply rely on the default construction of expected then return a double. You're done. Very nice. Centralized error handling goes, well, if you like undefined behavior, use operator star. If you don't use dot value. And that's pretty much the extent of my theory here because I'm unsolved like indicated is I'm gonna actually act calm right now. E is thrown if I call value against the dot and that kind of stuff. So, by the way, this one has undefined behavior so don't do this; use relative of the one dot value. Local handling is easy adjust it if it's hard we have the error and we know what to do. And of course, expect a bit more cost because there's a little extra testing involved but it's all good. Question for everybody. You know, once I walk into a forest and a tree fell, I didn't hear it. What happens if you have, if you call a function it fails to produce the result, it returns an actual exception but you never look at the result. So, essentially expect it is a contra that says it's either a t or an explanation for the fail of producing said t. Agreed? You are with me. Now, I call a function, gives me unexpected, and it turns out I don't need it. Well, if I don't need it I don't care if it could not be produced. This is the gist of lazy evaluation. It's beautiful. Well, there's been some discussion about it. Apparently, some people said, "Actually, you can't, you gotta, "like, there's an error that goes unchecked." And you know, they have an argument. I agree; they do have an argument. It's just completely wrong. (crowd laughs) And by the way, in LLVM you're gonna find something like that which has a different name. But it's kind of along the same lines. And actually in the destructor if you didn't check the thing, it's going to? No, it's not gonna throw. They know more than that. They know better than that. It's going to abort the whole application. (crowd laughs) The tree not only was audible, it was an atomic explosion, okay? No, I'm not kidding. This is, like, true. Truth! I saw it, like, just five minutes before my talk. Anyhow, to wrap up, we associate with expected errors with the goals of computation. And I think that's wonderful. We naturally allow as many exceptions as you want, no problem. You can teleport across threads of, you name it, stacks; whatever you want. You just can't transport exceptions just like normal values so no need to throw them. You can save them now, throw them later. You can't do whatever you want with classy composition measures that C++ gives you. You can collect, group, and combine them. And are much, so much simpler for a compiler to optimize because it's simple if allows tests. You've been great. Thank you very much! (crowd applauds) Thank you. Do we have time for questions? Thank you, yes please. - [Man] Hi, with exceptions if a function can throw an exception, E1, and another function invokes that function, and can also throw an exception, E2, then the total set of exceptions that can be thrown by the second function are E1 and E2. How do you represent and compose errors with this type, in general? - This is very interesting. So, how do you compose with a function that may throw different types of exceptions and how they compose with given that expected is only one type? Actually, the initial design was more generous in that regard; it allowed more composition. But the current design does not, sort of, the design, my baby would have had, like, an exception; would have supported an exception. That said, I notice that the world is moving from large exception hierarchies to smaller exception hierarchies. And there's a bit of a trend in the community to say, "you know what, we really don't "need those many exception classes "because we do one thing with them all. "We just bring here and we say, 'have a good day.'" So it's not, you know, I don't think it's a critical issue but I agree it is an issue. It's difficult to compose if you have, like, multiple exception types. Yes, please. - [Man] If I function has side effects and it's returning unexpected and it successfully generates the expected or maybe it doesn't get to it yet because one of the side effects fails, what then? - Yeah, that brings us to, so, you know, the function has side effects. It also produces a value but you get, like, you know, don't check the other set. There is a bit of a fuzzy area there, right? That's the gist of. Well, I should add there is a thing as an expected void. Which is interesting because I expect nothing. It's only run for the side error effects of the function. If a function has a side defect and this is kind of an imperfect scenario. But, you know, this is C++. Imperfect scenarios are the name of the game. You can't have everything. I agree there's a fuzzy area there. Where are the side effects versus the return value? It applies best to functional-style scenarios. Thank you. - [Man] So you observe that it doesn't really make sense to complain if the expected object is destroyed without being accessed because you didn't need the values so you don't care if it failed. So, what about expected void? - Yeah, so expected void would be that one case in which it's painfully clear that I'm running the function just for it's side effects so then, something must be said about that error. I agree. Yeah, expected void would be the one case in which, eh, you got to the destructed didn't check anything it's expected void, there might be some, there might be some room there. I agree, thank you. Great point. - [Man] Hey, so, during your talk you mentioned, you know, exceptions having a performance cost, which, I didn't really like that in the ether because it's just sort of, compared to what, it's very non-specific. So, I'm just curious in these cases, where you've looked at it, I assume you compared to something. So, was it a case where an exception was being used for error handling in a local scope? And did you look at (Andrei clears throat) do you think that the results would generalize in, you know, the situations where exceptions really shine is if you're throwing, let's say, an exception of five call-stack layers, now getting rid of an exception means adding branches every single layer of the call-stack. I mean, I'm skeptical that exceptions will be slower in the normal path than error codes in that case. - I think it's a, yeah, I understand the point. So, the point it, well, how did you, what's your baseline when you evaluate? Well, it's kind of interesting. The baseline was the same application written with error codes and enabling exceptions. So you pay all the costs but you don't reap the benefits. - [Man] Oh, I see. - So that, you see, that makes it a bit unfair to exceptions. I agree with that. The ideal test would be, you write a whole application using one style and you write a whole application using the other style and you compare. That would be difficult. We didn't run such a test. Thank you. - [Man] One of my issues with exceptions, historically, has been it's difficult when you're trying to log where errors happened. Where exactly the error happened if you're catching at some level up the call-stack. It seems like with rich composition with this sort of methodology it would be possible to inject, you know, with relatively low (Andrei clears throat) compiler overhead, something that would facilitate being able to log an effective stack-trace of where your error happened even if you're catching it three or four levels up. Is that something that's been given consideration in the design as an optional way to enable or, you know, would that be roll your own if you wanted to enable that sort of functionality? - Okay, so, let me make sure I understand. The question is like with, with the Try Catch approach it's sort of more as a theory to, on the way up, to kind of print things, log things, when did this happen? - [Man] More specifically, if you want to get the global error handling at a higher level of the stack, you're giving up the specificity of exactly where the exception is from. - Right, thank you. Yes, with expected this is pretty much a typical scenario in which you do have that chance with normal programming means as opposed to, kind of, as a theory stack-traces and stuff. So, I don't, it was not a consideration but it's a great, it's a great angle that you have about that because I think it makes it painfully easy to just, simply, whenever you feel you're gonna be able to insert instrumentation. Say, "oh, this, something bad is happening here "and I'm going to continue moving upwards." Thank you. Andre? - [Andre] If you want to force a person to deal with the excepted, couldn't you just mark it as a "no-discard?" - No-discard? There's some, yeah, I think you could do that. GCC has that, right? There's an annotation that, today, there is an annotation that says no-discard, no-discard and that kind of stuff. So if you're talking more like competition with that feature? - [Andre] No, I'm saying, if you were worried if you were trying to temporary expected, it would just disappear if you care about the side effects? - Ah, okay. - [Andre] And you force them to deal with the side effects by marking no-discard. - So the courier of that would be expected void would always be no-discard. Is that along the same? - [Andre] Well, you mark is as no-discard so they have to ask it, which would cause it to throw. - Okay, this is it, so we should chat more about this right now. We're out of time here, so no more lunch for you. Thank you very much! (crowd applauds) Thank you!
Info
Channel: CppCon
Views: 50,937
Rating: undefined out of 5
Keywords: Andrei Alexandrescu, CppCon 2018, 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, conference live streaming, event videographers, capture presentation slides, record presentation slides, event video recording
Id: PH4WBuE1BHI
Channel Id: undefined
Length: 58min 58sec (3538 seconds)
Published: Thu Nov 01 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.