
Error codes vs exceptions: critical code vs typical code - suraj
http://www.yosefk.com/blog/error-codes-vs-exceptions-critical-code-vs-typical-code.html
======
shin_lao
If you use C++ without exceptions, it means you cannot use the STL or any
library that throws. Pretty weak.

Additionally, almost all examples of "exceptions are bad", fall short because
of RAII (at least in C++) or finally clauses (in other languages).

Good reasons for (almost) not using exceptions (in C++):

\- Performances

\- "Expectability"/readability

\- Platform-dependant constraints

There are probably others, but the article only talks about the "readability"
arguments without even mentioning things such as exceptions specifications.

One thing you need to remember about exceptions: they're nothing more than a
fancy goto.

~~~
tptacek
What a weird claim. Not only do you not need exceptions to use the C++
standard library, but you can (or at least used to be able to) compile C++
code without support for exceptions.

Also, exceptions are not just a fancy goto, at least in C++. They're a fancy
longjmp. Goto doesn't unwind the stack.

 _Edit_

Wait wait wait it appears I am wrong about this; the STL we used could be
configured not to use exceptions but wow, that sucks, you need them for the
standard library.

~~~
shin_lao
You actually need exceptions anywhere you do new, unless you exclusively use
new(std::nothrow) (hint: you don’t). On top of that the STL may throw
exceptions such as std::out_of_range as I responded in another comment. And
again, you have to review all your libraries to make sure they don’t throw.

Exceptions are a fancy goto, longjmp is more accurate if you will, but it is
my understanding that longjmp will not call all the appropriate destructors,
it will just restore the environment which can lead to resources and memory
leaks, from the standard:

 _If any automatic objects would be destroyed by a thrown exception
transferring control to another (destination) point in the program, then a
call to longjmp(jbuf, val) at the throw point that transfers control to the
same (destination) point has undefined behavior._

Some compilers do add the appropriate clean up code to make longjmp
cooperative to that aspect, so I might be just nitpicking a little bit here.

What I meant by "fancy goto" is that basically exceptions break the flow and
can make the code much, much harder to review and making, solid and reliable
code with exceptions is much, much harder than you might think (in C++),
Raymond explained it better than me:
[http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/35294...](http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx)

~~~
evincarofautumn
Exceptions aren’t really like “goto”—they’re dynamically bound, dynamically
typed, nonlocal “return” statements. Which incidentally is a useless mechanism
for error handling unless you have some kind of destruction semantics, which
is mentioned in the article. Anyway, given the choice between exceptions and
return values for error handling, I go for return values that the compiler can
force me to check.

------
jbert
Can someone who uses exceptions in large systems offer a comment on _which_
exceptions they throw?

One of the problems I have in trying to design with exceptions is that they
seem to offer a lot of potential to break abstraction layers.

As a concrete example, if you have a cacheing layer which - say - is
implemented over the filesystem. In the event of an inability to access the
correct location, the low-level routines might throw a permission-related
exception. If you swap out the filesystem cacheing implementation with, say,
one based on memcached you might instead get a network-related exception.

Neither of these types really make sense in the context of a 'cacheing layer
error', they seem to leak implementation details - breaking the abstraction.

Do people accept this, or do they catch-and-rethrow new exception types at
major layer boundaries? (e.g. in the above two implementations, both low-level
exceptions may be caught and rethrown as "CacheInitialisationError" with some
perhaps some additional diagnostic about the underlying cause). If they have
such API-specific error types, do you go to the trouble of modelling an
exception type hierarchy? So all your 'cache layer' exceptions inherit from
'CacheError' so a higher level can catch all "CacheExceptions" in one place?
This seems like a lot of additional modelling effort, is it commonplace?

To my mind, the possible exceptions a API call may make (or equivalently, the
errors it could return) are _part_ of the API, and the errors need to be at
the same conceptual level as the rest of the API. (So you can't have
filesystem errors in a generic cache API).

What happens in practice?

~~~
MikeCodeAwesome
Given your scenario, you are indeed correct: throwing implementation-specific
exceptions leaks internal details and further couples clients of the API. Can
you imagine using such an API which forces you to continually handle new
exceptions that arise from new implementations? As you suggest, it's better to
create an exception for the API which then wraps the underlying exception. I
would not go so far as to create a family or hierarchy of API exceptions
unless there's a need to communicate each of them individually.

What usually happens in practice is a combination of none and all the above. A
bit of a mess, really.

------
kombine
Example from the article criticizing exceptions:

    
    
      open_the_gate()
      wait_for_our_men_to_come_in()
      close_the_gate()
    

D programming language for example has an excellent support for RAII with
ScopeGuard statement <http://dlang.org/statement.html#ScopeGuardStatement> The
example in question will become:

    
    
      open_the_gate();
      scope(exit)
        close_the_gate();
      wait_for_our_men_to_come_in()
    

Then even if wait_for_our_men_to_come_in() throws we still close the gate.
There is additional granularity - we can execute statement on success or
failure too.

The technique was originally developed by Andrei Alexandrescu for C++ and
described in [http://www.drdobbs.com/cpp/generic-change-the-way-you-
write-...](http://www.drdobbs.com/cpp/generic-change-the-way-you-write-
excepti/184403758)

~~~
ajross
You can do the same thing in C++ RAII with an extra function scope though.
Just create a Gate object or whatever and drop it on the stack before you call
the wait_for... thing. An exception thrown out of that scope will clean up the
Gate object for you and close it. Or just use Java, which has a "finally"
syntax that does the same thing. Or Python's "with", which match D very
closely indeed.

