Exceptions vs. Error Codes

S

Shane Groff

I know this is a recurring discussion (I've spent the last 3 days
reading through threads on the topic), but I feel compelled to start
it up again.

After reading through the existing threads, I find myself convinced
that exceptions are a better mechanism, for example because
constructors and operators can't return errors, and code that doesn't
need to handle/translate/recover in the face of errors, but can just
propagate the error is cleaner with exceptions.

However, those reasons are often not compelling enough to convince
people who don't like exceptions (usually in my experience because
they incorrectly think that exceptions are more 'dangerous' somehow
than error codes, when in fact, they just don't see that it is just as
dangerous to ignore an error code as it is to not handle an exception
when it should be).

Consider this idiom when using error codes:

ErrorCode EcFoo()
{
ErrorCode ec;
Check(EcBar());
Check(EcBletch());
...
Exit:
return (ec);
}

Here, Check is a macro that checks the return code and jumps to Exit
if an error occurs.

Using this pattern, we avoid the appearance of a slew of if/then
statments (although they are there, of course, just hidden by the
preprocessor).

This makes the code almost as straightforward as the exception case,
and doesn't introduce much overhead (space or time), since it is
basically just adding a check for 0 to each call.

This addresses some of the problems I've seen leveled against error
codes. Although it seems to me that we're just writing what amounts to
our own equivalent to exceptions here (for example, functions that
don't handle the error don't need to do any 'additional' work to
propagate the error, assuming they are following the pattern), some
people are more comfortable with this because they know that returning
and checking for error codes is always fairly inexpensive, while
throwing an exception can be expensive.

Any specific thoughts on the idiom, or compelling arguments to offer
for the use of exceptions to the reluctant?
 
M

Mike Wahler

Shane Groff said:
I know this is a recurring discussion (I've spent the last 3 days
reading through threads on the topic), but I feel compelled to start
it up again.

After reading through the existing threads, I find myself convinced
that exceptions are a better mechanism, for example because
constructors and operators can't return errors, and code that doesn't
need to handle/translate/recover in the face of errors, but can just
propagate the error is cleaner with exceptions.

However, those reasons are often not compelling enough to convince
people who don't like exceptions (usually in my experience because
they incorrectly think that exceptions are more 'dangerous' somehow
than error codes, when in fact, they just don't see that it is just as
dangerous to ignore an error code as it is to not handle an exception
when it should be).

Consider this idiom when using error codes:

ErrorCode EcFoo()
{
ErrorCode ec;
Check(EcBar());
Check(EcBletch());
...
Exit:
return (ec);
}

Here, Check is a macro that checks the return code and jumps to Exit
if an error occurs.

Using this pattern, we avoid the appearance of a slew of if/then
statments (although they are there, of course, just hidden by the
preprocessor).

This makes the code almost as straightforward as the exception case,
and doesn't introduce much overhead (space or time), since it is
basically just adding a check for 0 to each call.

This addresses some of the problems I've seen leveled against error
codes. Although it seems to me that we're just writing what amounts to
our own equivalent to exceptions here (for example, functions that
don't handle the error don't need to do any 'additional' work to
propagate the error, assuming they are following the pattern), some
people are more comfortable with this because they know that returning
and checking for error codes is always fairly inexpensive, while
throwing an exception can be expensive.

Any specific thoughts on the idiom, or compelling arguments to offer
for the use of exceptions to the reluctant?

One obvious disadvantage of your 'error code' solution is that
it doesn't implement 'stack unwinding' i.e. automatic destruction,
whereas exceptions do.

-Mike
 
A

Alf P. Steinbach

* Shane Groff:
I know this is a recurring discussion (I've spent the last 3 days
reading through threads on the topic), but I feel compelled to start
it up again.

After reading through the existing threads, I find myself convinced
that exceptions are a better mechanism, for example because
constructors and operators can't return errors

Constructors can produce error codes. A constructor can take a reference
or pointer argument, or it can store an error code somewhere else. One
main reason why that is not a _good idea_ is that the built-in mechanism
for cleaning up when an objected is allocated dynamically and the
constructor fails, requires an exception from the constructor, and that
similar techniques implemented by oneself have the same requirement; without
such automated cleanup there is a live but invalid object around.

Another main reason is that the approach is not general: additional arguments
cannot be tacked on to a copy constructor or, as you write, an operator, so
these would have to store their error codes in some accessible place.

The "live but invalid" argument is also the main argument for not using
two-phase construction (except when encapsulated by an object factory).

and code that doesn't
need to handle/translate/recover in the face of errors, but can just
propagate the error is cleaner with exceptions.
Yup.


However, those reasons are often not compelling enough to convince
people who don't like exceptions (usually in my experience because
they incorrectly think that exceptions are more 'dangerous' somehow
than error codes, when in fact, they just don't see that it is just as
dangerous to ignore an error code as it is to not handle an exception
when it should be).

Consider this idiom when using error codes:

ErrorCode EcFoo()
{
ErrorCode ec;
Check(EcBar());
Check(EcBletch());
...
Exit:
return (ec);
}

Here, Check is a macro that checks the return code and jumps to Exit
if an error occurs.

