I think exceptions are an important language advance, but
I can't dispute that they can cause a mess of problems
when it comes to cross-language (or cross-thread)
boundaries.
Exceptions are not completely problem-free, but there is no alternative. If you don't have exceptions, every single line of code:
doSomething();
Becomes:
int result = doSomething();
if (result != OK) return result;
... and that is the simple case, without memory management, an actual result to return, unfortunate interactions with your other control flow statements, and so on. And you still end up with your application helpfully saying "ERR: -1923876".
This is simply doesn't work. And few people programming C/C++ appear do it correctly; I'd imagine that many security flaws come from improperly proceeding code that should have checked a return code.
I prefer return values over exceptions in languages that make it safe and easy.
Haskell does a pretty good job, I think.
With Maybe or Either, you have to check it to extract your value, but if you are doing a lot of things that will result in/use the results of Maybe/Either, you can just operate in a Monad that makes them the default.
Similarly, languages with multiple return values make you explicitly ignore the return value.
Exceptions have their benefits, but I have a personal preference for return values because they make it explicit in the interface. Also, I find that it makes more sense to handle various success and failure scenarios in the same bit of code instead of writing a best-case flow and assuming that exceptions will be thrown and errors will be dealt with somehow.
Exceptions are great if you can't continue due to programmer error, but most cases where people use exceptions are just expected execution paths that (in my view) make no sense to handle via non-local control flow.
Exceptions aren't the only solution though. Conditions/restarts in common lisp seem to be a more powerful but similar alternative.
One problem with exceptions is that they can only be one deep. Ie. if you are executing the code invoked by handling an exception, what should happen if another exception gets thrown. Java says forget about the old exception (ie. lose information), C++ says crash. Maybe there is something in between?
With or without exceptions, being error-safe is still really hard. I find you have to think "transactionally". For every operation you perform you have to think about what you need to clean up if it fails, so you focus on specifying what you want to do, not how you want to do it.
This is simply doesn't work. And few people programming C/C++ appear do it correctly; I'd imagine that many security flaws come from improperly proceeding code that should have checked a return code.