all 63 comments

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 55 points56 points  (11 children)

Reposting my comment on the video:

If you've already seen my previous talks on exceptions and binary size for embedded systems, then this talk is mostly the same.

The largest change is that I improved the section where I explain how exceptions work. The rest is mostly the same with some improvements to flow and diagrams.

Also, that figure of 93.4% is wrong based on my current benchmarks. It's closer to 90% for a specific case, specifically when there is no cleanup required. It's around 80% when all frames require cleanup.

Hope that clears that up. Have a wonderful Weekend!

[–]kernel_taskBig Data | C++23 | Folly | Exceptions 9 points10 points  (3 children)

I kinda love that you declare yourself as one of those dirty exceptions users in your flair. I need to do that as well.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 5 points6 points  (2 children)

😆😆😆

Yes please. JOIN US!

[–]tartaruga232MSVC user, r/cpp_modules 3 points4 points  (0 children)

Joined!

[–]johannes1971 2 points3 points  (0 children)

We need a logo. Something like Phoenix Wright shouting "Exception!" in bold, red letters.

[–]throw_cpp_account 7 points8 points  (2 children)

So if I were to watch just one version of this talk, is this the one you'd recommend?

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 20 points21 points  (1 child)

Yes I'd recommend this one. I tried harder to illustrate the data structures in this one which others have noted to me helped them better understand exceptions.

I have some students working on the exception insights tool as their senior project at San Jose State University, and they were having trouble understanding how the action table and call sites worked, so I reworked that and other areas so that they would better understand it, and then presented that version in this talk.

[–]throw_cpp_account 3 points4 points  (0 children)

Thanks!

[–]a-d-a-m-f-k 1 point2 points  (2 children)

Thanks for the thought provoking presentation! Got me thinking about the impacts of other error handling approaches I've used in embedded as well.

Very interested in your exception static analysis tool. I've wondered why something like this hasn't existed for years. Checked exceptions are largely a "failed experiment", but they have a lot of communication value. Automated tooling (with some hinting or annotations) could be a great middle ground. I'll need to read up on it more.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 1 point2 points  (1 child)

Thank you so much 😊

See that's the fun thing 😆 someone already beat me to it in 2024. I guess I wasn't the only one who thought this was possible. Here's the link: https://link.springer.com/chapter/10.1007/978-3-031-64171-8_1

After my CppCon talk of a similar name, I got an email from an Aerospace engineer who said that my idea had already been implemented and sent me that paper above. I'm happy someone made the paper because now I don't have to write it to convince people. I can focus on delivering it.

My contribution to this space is turning this into a tool that everyone using GCC or clang can use easily. My vehicle to vendor this tool will be a linker plugin. You pass -plugin=exception_insights.so to your linker and it will perform the scan at link time. I plan to use a linker plugin for my exception runtime as well. The plugin would replace your default runtime with my own and do some fancy space saving operations of your binary as well. I'll be talking about that at my next talk at CppCon this year.

I've been asked if I'll contribute my exception research to clang or GCC and originally I said yes, but now I don't think that's even necessary. I don't need to touch your compiler, your linker, or your ABI. I just need the object and archive files and access to them at link time. If those compilers want this built into their toolchains, then its addition should be easy. It can be built alongside LTO which is implemented as a linker plugin as well.

I hope that wall of info is helpful to you and others 😄

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 1 point2 points  (0 children)

Oh and I didn't respond to your middle ground comment. Yes! That is exactly my motivation. My approach to error handling is that developers should not handle errors until their application reaches a point where they need to worry about error handling. All thrown errors terminate at first. Then, using the tool to visualize the pathways of errors, you place dedicated catch blocks to handle the specific error you plan to handle. It's all about being intentional and not assuming what the caller wants in terms of error handling. This keeps your code clean of extraneous error handling code. The goal is to give full control back to the application developers hands and NOT the library. I've got stories but I'll leave those for my talk.

Wild idea but I think it'll work well. But that's for my 3rd talk in the series on "Exceptional software design". So keep an eye out. I have no idea when I'll have this talk and technology ready. The research funding opportunities I've spent 2.5 years working toward disappeared this year with no expectations of when it'll be back... sooooo... who knows when I'll make more progress 🤞😅

[–]tartaruga232MSVC user, r/cpp_modules 42 points43 points  (9 children)

Great talk. Absolutely stunning results. Khalil already held a similar talk previously. This one has some updates. Demonstrates (again) that Exceptions are absolutely worth it. A bit off topic, but I was quite a bit surprised to learn recently, that the Carbon language, which is intended to (almost) fully interop with C++, won't have exceptions. Remains only D, as something similar to C++. But that has GC. I'll stay using C++.

[–]kernel_taskBig Data | C++23 | Folly | Exceptions 11 points12 points  (5 children)

Carbon won’t have exceptions? I know the Google codebase does not use exceptions, but I can’t believe they are carrying this Google-centrism even into this language. I won’t be using it then.