Macros are evil.

An alternative is to make the error code an object that requires a check()
or value() or succeeded() call or something in order not to throw an
exception.

Some folks have advocated that approach e.g. because it makes the code more
explicit; one main drawback is that all code everywhere must then check every
function call for possible error, giving C-style impenetrable code, and
another main drawback is that exceptions are needed for constructors and
such anyway, lest one wind up with "live but invalid" objects...
 
S

Shane Groff

Mike Wahler said:
One obvious disadvantage of your 'error code' solution is that
it doesn't implement 'stack unwinding' i.e. automatic destruction,
whereas exceptions do.

-Mike

I'm not sure I understand. If I have local objects within the
function, they will be destroyed when the function exists whether the
exit is through a normal return, or an exception. If every function
is written along the pattern above, then each function jumps to the
return and passes the error code back up the chain, and local objects
are destroyed when the function returns.
 
J

John Harrison

Shane Groff said:
I know this is a recurring discussion (I've spent the last 3 days
reading through threads on the topic), but I feel compelled to start
it up again.

After reading through the existing threads, I find myself convinced
that exceptions are a better mechanism, for example because
constructors and operators can't return errors, and code that doesn't
need to handle/translate/recover in the face of errors, but can just
propagate the error is cleaner with exceptions.

However, those reasons are often not compelling enough to convince
people who don't like exceptions (usually in my experience because
they incorrectly think that exceptions are more 'dangerous' somehow
than error codes, when in fact, they just don't see that it is just as
dangerous to ignore an error code as it is to not handle an exception
when it should be).

I think your reasoning is spot on. I've seem many examples where code
appears to be checking for errors because there are loads of if statements
checking return values (often this is the bulk of the code!) but when you
actually test it doesn't work because somewhere in the chain of function
calls a return value check was missed, or because the code has been hacked
around the cleanup after detecting an error doesn't work correctly.
Consider this idiom when using error codes:

ErrorCode EcFoo()
{
ErrorCode ec;
Check(EcBar());
Check(EcBletch());
...
Exit:
return (ec);
}

Here, Check is a macro that checks the return code and jumps to Exit
if an error occurs.

In C++ you are not allowed to jump over a declaration. So this would force
you into C style code where all the declarations are at the start of a
block.

john
 
M

Michael Kurz

Shane Groff said:
"Mike Wahler" <[email protected]> wrote in message
I'm not sure I understand. If I have local objects within the
function, they will be destroyed when the function exists whether the
exit is through a normal return, or an exception. If every function
is written along the pattern above, then each function jumps to the
return and passes the error code back up the chain, and local objects
are destroyed when the function returns.

You are right. The stackunwinding is only needed when you throw an exception
somewhere several levels deep in a callstack and the exception hanling need
to unwind all these calls in progress. In your sample this is not a problem.
I think goto would not allow this anyway.


Regards
Michael
 
M

Michael Kurz

Shane Groff said:
Consider this idiom when using error codes:

ErrorCode EcFoo()
{
ErrorCode ec;
Check(EcBar());
Check(EcBletch());
...
Exit:
return (ec);
}

Here, Check is a macro that checks the return code and jumps to Exit
if an error occurs.

Using this pattern, we avoid the appearance of a slew of if/then
statments (although they are there, of course, just hidden by the
preprocessor).

This makes the code almost as straightforward as the exception case,
and doesn't introduce much overhead (space or time), since it is
basically just adding a check for 0 to each call.

This addresses some of the problems I've seen leveled against error
codes. Although it seems to me that we're just writing what amounts to
our own equivalent to exceptions here (for example, functions that
don't handle the error don't need to do any 'additional' work to
propagate the error, assuming they are following the pattern), some
people are more comfortable with this because they know that returning
and checking for error codes is always fairly inexpensive, while
throwing an exception can be expensive.

Any specific thoughts on the idiom, or compelling arguments to offer
for the use of exceptions to the reluctant?

I have seen these kind of error handling many times in code, but somehow I
never liked the necessary goto statement.
So I never used it myself.

When I have to decide if I should use exception handling or error codes I
have mostly the following things in mind:
- Error codes are self documenting whereas exceptions are not.
- Exceptions are slower so if its within performance critical code I dont
want to open a try/catch block all the time.
- Exception lead to a cleaner caller code.
- C++ constructs throw exceptions themselfes (new) so If not using
exceptions as error communication these should be converted to error codes
or avoided (hard to achieve), which is quite an effort and a possible
performance problem again .
- Make code (caller) exception safe requires some knowledge. After reading
Herb Sutters "Exceptional C++" I wondered why my programs ever worked
properly ;-)


IMHO at the end its a mixture of the requirements your code has to foolfill
and a bit of personal taste.



Best Regards
Michael
 
M

Mike Wahler

Shane Groff said:
I'm not sure I understand. If I have local objects within the
function, they will be destroyed when the function exists whether the
exit is through a normal return, or an exception. If every function
is written along the pattern above, then each function jumps to the
return and passes the error code back up the chain, and local objects
are destroyed when the function returns.

Right. But with exceptions, you don't need to write anything.
Just throw the exception and it 'just works'.

-Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top