The point is that D is hardly unique about this. And further: the fact that D
_has_ syntax that makes this operation clean doesn't mean that it will be
used. That first example can be written in any language, and often is. The
"open/close" pair is clear and obvious in this example for didactic purposes,
but in old code under maintenance that kind of assumption can be very subtle.

Syntax won't save you here. The problem being explained (which I only
partially agree with) is that the whole exception model is flawed.

------
pragmatic
No discussion of Exceptions vs Error codes is complete without referring to
Joel Spolsky's Exceptions article:

<http://www.joelonsoftware.com/items/2003/10/13.html>

Also:

[http://channel9.msdn.com/Blogs/Charles/Erik-Meijer-and-
Rober...](http://channel9.msdn.com/Blogs/Charles/Erik-Meijer-and-Robert-
Griesemer-Go)

Erik Meijer interviews Robert Griesemer about Go. They chat about exceptions
for error codes. The exception talk starts ~8:00.

Erik is in favor of failing fast with exceptions b/c most of the time, when
you have an exception in an application, you want to fail fast because there
is "nothing you can do" at the point where you have an unexpected null, etc.

Very interesting discussion between two smart guys.

------
nsfyn55
Java world -> Aren't checked exceptions just error codes that use types
instead of arbitrary values and compiler gaurantees?

And wasn't the thought that checked exceptions would be better for critical
code because the compiler guaranteed that they were handled.

I'm no fan of java's error handling, but I think that the insinuation that the
lack of any formal error handling makes you write less error prone code is
bordering on the absurd.

Detail oriented individuals write robust code given the constraints of their
environment. If its arbitrary strings that indicate error state they use
strings if its integer return codes they use integer return codes.

------
SolarNet
This is why I hate all modern programming languages (I'm actually writing an
article on that >.>). The conclusion to this article is the same as many
others. Programming language feature X is better in situation A and
Programming language feature Y is better in situation B. Where the features
are often incompatible (or fulfill similar areas, error handling in this
case). The solution is of course to have multiple programming languages in
your projects that have both situation A and situation B

But languages are so annoying to integrate (where they can call into each
other and support each other's features, without ridiculous glue code. Let
alone swap code in the middle of a file), unless they were designed to (e.g.
python and C). It is hell to set up the library dependencies and get the
compilers to cooperate with each other. Which is why most just pick the
language that's best for the majority of their project (or spend large amounts
of developer time integrating other programming languages (not scripting
languages, that's related but not quite the point) into their project).

Which is why we need the ability to change semantic languages within the same
programming system, and have the programming system resolve the differences
between the semantic languages automatically. So you can have one semantic
language with features designed for situation A and another with features
designed for situation B and just swap between them as needed and have them
compile together automatically. This is what my Masters thesis and probably
what my PhD dissertation will be on.

------
PaulHoule
Another weak article. He never mentions

try { makeAMess() errorProneOperation() } finally { cleanUp() }

your first instinct should be to use finally, not catch, but Java brought us
the tragedy of checked exceptions (in which your #1 motivation in working with
exceptions is to shut up the compiler) and the bad habits have been adopted by
people who use other languages that copy the (otherwise pretty good) Java
exception handling style.

~~~
danieldk
Another person who didn't read the whole article? ;) Near the end he mentions
RAII in C++, _with_ in Python, and _using_ in C#.

------
hackinthebochs
Exceptions are far far more readable than error code handling. Exceptions
allow one to separate error handling from the structure of the "mainline"
execution path of an algorithm. Weaving if/else statements in and out of the
mainline path of an algorithm muddies the intent. Exceptions help with this
greatly. Instead of having to comprehend the entirety of the algorithm (when
exception handling can sometimes take up 50% or more of the code), you can
zero in on the most common execution path. Then understand the degenerate
cases in turn. The fewer branches your brain has to process in any given
moment is a boon for readability.

Yes, exceptions are just fancy goto's--but that is a good thing! Goto's when
used sparingly can greatly increase the readability of exception handling
code. Exceptions simply create a language level abstraction for this
functionality.

~~~
beagle3
> Yes, exceptions are just fancy goto's--but that is a good thing! Goto's when
> used sparingly can greatly increase the readability of exception handling
> code. Exceptions simply create a language level abstraction for this
> functionality.

That is entirely incorrect, except in the odd case of Sinclair ZX Spectrum
Basic, which is the only programming language that I"m aware of that allowed
you to do "LET a=10: GOTO a" (all other languages with goto require a label;
GCC has an extension for "indirect goto" and "label address" that is somewhat
similar, although it restricts you to staying within the same function scope).

If anything, it's a fancy "longjmp", that also calls destructors along the
way.

The big difference, and it is HUGE, is that when you do a "goto", you know
where you the next instruction is coming from. When you throw an exception,
you do not know which instruction will execute next from looking at the throw
site (and in fact, you may need to conceptually inspect an unbounded number of
stack frames to tell; which is different from longjmp or expression goto which
only require you to examine one memory location).

> Exceptions allow one to separate error handling from the structure of the
> "mainline" execution path of an algorithm

In theory. In practice, they essentially guarantee that the error handling is
not properly tested, and often useless except in the case of "everyday
expected errors", which are not really exceptional, but rather quite frequent.

~~~
hackinthebochs
You took my comparison with goto entirely too literal. The point was more
about code organization rather than implementation details. This should have
been clear from the context.

>In practice, they essentially guarantee that the error handling is not
properly tested, and often useless except in the case of "everyday expected
errors", which are not really exceptional, but rather quite frequent.

This hasn't been my experience.

------
aidenn0
And C++ exceptions are the worst of both worlds (uncaught C++ exceptions have
no call-stack; they are neither local, nor explicit).

