Why are they universally shunned though? They are more elegant than returning error codes up the stack, and if you stick to RAII principles they should clean up after themselves too. I use them mainly for things like initialization / loading files. Initialization is usually fatal while loading files usually allows whatever was asking for the file to put a dummy file in if lets say it wasn't found, or there was some data missing and so on.
That's a topic that will surely generate a holy war thread...
Some reasons I can think of at the moment:
1) Tradition. Nintendo and Sony have traditionally given developers quite terrible C/C++ compilers, where many features of the language either didn't work, or were implemented horribly. Templates and exceptions were greatly affected by this, and any cross-platform code-base unfortunate enough to have to support those compilers would often choose to avoid those features. Even now, Microsoft and Sony still recommend that you use the options for their console's compilers that disable exception support.
2) Writing exception-safe code in C++ is hard. Sticking to RAII makes it sound easy, but there's still all sorts of pitfalls, like objects being left in an inconsistent state (
some members being written to prior to a throw, but others that were going to be written after), double-throwing (
your raii classes themselves aren't allowed to trigger errors during resource deallocation), etc... It's very easy to create very subtle bugs if your whole team aren't C++ experts (which is usually the case). I've known guys who've been writing games in C++ for a decade who still aren't familiar with many of the language's quirks and subtleties.
3) Performance. It's not a big deal on a modern PC, and not
as much of a big deal on modern consoles
as it used to be, but most C++ exception implementations cause horrendous amounts of code bloat (large executables). In console environments, large executables can in fact be a real problem for performance. On older consoles, it definately was a huge problem, so this ties into the 'tradition' problem as well.
4) Style. The elegance argument is highly subjective, and attempting to debate it will only incite a holy war. Some people would argue that manual error codes are more readable, maintainable, etc... especially since C++'s
exception specifiers are retarded, which makes reasoning about whether code will throw or not quite a pain when compared to newer languages. Even outside of games, some big players like Google even shun C++ exceptions.
5) Multi-core engines. Standard C++ exceptions aren't clonable, which means passing them across thread boundaries is a pain. In a job-based architecture, the point of invocation and the point of execution aren't actually connected by a call-stack, so handling errors by unwinding the stack doesn't work. If using exceptions, you need a way to catch them in the job handler, clone them onto the heap, and then re-throw them when the job results are collected. You can do this by using your own exception base class, but then the standard exceptions still aren't usable.