7 ms·
Problems with C++ exceptions
- Flundstrom2 1y agoJava and Rust are the only language that I know of that has proper handling of exceptions; mandatory declaration as part of method declaration, since exceptions ARE an integral part of the contract. (Yes, I consider the Result<T, E> being corresponding to exception declaration, since the return value MUST be checked prior to use of T.)
- nielsbot 1y agoSwift user here: I have to say one of the best features of Swift is the exception handling. Which is to say, exceptions in Swift are not C++/Java/Obj-C style exceptions, but instead are a way to return an error result from a function. And Swift enforces that the error is handled. That is, a `throw` statement in Swift simply returns an `Error` value to the caller via a special return path instead of the normal result. More explicitly, a Swift function declared as: func f() throws -> T { } Could be read as func f() -> (T|any Error) { } More here: https://github.com/swiftlang/swift/blob/main/docs/ErrorHandlingRationale.md https://github.com/swiftlang/swift/blob/main/docs/ErrorHandl...
- thomasmg 1y agoI saw that in Swift, a method can declare it throws an exception, but it doesn't (can't) declare the exception _type_. I'm not a regular user of Swift (I usually use Java - I'm not sure what other languages you are familiar with), but just thinking about it: isn't it strange that you don't know the exception type? Isn't this kind of like an untyped language, where you have to read the documentation on what a method can return? Isn't this a source of errors itself, in practise?
- mayoff 1y agoSwift gained limited support for “typed throws” in Swift 6.0 (2024). https://github.com/swiftlang/swift-evolution/blob/main/proposals/0413-typed-throws.md https://github.com/swiftlang/swift-evolution/blob/main/propo... I say limited because the compiler doesn't (yet, as of 6.2) perform typed throw inference for closures (a closure that throws is inferred to throw `any Error`). I have personally found this sufficiently limiting that I've given up using typed throws in the few places I want to, for now.
- Someone 1y ago> isn't it strange that you don't know the exception type? Java experience taught us that, when writing an interface, it is common not to know the exception type. You often can’t know, for example, whether an implementation can time out (e.g. because it will make network calls) or will access a database (and thus can throw RollbackException). Consequently, when implementing an interface, it is common in Java to wrap exceptions in an exception of the type declared in the interface (https://wiki.c2.com/?ExceptionTunneling https://wiki.c2.com/?ExceptionTunneling)
- magicalhippo 1y agoWhat I liked about Bosst's error_code[1], which is part of the standard library now, is that it carties not just the error but the error category, and with it a machinery for categories to compare error_codes from other categories. So as a user you could check for a generic file_not_found error, and if the underlying library uses http it could just pass on the 404 error_code with an http_category say, and your comparison would return true. This allows you to handle very specific errors yet also allow users to handle errors in a more generic fashion in most cases. [1]: https://www.boost.org/doc/libs/latest/libs/system/doc/html/system.html https://www.boost.org/doc/libs/latest/libs/system/doc/html/s...
- tcfhgj 1y agoWhen using a language forcing checked exceptions, you would know, wouldn't you?
- thomasmg 1y agoYes I know Java and the challenges with exceptions there (checked vs unchecked exceptions, errors). But at least (arguably) in Java, the methods (for checked exceptions at least) declares what class the exception / exceptions is. I personally do not think wrapping exceptions in other exception types, in Java, is a major problem. In Swift, you just have "throws" without _any_ type. And so the caller has to be prepared for everything: a later version of the library might suddenly return a new type of exception. One could argue Rust is slightly better than Java, because in Rust there are no unchecked exceptions. However, in Rust there is panic, which is in a way like unchecked exceptions, which you can also catch (with panic unwinding). But at least in Rust, regular exceptions are fast.
- deleted 1y ago[deleted]
- nielsbot 1y agoI don't think it's strange at all--my main uses of the returned errors are 1a. yes, there was some error 1b. there was an error--throw another local error and encapsulate the caught error 2. treat result of throwing call as `nil` and handle appropriately I don't think typed throws add anything to the language. I think they will result in people wasting time pondering error types and building large error handling machines :) When I used Java, I found typed exceptions difficult to reason about and handle correctly.
- amomchilov 1y agoTyped exceptions are unlike typed parameters or return values. They don’t just describe the interface of your function, but expose details about its implementation and constrain future changes. That’s a huge limitation when writing libraries. If you have an old function that declares that it can throw a DatabaseError, you can’t e.g. add caching to it. Adding CacheError to the list of throwable types is an API breaking change, just like changing a return type. Swift has typed errors now, but they shouldn’t be used carefully, and probably not be the default to reach for
- fakwandi_priv 1y agoFrom what I can read Swift gives you a stack trace which is good. At the moment I’m using Go where that stack is only generated where the panic is triggered, which could be much higher up. Makes it a lot more unwieldy to figure out where an error happens because everyone uses: > if err != nil return err
- mayoff 1y agoSwift doesn't capture a stack trace in the `Error` object, but Xcode can break when an error is thrown if you set a “Swift Error Breakpoint”, and the debugger will show you the stack trace. Under the hood it just sets breakpoints on the runtime functions `swift_willThrow` and `swift_willThrowTypedImpl`.
- nielsbot 1y agoThis is built in to the language. When you call code that can throw (return an error via the special return path) you either have to handle it or make the enclosing context also throwing. Assuming `canThrow()`, a function that might throw an `Error` type: func canThrow() throws { ... } Call canThrow(), don't handle errors, just rethrow them func mightThrow() throws { try canThrow() // errors thrown from here will be thrown out of `mightThrow()` ... } Alternatively, catch the errors and handle them as you wish: func mightThrow() throws { do { try canThrow() } catch { ...handle error here ...or `throw` another Error type of your choosing } ... } There are a few more ways to handle throwing calls.. For example - `try?` (ignore error result, pretend result was `nil`) - `try!` (fail fatally on error result)
- mayoff 1y agoAnother really nice thing about Swift is that you have to put the `try` keyword in front of any expression that can throw. This means there's no hidden control flow: if some function call can throw, you're informed at the call site and don't have to look at the function declaration.
- binary132 1y agothat sounds very similar to noexcept(bool) to me, except that noexcept can be computed, for example by deriving from some generic trait, and we presume throws unless specified non-throwing by noexcept.
- munchler 1y ago`Result<T, E>` comes from Haskell's `Either a b` type. F# also has a `Result<'T, 'E>` type. It's funny how often functional programming languages lead the way, but imperative languages end up with the credit.
- raincole 1y agoActually (insert nerd emoji) this is a direct descendant from tagged union type, which existed in ALGLO 68, an imperative language. Java's checked exception is just an (very anti-ergonomic) implementation of tagged union type.
- munchler 1y agoOK, but it wasn't until functional programming came along that tagged unions were recognized as a natural way to implement sum types, which is how they are used in modern programming. The old idea of a "variant" has pretty much faded away.
- paulddraper 1y agoAlgebraic types, immutable structures, lambdas, higher-order functions. I think FP receives a lot of "credit." Albeit the "pure" FP languages aren't popular, because 99% FP is really hard.
- JoshTriplett 1y agoIn fairness, Rust also has unchecked exceptions (panics, if you use panic-unwind).
- majormajor 1y agoJava too has unchecked exceptions, though the post praising "mandatory declaration" didn't mention it for either. But they CAN be caught with a standard catch which may be non-intuitive if you don't know about them, and about that, in advance.
- morshu9001 1y agoIn high-level code, pretty much everything can fail in many different ways, and usually you're either just passing the error up or handling it in some catchall manner. Rust's behavior makes sense for its use cases, but it'd get exhausting doing this in like a web backend.
- wahern 1y agoIt's already too exhausting to use for OOM, which is why Rust effectively punted on that from the very beginning. And the ironic thing is that anyhow::Error (or similar) seems poised to become idiomatic in Rust, which AFAIU always allocates, while the extremely belated try_ APIs... does anybody even use them? It's a shame. Were I designing a "low-level" or "systems" language, rather than put the cart before the horse and pick error results or exceptions, my litmus test would be how to make handling OOM as easy as possible. Any language construct that can make gracefully handling OOM convenient is almost by definition the fabled One True Way. And if you don't have a solution for OOM from the very beginning (whether designing a language or a project), it's just never gonna happen in a satisfactory way. Lua does it--exceptions at the language level, and either setjmp/longjmp or C++ exceptions in the VM implementation. But for a strongly typed, statically compiled language, I'm not sure which strategy (or hybrid strategy) would be best.
- morshu9001 1y agoHm, not sure how you'd do this. OOM error auto bubbling up sounds dangerous because the inner code might not leave things in a consistent state if it exits unexpectedly, so that makes sense to manually handle, but it's tedious. Rust at least has nice ? and ! syntax for errors, unlike Go where the error-prone error handling actually ruins the entire language.
- binary132 1y agoIf only there were some sort of automated idiom for cleaning up state under failure modes
- aw1621107 1y ago
- barrkel 1y agoDon't forget, failure modes pierce abstraction boundaries. An abstraction that fully specifies failure modes leaks its implementation. This is why I think checked exceptions are a dreadful idea; that, and the misguided idea that you should catch exceptions. Only code close to the exception, where it can see through the abstraction, and code far away from the exception, like a dispatch loop or request handler, where the failure mode is largely irrelevant beyond 4xx vs 5xx, should catch exceptions. Annotating all the exceptions on the call graph in between is not only pointless, it breaks encapsulation.
- binary132 1y agoThis is a better way of expressing what I had been thinking about putting exception details behind an interface, except that in my mind encapsulating errors is just good design rather than implementation hiding, since the programmer might want to express a public error API, for example to tell the user whether a given fopen failed due to not finding the file or due to a filesystem fault.
- rerdavies 1y agoe.what() Just include the file name in the error message. And all of this is predicated on logging errors, which is not at all user-friendly, and not remotely acceptable in GUI applications.
- binary132 1y agoI was talking about catching different classes, not about logging. Even if you’re just implementing a string description, there’s nothing stopping you from designing that string for use in a UI element, or implementing a structured message that can specify more details usable for defining UI elements, similarly to how many REST HTTP-driven web UIs work. I don’t think I’m quite following your criticism.
- 1718627440 1y agoIf your error codes leak the implementation details through the whole call stack you are doing it wrong. Each error code describes what fails in terms of it's function call semantics. A layer isn't supposed to just return this upwards, that wouldn't make sense, but to use it to choose it's own error return code, which is in the abstraction domain of it's function interface.
- paulddraper 1y agoIt causes big problems in Java. For example, it plays poorly with generics. (Especially if you start doing FP, lambdas.) If Java added union types, it wouldn't be a big deal, but AFAIK this is still a limitation.
- muragekibicho 1y agoHonestly, I thought the diatribe would focus on needless complexity. The starting example is how I'd do it in C: ``` void f(const char* p) // unsafe, naive use { FILE \*f = fopen(p, "r"); // acquire // use f fclose(f); // release } ``` Wouldn't the simpler solution be ensuring your function doesn't exit before release? All that c++ destroyer stuff appears somewhat unnecessary and as the author points out, creates even more problems.
- mgaunard 1y agoThat means you cannot use early exit, and all your variables must be checked as to whether they were initialized (which on top of the checks might also require further state).
- themafia 1y agoYou can use goto to jump to one of several exit conditions based on the level of cleanup you need. It also nicely unifies all error exits into one place. The kernel makes heavy use of this style.
- mgaunard 1y agosure, but that has limitations as well, since gotos can't cross lexical scopes, so you can't introduce variables later on, and it's easy to mess up. Destructors are a higher-level and safer approach.
- 1718627440 1y ago> since gotos can't cross lexical scopes, so you can't introduce variables later on That's a C++ specific limitation, it works just fine in C.
- nanolith 1y agoIn C, you're correct. The problem is that, in C++, one must account for the fact that anything could throw an exception. If something throws an exception between the time that f is opened and f is closed, the file handle is leaked. This is the "unsafe" that Bjarne is talking about here. Specifically, exception unsafety that can leak resources. As an aside, it is one of the reasons why I finally decided to let go of C++ after 20 years of use. It was just too difficult to teach developers all of the corner cases. Instead, I retooled my system programming around C with model checking to enforce resource management and function contracts. The code can be read just like this example and I can have guaranteed resource management that is enforced at build time by checking function contracts.
- jari_mustonen 1y agoThe classic "Frequently Questioned Answers" is the ultimate takedown of this abomination of a language. https://yosefk.com/c++fqa/ https://yosefk.com/c++fqa/
- mgaunard 1y agoAuthor completely misunderstands how to use exceptions and is just bashing them. A lot of what he says is inaccurate if not outwardly incorrect. Also Bjarne's control of C++ is quite limited, and he is semi-retired, so asking him to "fix his language" is fairly misguided. It's designed by a committee of 200+ people. Anyway what you want seems to be to not use exceptions, but monads instead. These are also part of the standard, it's called std::expected.
- gblargg 1y agoAgreed. Author is trying to mix paradigms. Simplest approach if they want local handling and non-propagation of errors is to just have the file holder not check for open success, and check that manually after construction. Then you get guaranteed closure of file no matter how the function is exited. class File_handle { FILE *p; public: File_handle(const char *pp, const char *r) { p = fopen(pp, r); } ~File_handle() { if ( p ) fclose(p); } FILE* file() const { return p; } }; void f(string s) { File_handle fh { s, "r"}; if ( fh.file() == NULL ) { fprintf(stderr, "failed to open file '%s', error=%d\n", p, errno); return; } // use fh }
- mikhael 1y agothis may lose the value of errno, right?
- actionfromafar 1y agoGoes against "Resource acquisition is initialization" though to have objects which exist but can't be used. (I think that's the relevant pattern?) However, where the language and it's objects and memory ends and the external world begins, like files and sockets... that's always tricky.
- lealanko 1y agoThis is a common problem with try-catch syntax. An alternative, and arguably more useful syntax would be try (File_handle fh {s, "r"}) { // use fh } unless (const File_error& e) { // handle error } Where the "use fh" part is not covered by the exception handler. This is covered (in ML context) in https://www.microsoft.com/en-us/research/publication/exceptional-syntax/ https://www.microsoft.com/en-us/research/publication/excepti...
- mgaunard 1y agoDon't use try/catch in the first place; that's where his error lies.
- 1718627440 1y agoThat's not his own invention, but an idea introduced by the language founder to sell the idea of RAII and exceptions. He did say, that he would prefer the C version, but then why use C++ in the first place.
- charcircuit 1y ago>The first is that our error message may not be correct. It’s possible that the exception we’ve caught was not introduced by opening this file, and, the errno may not reflect the errno at the time fopen was called. All that is needed is a better File_error type that includes the error that happened. void f(string s) { try { File_handle fh { s, "r"}; // use fh } catch (const File_error& e) { fprintf(stderr, "File error: %s\n", e.msg.c_str(); return; } }
- oakpond 1y agoThe first two problems can be solved in a straightforward way with more custom exception types. For the "bigger problem", catch(...) can be used to prevent your code from crashing. If you really want to handle each case explicitly you could also use enums in combination with compiler flags that enable exhaustive checking.
- SuperV1234 1y agoThis post completely misunderstands how to use exceptions and provides "solutions" that are error-prone to a problem that doesn't exist. And this is coming from someone that dislikes exceptions.
- guy2345 1y agowhat about the misreported errno problem?
- binary132 1y agoobviously the errno should have been obtained at the time of failure and included in the exception, maybe using a simple subclass of std exception. trying to compute information about the failure at handling time is just stupid.
- marler8997 1y agoI avoid using exceptions myself so I wouldn't be surprised if I misunderstand them :) I love to learn and welcome new knowledge and/or correction of misunderstandings if you have them. I'll add that inspiration for the article came about because It was striking to me how Bjarne's example which was suppose to show a better way to manage resources introduced so many issues. The blog post goes over those issues and talks about possible solutions, all of which aren't great. I think however these problems with exceptions don't manifest into bigger issues because programmers just kinda learn to avoid exceptions. So, the post was trying to go into why we avoid them.
- thegrim33 1y agoNo offense, but why did you decide to write an instructional article about a topic that you "wouldn't be surprised that you misunderstand"? Why are you trying to teach to others what you admittedly don't have a very solid handle on?
- marler8997 1y agoNone taken :) I think sharing our thoughts and discussing them is how we learn and grow. The best people in their craft are those who aren't afraid to put themselves out there, even if they're wrong, how else would you find out?
- miohtama 1y agoIt's a bad post, but I find it funny someone thinks C++ can be fixed (:
- gorgolo 1y agoI didn’t really understand the writer’s comments with exceptions and I don’t code in C++. Their main complaint about exceptions seems to be that you can’t handle all of them and that you don’t know which you’ll get? If we compare this to python, what’s the difference here? It looks like it works the same here as in python; you catch and handle some exceptions, and others that you miss will crash your program (unless you catch the base class). Is there something special about C++ that makes it work differently, or would the author have similar problems with python?
- 1718627440 1y ago> would the author have similar problems with python? I would expect yes. It is true, that in a lot of modern languages you need to live with that dynamism. But to people used to C, not knowing that the error handling is exhaustive, feels deeply uncomfortable.
- apple1417 1y agoIn normal use it's essentially the same yes. The one interesting edge case that might catch some people out is there's actually nothing special about std::exception, you can throw anything, "throw 123;" is valid and would skip any std::exception handlers - but you can also just catch anything with a "catch (...)".
- marler8997 1y ago"You can't handle all of them and you don't know which you'll get" is a great summary of the first two problems, and, this same problem also applies to Python. I'll add that these only start becoming an issue when you start adding more exceptions to your codebase, especially if those exceptions start appearing deep in a callstack and seemingly unrelated code starts needing to be aware of them/handle them. The third problem (RAISI) is a C++ specific problem that Python doesn't have. Partly because in Python try/catch doesn't introduce a new scope and also partly because Python tends not to need a lot of RAII because of the nature of interpreted languages. I found this video a fascinating take on comparing C++ to Python if you haven't seen it: https://www.youtube.com/watch?v=9ZxtaccqyWA https://www.youtube.com/watch?v=9ZxtaccqyWA
- wvenable 1y agoI agree with the other comments that this understanding of exceptions is wrong. He's not wrong about these two points: - Correctness: you don’t know if the exception type you’ve caught matches what the code throws - Exhaustiveness: you don’t know if you’ve caught all exceptions the code can throw But that's actually not a problem. Most of the time you shouldn't catch a specific exception (so always correct) and if you are catching then you should catch them all (exhaustive). A better solution is merely: void f(string s) { try { File_handle fh { s, "r"}; // use fh } catch (const std::exception& e) { printf(stderr, e.what()); } } That's all you need. But actually this code is also bad because this function f() shouldn't have a try/catch in it that prints an error message. That's a job for a function (possibly main()) further up the call stack.
- smallstepforman 1y agoMy biggest beef with exceptions is invisible code flow. Add the attribute throws<t> to function signature (so that its visible) and enforce handling by generating compiler error if ignored. Bubbling up errors is OK. In essence, this is result<t,e>. Thats OK. Even for constructors. What I dislike is having a mechanism to skip 10 layers of bubbling deep inside call stack by a “mega” throw of type <n> which none of the layers know about. Other than
- gpderetta 1y agoI would also like to have type checked exceptions in c++ (as long as we can also template over exception specifications). But what I would really want is noexcept regions: <potentially throwing code>... noexcept { <only noexcept code here> ... } <potentially more throwing code>... i.e. a way to mark regions of code that cannot deal with unwind and must only call non-throwing operations.
- wakawaka28 1y agoDon't think of the uncaught type as a "mega" throw. It's just a distinct type of error that nobody specified that they can handle. If you truly worry about the caller missing something, then somewhere in there you can catch anything and translate into a recognizable exception. This is easiest to understand in a library. The interface functions can catch all and translate into one particular exception type for "unknown" or generic errors. Then, that will be caught by anyone using the thing as documented. This only works if it's just reporting a non-fatal error. In case of a fatal error, it can't be handled, so the translation is kind of pointless.
- classified 1y agoIf you use names that frigging bad, exceptions are the least of your problems.
- howtofly 1y agoAll this hassle can be avoided by using `cleanup` compiler attribute. Manage classical C resources by auto-cleanup variables and do error-handling the normal way. If everything is OK, pass the ownership of these resources from auto-cleanup variables to C++ ctor. Note this approach plays nicely with C++ exception, and will enter C standard in the form of `defer`.
- ranger_danger 1y agoYes, but now your code is no longer C or C++ standards compliant as it relies on compiler-specific attributes, if that matters to the programmer. Unfortunately, even the Linux kernel is no longer C because they use GCC compiler extensions (and are currently discussing adding MS ones too).
- 1718627440 1y agoA kernel will make use of asm, and can't abstract over the machine, so it will always be unportable and relying on compiler extensions.
- ranger_danger 1y agoThe Linux kernel did not always rely on compiler extensions though...
- 1718627440 1y agoThe kernel once did not need asm? That's a compiler extension, although a popular one.
- pmontra 1y agoOr do without exceptions for control flow. For example Elixir does have try/catch but it's used very rarely because most functions return tuples with the first element being either :ok or :error. Then we can pattern match it and return an :error to the caller if we have to, possibly bubbling up several levels return after return. Or let the process crash and restart while the rest of the application keeps running. A surprising number of errors eventually fix themselves (API calls, disk space, missing data) when you design the system to attempt more times to complete its tasks. That's not only a characteristic of Elixir and the BEAM languages. You can do it more or less easily on any language. Maybe you need a queue and workers reading from the queue and all it takes to manage them, and BEAM makes it convenient by including most of it. The page about try/catch explains it well https://hexdocs.pm/elixir/try-catch-and-rescue.html https://hexdocs.pm/elixir/try-catch-and-rescue.html
- senderista 1y agoOr, if you don't want to go to the trouble of writing an RAII wrapper class for FILE*, just use scope_guard and (after determining that fopen() succeeded) register a lambda to close the FILE* on exiting the function (including by throwing an exception). I'm not a huge fan of scope_guard (or defer() in other languages), but it gets the job done for one-off cases.
- febusravenga 1y agoI think i agree with commenters that article kind-of uses exceptions wrong. But this shows problems with C++ exceptions, C++ codebases are literred with bad exception usage because "normal" programmers don't get it. Most of didn't get it for long time. So, they are complex enough that their usage is risk. Anyway, IMO C++ exceptions have two fundametal problems: * lack of stacktrace, so actually lack of _debugabbility_, that's why people try/catch everything * destructor problem (that actually there are exceptions tht cannot be propagated further and are lost
- barrkel 1y agoYou probably do want that exception to bubble up, actually. You probably don't want to catch it immediately after open. Because you need to communicate a failure mode to your caller, and what are you going to do then? Throw another exception? Fall back to error codes? Unwind manually with error codes all the way up? And if so, logging was the wrong thing to do, since the caller is probably going to log as well, based on the same philosophy, and you're going to get loads of error messages for one failure mode, and no stack trace (yes, there are ways of getting semi-decent stack traces from C++ exceptions). Exception safety has a lot of problems in C++, but it's mostly around allowing various implicit operations to throw (copies, assignments, destructors on temporaries and so forth). And that does come down to poor design of C++.
- 1718627440 1y ago> you're going to get loads of error messages for one failure mode > and no stack trace That loads of error messages, meaning every layer describes what it tried to do and what failed, IS a user readable variant of a stack trace. The user would be confused with a real stack trace, but nice error messages serve both the user and the developer.
- barrkel 1y agoThis is error-prone boilerplate that obscures the code, obscures the logs and is a known antipattern (log and throw) - which you're implementing manually, by hand, in the hope you never make a mistake. You shouldn't do manually what you can automate. Boilerplate can make you feel productive and can give you warm fuzzies inside when you see lots of patterns that look familiar, but seeing the same patterns over and over again is actually a smell; it's the smell of a missing abstraction.
- 1718627440 1y agoNo. If you want to have the same information without that approach, you need to implement nested levels of error information, ten layers deep, each layer having a diagnostic, a log level, and file, line information, and a reason. In other words, you are building your own custom stack trace object, with annotated diagnostics. In addition, you can't reason about this at the upper level anyway, or you are rebuilding your application stack at some other place. The only thing you can do is to unwrap that object and serialize it into a diagnostic, which you could have done with less code, less memory and less compute. In addition, you would need to allocate on a failure path, which sounds like a nightmare. Also you can never have comments in the code, because what you would write into the comment is now in the diagnostic itself. That means you can also turn on DEBUG_LEVEL_XXL and get a nice description, what the program actually does and why. > This is error-prone Why is it error-prune. You receive an error of one type and need to convert it into an error of another type. You need to handle that or the compiler will bark. > obscures the logs How does it obscure the logs?
- binary132 1y agoThis strikes me as all wrong. The whole point of exceptions is control flow and destructors. By getting rid of RAII for the sake of simplifying the callsite a little, the author fails to obtain the real advantage, which is automatic resource unwinding of all local resources under failure conditions both during initialization and usage. If you want to simplify the callsite, just move the exception handling to a higher scope. I can admit it’s a little irritating to put a try/catch in main but it’s trivial to automate, and most programs are not written inline in main. The main problems I see with destructors have to do with hidden control flow and hidden type information. That said, hiding exceptional control flow from the mainline control flow of a function is also a useful feature, and the “exception type” of any given function is the recursive sum of all the exception types of any functions or operators it calls, including for example allocation. That quickly becomes either an extremely large and awkward flat sum or an extremely large and awkward nested sum, with awkward cross-namespace inclusion, and it becomes part of the hard contract of your API. This means, transitively, that if any deep inner call needs to extend or break its error types, it must either propagate all the way up into all of your callers, or you must recover and merge that error into _your_ API. For _most_ usecases, it is just simpler to implement a lean common interface such as std::exception and allow callers who care to look for more detailed information that you document. That said, there is a proposal (P3166 by Lewis Baker) for allowing functions to specify their exception set, including via deduction (auto): https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3166r0.html?utm_source=chatgpt.com https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p31...
- deleted 1y ago[deleted]
- leduyquang753 1y ago> … go on to talk about removing the required scope to avoid RAISI .. then go on to talk about compile-time enforcement of exception handling… Is this an LLM prompt?
- _dky 1y agoIf C++ had a contract on what exceptions a function can throw with compile time check to enforce caller catches those exceptions, would it make it better? Guess Java does that, not much experience in Java here.
- rerdavies 1y agoJava does it. It is a horrible horrible feature. C# used to do it. But they decided it's a horrible horrible feature, and removed it.