4 ms·
Simple: the meaning of the exception is known only to the guts of your library, and it is being automatically promoted to “catastrophic failure” when it could a
by makecheck 10y ago
Simple: the meaning of the exception is known only to the guts of your library, and it is being automatically promoted to “catastrophic failure” when it could actually be a very trivial problem or at least something that you could recover from gracefully. Instead, because you potentially forgot an exception clause somewhere, my entire program dies in an unpredictable place? That makes the entire model ridiculous.
- wvenable 10y agoAlmost all exceptions cannot be recovered from gracefully. Those that can, are generally recovered/retried from within your own code not the libraries code. An unhandled exception should always be a catastrophic error -- exceptions are exactly for situations where the code is in an unexpected situation. If you keep running, the application is now running in that unpredictable state. You just want to bury your head in the sand at a library boundary but I don't see why that's desirable. A well designed program (that isn't written in Java) should only have a handful of catch statements.
- makecheck 10y agoYou are making huge assumptions about how people use exceptions, and huge assumptions about the goals of a program. While you and I may agree that exceptions should be used for unexpected situations, nothing forces people to use them that way. A library programmer may have weird ideas about what warrants an exception. If my application appears to be functioning correctly, I should not have to risk crashing because of something that cannot be proven to affect my program in a significant way. My program may be trying to remain stable (e.g. a GUI for a user), and the user may well not care if my program has encountered an issue if it means throwing out their last 10 minutes of unsaved work. Some programs like CAD tools may run for days, and even partial results with logged errors can still be important to preserve in lieu of stupid crashes.
- wvenable 10y agoRight. So you catch the library exception in your code and deal with it. In GUI applications, I usually just have one top-level handler that shows any unhandled exceptions in a dialog and keeps running. Generally that's all a GUI app needs to remain running if it cleans the call stack up correctly and consistently That's the correct way to do it; blanket ignoring all unhandled exceptions from a library is just very strange by comparison. If there is a specific exception that you want to ignore, by all means ignore it and document it heavily. But you don't know if every exception is irrelevant or potentially data corrupting.
- makecheck 10y agoIf an exception violates its (function's) contract, it can terminate immediately and not reach even a catch-all. That contract can be inside a function I can't even see. Even if I do have a catch-all, what tells me that it is a "library" exception? They might have thrown a generic std::exception with a vague string description. They might have thrown an "int". At some point what I really want is proper language and compiler support for those boundaries so I can know exactly where an exception came from, and can trap it at those boundaries (or ask the library maintainers to do so).
- wvenable 10y agoThis just sounds like there are whole of really bad coding and C++ anti-features and you want to solve it with more C++ anti-features and bad code. A function could just not use exceptions at all and just call abort() on error, what are you going to do then? There isn't really a concept of library boundary in C++; you just have compilation units at best. At worst, you have a library that's implemented entirely as headers. What happens when the exception originates in your code but goes through the libraries call stack (because it was a call on a passed-in object or callback)? What you are asking for is likely impossible.
- makecheck 10y agoI know exactly what boundaries are lacking, that is what led to my original comment. And yes, functions can do whatever they want. Programmers expect special language features to be more useful than the alternatives though! If a function does call abort(), I can actually see the call, along with a stack trace; with source code I even know why the code exited. Odds are good that the abort() came from an assert() macro, giving me control over which builds can unexpectedly exit. Exceptions give me none of that, and introduce ways for valuable state to be totally lost in the process.
- maxlybbert 10y ago> While you and I may agree that exceptions should be used for unexpected situations, nothing forces people to use them that way. Here's another one: C++ allows programmers to overload operators, but doesn't enforce anything with regard to the overloaded behavior. I can make "+" actually subtract, or make "<", "==", and ">" always return "true", or have a function called "add_two" that actually adds three! When you find a language that prevents people from using it incorrectly, please let me know. Until then, I'll have to follow my rule of thumb that a library that crashes randomly -- whether it's caused by SEGFAULTing, failing to follow it's own exception specifications, or for any other reason -- is buggy and I won't use it. * In another comment, you say that you would like a language that enforces internal boundaries. It's not clear to me how that would work. Let's use a specific example: Imagine I am using a garbage collector library, and I ask for memory. It's a copying collector, and my request ends up promoting objects from one generation into another. Unfortunately, more objects survive than expected, so there isn't room in the target generation's pool. The library throws an exception, fails to handle it, and an internal exception specification doesn't include the exception causing us trouble. In other words, the programmer listed which scenarios were handled, and this one was not on the list. And, of course, we have a boundary like you had asked for. What can cross that boundary? The library doesn't have the memory I asked for. If we pass the exception across the boundary, it's not clear what purpose the boundary serves. It's also not clear what I should do with that exception: it's an internal detail. Even if I can figure out that we essentially have a buffer that's too small, I don't have access to that buffer (so I can't make it any larger), and I probably don't have access to the functions needed to start promoting survivors from our too-small generation to make room. How would the proposed boundaries handle this problem?
- makecheck 10y agoA boundary might not be enforceable in an absolute sense but the language should be able to tell when the boundary is crossed. An exception caught in main() that the compiler could automatically tag as having crossed a particular library or compilation unit would be infinitely more useful for debugging than what we have now. What makes a library sufficiently buggy? The set of conditions leading to a crash from an exception at run time may not be known to the library maintainer, even in a “good” library. The crash in your particular program, caused by the library, may not occur for awhile, after which you already depend on the API. With a normal error-handling scheme, this would still be OK because you could trace the error. Programs can already crash in enough ways, without C++ introducing more. I can debug even severe memory errors in fairly straightforward ways (e.g. trap in debugger, or run "valgrind", or other instruments). I hate debugging a “crash” that is essentially C++ converting a trivial mistake from someone’s error-handling code into a fatal, untraceable run time error. When I see terminate() I know I have a problem. I essentially have to start using "grep" for every possible "throw" statement (hopefully finding something in recently-committed code), trying to match functions that might be related to what the program seemed to be doing when it died. And since C++ exceptions provide very little help when debugging, one has to go to extra effort to add information to every exception (which is useless if the exception at run time didn’t come from your own code). C++ exceptions are just not “better”, like a lot of C++ changes. They can’t break crucial debugging mechanisms like stack-traces and expect the result to be better.