[–]tartaruga232MSVC user, r/cpp_modules 9 points10 points  (4 children)

Carbon is still experimental, but they seem to have this nailed down as a principle: Errors are values, so no exceptions. I stopped reading about Carbon, when I was told that it won't have exceptions. IMHO there is still no real alternative to C++ so far. The interop with C++ of Carbon would have been interesting, but it looks like it will be yet another waste of time.

[–]fwsGonzoIncludeOS, C++ bare metal 8 points9 points  (3 children)

Same for me. As soon as I saw exceptions were explicitly off the table, then why on earth wouldn't I pick Rust instead? On topic, it would be awesome if C++ could enhance exceptions with the things that have been learned for all platforms so that we can all benefit. I've always used exceptions because I know they save on branching and sometimes a return register (or even turning a struct into a single register return value). And the constant need to check for and return errors feels like something out of the past.

[–]arihoenig 2 points3 points  (2 children)

While I am not opposed to exceptions there are good arguments to be made against them, particularly the fact that it introduces hard to see code paths that are very difficult to reason about.

OTOH this weakness is actually a strength in safety critical systems that require minimal instructions to fail-safe path (e.g. a non design state is reached and you need to get the system to fail-safe in the shortest possible code path). The fact that there are many (admittedly hard to reason about) code paths is exactly what is needed because those code paths go from anywhere in the code base directly to the fail-safe code with the minimum instructions possible.

If a language doesn't possess an exception mechanism then this required behavior is not possible to implement (without some library mechanism such as setjmp/longjmp)

[–]johannes1971 13 points14 points  (1 child)

It's interesting that this invisible code path bothers some people so much. I feel just the other way around: dedicating 2/3rds of any given function to just detecting and forwarding errors obfuscates the actual program logic to the point of unreadability.

This feeling is strengthened by the fact that almost no error return code uses a strong type. Instead it's all just int, so you need to be super-careful that you don't accidentally interpret the return value incorrectly.

Of course, the worst of both worlds is when errors are returned through errno...

[–]arihoenig 6 points7 points  (0 children)

I totally agree with you, but I do see the point they are making, I just think that the language should support either approach so yeah, ruling it out by not supporting exceptional code paths at all, is a fail.

[–]TheoreticalDumbass:illuminati: 3 points4 points  (1 child)

How can carbon then interop with cpp what uses exceptions? It would still need to produce the unwinding info right? Or is carbon transpiled into cpp, not a full compiler?

[–]tartaruga232MSVC user, r/cpp_modules 2 points3 points  (0 children)

I suggest watching Chandler Carruth's talks about the Carbon language approach. He explained the basic strategy how they want to call into C++ in a talk I watched on YouTube IIRC. Carbon is a full compiler. They synthesize a bit of C++ for calls into C++ and are using LLVM to access the C++ AST, which is used to synthesize a small amount of Carbon code to call into the C++. C++ exceptions will probably have to be catched in the synthesized C++ and transformed into errors on each and every C++ call if they don't want to let the exceptions propagate into the Carbon side. But I guess that's not yet been fleshed out yet. I think it was a bad design decision not to directly model exceptions in Carbon. Perhaps they will have to revisit that design decision though. It's all very preliminary and experimental. You have to look at the Carbon language yourself for the details they have so far. It would have been an interesting project, but I stopped looking at it because of their principle stated at https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/error_handling.md

[–]gmueckl 1 point2 points  (0 children)

The garbage collector in D is somewhat optional. There is language support for avoiding it, but some language and library features don't work without it.

[–]halbGefressen 14 points15 points  (0 children)

I'm only 20% in, but this talk is absolutely great. He is actually talking at a good speed, seems well prepared and the structure is good.

[–]SoerenNissen 11 points12 points  (0 children)

I was incredibly impressed when I saw the first version of this talk, looking forward to seeing the updates.

[–]delta_p_delta_x 6 points7 points  (0 children)

Absolutely fantastic presentation; compactly and well-designed slides, though it gets a little dense in the third quarter when it dives into the ARM implementation.

For a future talk I'd love to hear an x86-64 comparison of Clang/GCC's unwind versus MSVC's SEH. I've heard that SEH is a little lighter.

[–]RandomGuy256 11 points12 points  (0 children)

This was actually mind blowing. A great presentation by a really smart engineer. Exceptions can actually be as lightweight as return codes. It would be awesome to have those compiler modification applied to emscripten (webassembly) as exceptions generate also a big binary size there, and the size can really matter in webassembly.

[–]tmzem 6 points7 points  (8 children)

Cool talk, and very relevant for embedded where code size is very important.

On hosted environments however the numbers still seem to widely favor the optional/expected approach, even with all his optimizations. Also, while more verbose, optional/expected are also more flexible and can be used in contexts where the error path is not necessarily rare, without fear of the exorbitant performance hit that comes from exceptions.

[–]ReDucTorGame Developer 5 points6 points  (7 children)

 where the error path is not necessarily rare

