7 ms·
The only way exceptions should be exceptional is that they should rarely be used in any code base. At best, the are a micro optimization that helps you a tiny b
by randyrand 5y ago
The only way exceptions should be exceptional is that they should rarely be used in any code base. At best, the are a micro optimization that helps you a tiny bit in the success case. They should be used when standard return value errors are measured to be impacting perf (ie. exceedingly rarely). Unfortunately, C++ language authors made them basically a requirement for OOP and RAII.
Imagine writing a parser. Can you use exceptions? It's hard to say! In fact, often impossible to say! How often an exception is thrown is highly dependent on the input data. If you're in a controlled environment with nearly always well formed data, it might be a perf win! But there's a ton of complexity in just deciding which type of error to use, and thats on top of the complexity of now having multiple ways of returning errors!
No one likes code that has 2+ ways of indicating method failure. If you are a library author, do your users need to check for both Exceptions and Error codes? If you are a nice author, you'll just choose 1 way to indicate failure and wrap where necessary.
If you want to use exceptions, they should be used in very limited scope when perf measurements indicate they would help, tightly wrapped in a catch, and then immediately converted to some other standard error type.
Even better, we should just let the choice of when to use exceptions up to an optimizing compiler, and provide the compiler a function to convert between exceptions and our more general purpose error type of choice.
- Traster 5y agoOk, I'll bite. Disclaimer: I'm not a software engineer, FPGA engineer by training. The use model I have in my mind is that my functions all work 99.999% of the time, but occasionally something happens where basically I want to throw up, I want the whole thing to collapse. Exceptions are great for me. I'm in an industry where "stop" is an okay solution in the rare occasion something goes wrong. So what's the alternative? I have to overload my return values annd then put in a load of special cases to propogate or return the exceptional cases. I am aware there are functional languages that have other ways of dealing with this, or languages with things like optional values etc. But fundamentaly I like the idea that there's just a mechanism to just collapse, and collapse in a way that is traceable. Having said that, my experience essentially does not involve catching exceptions, to me that's what is almost unthinkable (read: probably a bad hack). I guess my use case is not performance but separateing normal code function from catastrophic errors. Could you explain, do you think this is a fundamental mistake in the way I'm approaching this?
- randyrand 5y ago> I have to overload my return values annd then put in a load of special cases to propogate or return the exceptional cases. Yes + syntactic sugar. Propagating an error up can and should be a single character of code. > collapse in a way that is traceable. Zig supports "error traces" (note, not stack traces) by default, since they have a single common way of reporting errors and a smart compiler. > I like the idea that there's just a mechanism to just collapse, Go uses Panic + Recover.
- kinjba11 5y ago> my experience essentially does not involve catching exceptions Which is part of the problem. If you're not catching the exception, you don't need exceptions. You just need a way to log stuff, such as a trace, and exit the program. New languages such as Rust and Go encourage this explicitly - that is, use the ordained error path in the return value for expected errors, and only panic for catastrophic errors (or use panic in hotpath where return overhead has unacceptable performance impact and errors are super rare). http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0709r4.pdf http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p070... > Programs bugs are not recoverable run-time errors and so should not be reported as exceptions or error codes. — We must express preconditions, but using a tool other than exceptions... migrate std:: away from throwing exceptions for precondition violations.
- ncmncm 5y agoWhen you are writing a library, you don't know what the program that calls you might have set up to handle failures, but you know exactly how to get to it: throw, and you are there, and it is now for the caller to decide what to do, thus not your problem. A library that exits has stolen control away from its rightful owner. Very few languages are as finely tuned to the needs of libraries and users of libraries as C++. Exceptions are an essential part of that compact. Failing to abide by it makes your library a bad citizen.
- deleted 5y ago[deleted]
- tremon 5y agoThey should be used when standard return value errors are measured to be impacting perf (ie. exceedingly rarely) I disagree with this. I think they should be used whenever "standard" return value error handling starts to obfuscate the normal operation (happy flow) of the code. This impact on developer performance happens a lot sooner than the measurable machine performance.
- randyrand 5y agoIt should not obfuscate the happy path. In rust, propagating an error up is as easy as adding a '?' after the function call. fn read() -> Result<String, io::Error> { let f = File::open("hello.txt")?; // propgate up? let s = f.read_to_string(&mut s)?; // propgate up? Ok(s) }
- mcronce 5y agoUntil getting the ? operator in Rust, I always thought I really liked exceptions. After all, what was the alternative? `if err != nil { return nil, err }` everywhere just to propagate errors up and probably ultimately have the process panic when there's an error? "How obnoxious", I thought; the vast majority of the time I'd rather crash and be restarted than attempt to recover and risk severely corrupting state. Turns out I strongly prefer explicit errors over exceptions, I just also strongly prefer error handling not consuming 75% of the lines on my screen. And in the cases where I really do prefer a panic, there's always `.unwrap()`
- ncmncm 5y agoThis just spectacularly bad advice and policy. Point performance is the worst way to decide about error handling. The overlap between "things that fail and the caller knows something useful to do", and "things that fail and somebody maybe six levels back in the call chain might know something to do" is remarkably thin. Most things are the latter. Once in a while, it makes sense to provide one for each: (1) open a file, and it had better succeed or you might have to bail out, or (2) see if a file is there. The reason exception is the default, aside from that there are plenty of ways to return an error report, is that when you need code to clean up, the destructors are right there. They get exercised every time through the code, so are well-tested. How often is your special-snowflake error handler ever exercised? And the next one? And the next one? Even before you have given a moment's thought to error handling, your destructors are cleaning up, correctly. So, even if you haven't written code to handle it, your program has remained in a well-defined state. And, very often, you don't even need to write them: they are auto-generated from things the compiler already knows. If you have set up a high-level catch block, you get there without fuss, and that code knows something correct to do, even if it is only to shut down cleanly. If you left a record of what was about to be attempted, that code knows what to try next.
- randyrand 5y ago> Point performance is the worst way to decide about error handling. My point is exactly that. It's more important to have a single common way of doing error handling than it is to have a small perf improvement. That's why I recommend return values. You'll never hit a perf problem with them. Exceptions cannot be used in code hot paths (~thousands of iterations) when it's not a small perf problem anymore. Therefore, they cannot be your only method for signaling failure. And therefore, assuming we care about consistency and composability of code, it's far better to use return value based error handling unless you have good reason (almost never). Ideally with syntatic sugar from the compiler (see Rust, Zig, Swift, etc). > when you need code to clean up, the destructors are right there. Exceptions are not needed to achieve this. C++ calls the same 'well tested' destructors when you return from a function. The one point in your favor, that I acknowledged above, is that C++ requires exceptions to report failure from Constructors and Destructors, for OOP, std::vector initialization, for much of the standard library etc. Thats a travesty.