Hating C++: Exceptions
September 04, 2018I've recently started working in C++ after a long time working in C (and occasionally Python). To be honest, I had hoped to avoid C++ and go straight to D/Rust/Go/Nim/retirement, but it was not to be. The language has changed a lot since I last used it "in anger" in 2006 or so, and the Facebook flavor is unique in its own ways. Some of those differences are improvements. Some are mixed, or I'm waiting to see (lambdas are a good example). One thing that doesn't seem to have changed to any significant degree is exceptions. I hated them then, and I hate them now. Just to be clear, I'm not anti-exception in all cases. I believe there are good implementations and usage patterns for them. Unfortunately, the specific implementations and usage patterns I've seen in C++ are absolutely horrible. Here are some reasons.
- When you throw an exception, you also throw away most of the stack. For an uncaught exception you might get an actual stack trace, but that doesn't help until next time. Otherwise, you get to see where an exception was caught but not (readily) where it was thrown. If you're in a debugger you might be able to pull apart the stack manually and get at that information, but it's tedious at best.
- Intermediate functions have no chance to see and log the error. It just flew right by them, unless they caught and re-threw it themselves (I'll get to that). This is a huge missed opportunity to zero in on the problem. In professionally written code it's highly likely that some intermediate layer would have logged something, saving a developer the trouble of drilling manually through each and every intermediate function to see how far it got before the flow of control was yanked sideways.
- Most RPC layers don't support exceptions. If you're working on a distributed system, that means you probably have both exception and error-return models in your code, and that's worse than either alone.
- Exceptions are far more costly than error returns. All that stack unwinding doesn't come for free.
Sure, it's possible to implement and use exceptions in a way that makes them less bad in some of these ways, but nobody ever seems to do that and even the very best attempts still fall short of good old-fashioned error returns. They're a bit tedious, but they work. Be fancy on your own time. Exceptions should be exceptional, indicating something so unexpected that it justifies termination of a pretty high-level entity - a process, a thread, a complex I/O request. It should never be the case that a single API or user request encounters multiple exceptions as part of its normal progress. That's bad for both performance and maintainability. It turns an otherwise deterministic flow of control that's easy to reason about into a chaotic pile of jumps between widely separated pieces of code. Really, it's more like old-fashioned BASIC than anything a modern professional programmer should accept.