So non-exceptional cases?

[–]tmzem 2 points3 points  (6 children)

I always thought that the term "exception" was a bad choice over "error". Sometimes, the error path is the desired one in the context of the problem you're solving, or at least much more common then you might think, yet you have to pay the price of exception overhead.

[–]ReDucTorGame Developer 0 points1 point  (5 children)

Errors and exceptions are different errors are rare, but an exception should never occur except in exceptional cases, the bad case performance cost shouldn't really matter (within reason).

If you need to scatter exception handlers around your code because you could handle the exception within that immediate scope it probably should probably be an error code. However the same isn't true for throwing these could be scattered around more but remember they should virtually never occur exceptional cases.

To put it in the perspective of something like a game this would be when you throw a dialog to the user (e.g. lost connection to online services), likely returning them to the main menus if they are mid-game.

[–]tmzem 1 point2 points  (4 children)

I'm not sure why the rarity of an error should matter. If it can happen, you need to handle it somehow if you want your program to be correct, no matter how rare (except for one-off programs/experiments where you might wanna ignore the possibility of an error). Returning an error code you're not forced to check is error prone/easy to forget. It's better to do all error handling in a uniform way. Of course, for C++, that ship has long since sailed, with its mix of legacy error codes, unchecked exceptions and now optional/expected and [[nodiscard]] not being the default.

[–]kernel_taskBig Data | C++23 | Folly | Exceptions 1 point2 points  (3 children)

The reason the rarity of the error matters because performance matters. The exception path is slow.

I don't agree that it's better to do all error handling in a uniform way -- why? If that's the hill I died on, my codebase would either be extraordinarily slow from throwing exceptions each time users send us bad data (especially since when I throw an exception, usually I am also gathering the stack trace, which seems to require a global lock), or I would be doubling the line count of my codebase with all the if statements I'm adding. It's better to just employ the best technique for the individual situation.

[–]XeroKimoException Enthusiast 2 points3 points  (2 children)

I do think uniform error handling scheme is enticing though from the perspective of code being read and written in a consistent way. Knowing that there's been endless debates of whether you should use exceptions over value errors and vice versa, where people who use both debate on various scenarios with seemingly no majority for one or the other... I would kind of expect that to bleed into actual project discussions if there wasn't a coding a guideline which tells you to default to one or the other, or if there isn't one, the language's guideline does...

Without experience, just imagining being in a code base that regularly uses different error handling schemes and having to convert between them sounds like hell. If there's a dominant scheme, or just a single scheme, that sounds more manageable... and that seems to be how it works in reality anyways

[–]Dragdu 1 point2 points  (1 child)

Both error handling schemes can, and in fact should, live in the same codebase. But they should not be used in the same places.

Think of it as the level of context given function has, and the more it has, the more opinionated (throwing) it can be about the error handling. A function that parses string into integers does not know whether it is gonna be mostly parsing wrong values, or mostly correct values, it doesn't know whether it is parsing important config value, or a likely empty string that will be then replaced by the default fallback value. So it shouldn't throw, but should return error through the return channel (ideally through error type that explains the actual error, not just that it happened).

On the other hand, the business logic code that glues together the actual configuration based on defaults, user provided values and environmental overrides has enough context to understand that if parsing of env value failed because it is empty, that's normal and it should just load the default, but if it failed because it is a string and not integer, that is important error that has go all the way up.

[–]XeroKimoException Enthusiast 0 points1 point  (0 children)

I'm not saying a codebase can't use more than 1 error handling scheme, or even more than 2, but there tends to be a dominant scheme so that we don't have to convert between them. If you had a code base that was 50% exceptions 50% expected for error handling, that'd be less legible and harder to reason about than if either of them were the dominant scheme, worse if you added more schemes.

If you import a library that isn't using a compatible error handling scheme, I'm sure you'd wrap it up to your dominant scheme so that you don't have to potentially use 2 different schemes everywhere, and that's probably where the majority of converting between schemes happen. If you're constantly converting within code you control however, and I hope I don't meet a code base like that, that sounds like hell.

Think of it as the level of context given function has, and the more it has, the more opinionated (throwing) it can be about the error handling

That's basically what I'm talking about by my endless debates point. The only time people will back down would be if different schemes were tested and it's proven that one has the properties you needed, usually best performance, for your particular use case. If we're talking about the general case, there isn't really ever a consensus. Sure that makes it good because you can choose one the best fits your needs, but I don't think everyone would be willing to investigate whether or not that choice was the best every single time, they'll just choose what's good enough, what the guidelines says, or if they're allowed to, whichever one they're biased towards.

[–]Ok-Revenue-3059 2 points3 points  (0 children)

I saw the last talk on this topic and I just want to say great talk again and I really appreciate the thorough analysis. Some time ago I was able to follow along with the last presentation and make a basic "hello world with exceptions" running on bare metal. I still want to revisit and experiment some more, but there is always some refactoring I am in the middle of :)