I disagree with your disagreement.
What? Civilized disagreement? I thought this was the internet...
The assumption that "exception safe" is the same thing as "error safe" is wrong. Exceptions are a _mechanism_ of dealing with errors, not errors themselves. Exception-safe code has to deal with safety from that particular mechanism as in addition to being safe against the errors themselves.
There are many ways of making sure coders are dealing with errors when necessary. The world is not all exceptions, return codes, and errno.
Exceptions are a failed experiment and the CS community has already proven that you can do much better be it in realms needing extremely safe error-handling (medical devices, aviation, etc.) or realms needing easy error handling (games) and everything in between.
I'd be interested in seeing some of these mechanisms because I'm used to using exceptions in my hobby/tool code, and dealing with return codes/error codes at work. If you aren't using an exception, then you're propagating an error code yourself (or ignoring it). This can make it harder for the compiler to work with the branch predictor as it cannot necessarily determine which branch is the failure state. Admittedly, if you're doing it yourself there is less "hidden" code, but the same can be said about RAII and virtual dispatch and, for the most part, we've accepted the compiler can do those better then we can. (And/or we accept the size/performance tradeoff)
Any elaboration on a real error-handling sliver bullet would be great
Increasing code-size, with all the extra potential unwinding cases added everywhere, can have a huge performance impact. Even just having exception support enabled in your compiler (regardless of whether you even use them or not) makes your code slower
Exceptions tend to make code faster (if you're using them for error states and not flow control), safer, and easier to read. Their only downside that I'm aware of is they do increase code size due to the dispatch tables
Libraries targetted at games generally are forced to avoid using exceptions at all, because many game-developers disable them for performance reasons (the compilers for consoles have them disabled by default, with a compiler option to enable them if they're really required!).
n.b. my statements are only true for C++'s exception system -- the mechnisms in newer languages are much nicer... Also C++'s mechanism is much nicer on x86-64 than it is on x86/PPC/ARM/etc...
True, you can potentially run into more cache misses and similar problems with the increased code size, but you'd have to measure that. (I don't have an exception-safe code base to test against an equivalent non-exception-safe code base in a meaningful manner at this point) However if you're using error codes/return values you're either ignoring the errors (questionable practice, but hey, we're games, not medical software) or you're adding a lot of ifs/branches that the compiler can't necessarily predict, which may be worse (again, would need measurement in your use case).
Yeah - strong exception safety is very hard. You've got to be constantly mindful that any line in your program could potentially be a hidden return statement, and make sure that the program will always be in a valid state at all times (except when you're completely sure that the lines in question give you the nothrow guarantee).
Writing exception-safe code is just as hard/easy as writing error safe code, no matter your error mechanism.
... You are correct in that it is hard to write code to enforce the strong or nothrow guarantees.
When exceptions are disabled (or you give up on the strong guarantee), that massive mental tax goes with them I personally hate the exceptions-mental-tax, because most IDE's suck at informing you whether a line of code can potentially throw or not, so it's not easy at a glance to tell how safe some bit of code is. If throw-specifiers / noexcept weren't broken features, this tax might be lifted somewhat...
If you're using return values to report errors, the alternative mental tax is that you've got to be constantly mindful that functions have return values that you may have to check...
Usually in games, "expected errors" are extremely rare anyway, so the debate over the correct style only concerns a very small amount of code. If you expect an error to occur though (e.g. you tried to insert a new key-value into a map, when that key was already present) then most people will say that exceptions are the wrong mechanism.
"Unexpected errors" in games are usually just given straight to the crash-handler, with no need to write recovery code (just a need to generate good debugging information to fix the code/data).
I think a large part of it comes from proper SRP use. So if your resources are held by objects solely concerned with holding them, it makes your job a lot easier. I think you're going to have a large mental tax either way. Either you know that an error will hop out of your code, or you manually code in the return value checks and hop out of your code yourself.
The largest mental tax, IMHO, is determining what constitutes an exception vs. what doesn't. But that's something that you have to handle no matter your mechanism. Does this error mean a code flaw? Then assert or crash. Does it mean a data flaw? Maybe then you want to use a placeholder instead or throw a warning to the user. Part of that choice is going to come down to who most likely generated the bug (coder or designer/artist) and who is most likely able to fix it. Exceptions can help because they are great at getting errors from where they happen to code that can actually make decisions about what to do about it (crash, open a dialog box, replace with placeholder, etc).
In my opinion, these are the largest reasons why they are not used in AAA development today:
- Old compilers didn't do exceptions well. This means older code bases are going to be full of code that didn't use them, and don't expect them.
- Older hardware (and mobile hardware today to some extent) didn't have the memory to spare for a larger executable size that exceptions and RTTI required, so they were disabled. This, again, results in a code base that doesn't expect them.
- Exceptions are not language-neutral. Your libraries with C interfaces cannot throw exceptions, and you can't let exceptions escape your code into other code that uses a different language.
- Exceptions cannot escape threads - requiring you to come up with some sort of home-grown cross-thread propagation mechanism.
Nothing insurmountable, but it's much easier for someone to use exceptions in a hobbiest/indie situation where they aren't dealing with large amounts of non-exception-safe (and heck, frequently non-RAII) code and where their game may not need to squeeze out every ounce of power out of the machine via ignoring errors (or crashing).
Edited by SmkViper, 22 July 2014 - 07:43 AM.