Should I use exceptions instead of error codes?

M

mike3

Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?
 
A

anon

mike3 said:
Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?

I like this paper about exception handling:
http://neil.fraser.name/writing/exception/
 
P

paolo.brandoli

Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?

Personally I prefer exceptions to error codes. Error codes could
remain undetected, causing an error to propagate.
Exceptions cannot go undetected: or they can but you have to
explicitly code that.

If an undetected error in the package could cause a big mess, then I
would report the error using an exception.
I prefer an application crash with a big bang because of an uncaught
exception than a big mess that acts nicely and goes undetected.

I also think that an application is easier to read when its functions
return the intended return value with the "return" statement, instead
of saving it in some parameter passed by reference.

You could also implement some macros that report the stack trace
traveled by the exception (chained exceptions, or http://www.ddj.com/cpp/191100567
without chaining the exceptions)

My 3 USD cents (or 2 eurocents)
 
P

Pete Becker

Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?

You should begin by listing the possible errors. Off the top of my
head, I only see two: running out of memory, and attempting to divide
by zero. The first throws an exception, and you can't fix it, so don't
try. The latter should probably also throw an exception.
 
J

Joe Greer

Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?

The rule of thumb that I use is to ask myself if what I want is stack
unwinding or not. That is, do I expect my immediate caller to be able
to handle this problem, or is it more likely to be handled at a higher
level. If I don't expect my immediate caller to be able to handle it,
then exceptions are the best answer for error handling. If I expect my
immediate caller to be able to fix it, then error codes is the way to
go. Sometimes you end up with a sometimes yes, sometimes no sort of
answer and you then pick your favorite.

Exceptions require a certain amount of overhead to throw, so if you
expect to be having the error a lot, then throwing an exception may not
be your best alternative. Of course, if you expect the error a lot, it
is also usually the case that the immediate caller can handle the
problem and move on, so the first guideline catches that, but it is
still a concern.

HTH,
joe
 
M

mike3

(e-mail address removed):







The rule of thumb that I use is to ask myself if what I want is stack
unwinding or not. That is, do I expect my immediate caller to be able
to handle this problem, or is it more likely to be handled at a higher
level. If I don't expect my immediate caller to be able to handle it,
then exceptions are the best answer for error handling. If I expect my
immediate caller to be able to fix it, then error codes is the way to
go. Sometimes you end up with a sometimes yes, sometimes no sort of
answer and you then pick your favorite.

Exceptions require a certain amount of overhead to throw, so if you
expect to be having the error a lot, then throwing an exception may not
be your best alternative. Of course, if you expect the error a lot, it
is also usually the case that the immediate caller can handle the
problem and move on, so the first guideline catches that, but it is
still a concern.

HTH,
joe

And that last part is the thing. See, for the bignum package, there
are 3 possible types of errors: overflow, out of memory, divide by
zero.

The main concern is because the standard library functions (in
this case, "vector") may throw an exception if they fail, yet the
bignum routines return error codes for errors that are produced on
their level, and isn't that mixing two types of error handling in a
precarious way?

Also, the overflow and divide-by-zero conditions may occur more
commonly in this than you might think, at least with certain
specific types of fractals. For example, consider the iteration
of z = sin(z) on the complex plane. It may diverge wildly --
tetrationally,
to be precise -- and hence hit the ceiling real quick like you
wouldn't believe. If the "bailout" value (threshold for when the
number is considered large enough to be considered "diverged"),
is set high enough, this will max out pretty quick, generating
an overflow error. Of course once this is caught the iteration
algorithm should return that the point diverged and halt calculation,
so for each pixel there may not be a significant performance loss
even if the overflow is signaled via exception, as the loop does
not continue.
 
P

peter koch

Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?

For your application, I see no choice but exceptions.
First, if you want to be able to write your bignum expressions in a
natural way, e.g. for using operator overloading, an exception is the
only way to throw an error. operator* can not return an errorcode, and
you would have to use some ugly hack - e.g. by setting a global error-
variable.
The second reason is that a library in general should use exceptions
as the default error-handling mechanism as it is unlikely that the
error can always be handled locally. This second point is not an
universal truth, but the result of my experience in that area.

/Peter
 
P

Pete Becker

For your application, I see no choice but exceptions.
First, if you want to be able to write your bignum expressions in a
natural way, e.g. for using operator overloading, an exception is the
only way to throw an error. operator* can not return an errorcode, and
you would have to use some ugly hack - e.g. by setting a global error-
variable.

Or use the hack that floating-point math typically uses: return a
special value, not-a-number, that persists through subsequent
arithmetic operations. Then the user just has to check for it at the
end.
 
P

peter koch

Or use the hack that floating-point math typically uses: return a
special value, not-a-number, that persists through subsequent
arithmetic operations. Then the user just has to check for it at the
end.

Right - that is also a solution, if you call it a hack. I tend to
agree when we talk about NaN ;-)

/Peter
 
K

Kira Yamato

Or use the hack that floating-point math typically uses: return a
special value, not-a-number, that persists through subsequent
arithmetic operations. Then the user just has to check for it at the
end.

This solution sounds elegant, but oh boy is it hard to find where in
the possibly long and convoluted sequence of calculations did the
illegal operation originated!

And why persist in the looong computation once you've reached a NaN already?

I suggest just throw an exception and end the misery early. Moreover,
debuggers can be setup to break upon an exception being thrown. Or
with the proper API's (system dependent), you can dump the call-stack
at runtime in the exception class constructor.
 
M

mike3

On 16 Nov., 11:14, mike3 <[email protected]> wrote:
For your application, I see no choice but exceptions.
First, if you want to be able to write your bignum expressions in a
natural way, e.g. for using operator overloading, an exception is the
only way to throw an error. operator* can not return an errorcode, and
you would have to use some ugly hack - e.g. by setting a global error-
variable.
The second reason is that a library in general should use exceptions
as the default error-handling mechanism as it is unlikely that the
error can always be handled locally. This second point is not an
universal truth, but the result of my experience in that area.

/Peter

However, overloaded operators are not the only way of
accessing the library. The other method is the use of
member functions, especially special "fast" member
functions. Those fast functions, since they are not
overloaded operators, _can_ (and in the present
implementation, _do_) return error codes. These "fast"
functions have various optimizations included.

But now then I have two ways functions can report errors:
exceptions _and_ error codes (although even error code
functions may throw exceptions if the standard library
code they use (if any) fails somehow -- but that's a
feature of the standard library.). Is this a bad idea
since then one needs extra handlers for both? However, I
also have it so error codes returned are objects with member
functions, one of which can throw the error as an
exception. This way I can handle the errors in either
way if one way is more convenient than the other.
 
J

James Kanze

On 2007-11-16 19:27:37 -0500, Pete Becker <[email protected]> said:
This solution sounds elegant, but oh boy is it hard to find where in
the possibly long and convoluted sequence of calculations did the
illegal operation originated!

It may not be important. For bad_alloc, I think an exception is
definitly in order, but in many cases, propagating a result of
[+-]Inf is sufficient. You don't need to know where the "error"
occured, only that the results are "infinitly large".
And why persist in the looong computation once you've reached
a NaN already?

That's not necessarily the problem. The real problem is that
handling the special case in all of the operations can be a real
performance drag, unless (as is the case with IEEE floating
point) it's done with special hardware (in which case it adds to
your gate count).
 
P

Pete Becker

This solution sounds elegant, but oh boy is it hard to find where in
the possibly long and convoluted sequence of calculations did the
illegal operation originated!

Well, no, it's not hard when you're debugging. Just install a trap handler.
And why persist in the looong computation once you've reached a NaN already?

Speed. Correct code with correct data doesn't produce NaNs.
 
K

Kira Yamato

Well, no, it's not hard when you're debugging. Just install a trap handler.

Ok. I will buy this.
Speed. Correct code with correct data doesn't produce NaNs.

Care to explain how checking for special values (NaN, Inf) for every
arithematic operations can result in speed up? Wouldn't the code need
if statements to handle
NaN + NaN = NaN
Inf + Inf = Inf
Inf - Inf = NaN
Inf * Inf = Inf
Inf / Inf = NaN
and so on?

In fact I will say having special values will slow down computations
(unless your hardware supports it). In this case, the OP is writing
software here.
 
J

jehugaleahsa

Hi.
(crossposted because the program is in C++ and some C++-related
elements are discussed, hence comp.lang.c++, plus general program
design questions are asked, hence comp.programming.)

I'm making this bignum package in C++. I'm wondering though on how to
handle the errors. Right now most operations routines return error
codes if they fail -- but some routines they use inside them, or
overloaded operators, will throw exceptions on failure. For example,
the C++ standard library routines that get used, for instance to copy
vectors or pieces of vectors of digits. These may throw on failure.
Would it be good to then use a consistent system of error handling
where bignum ops always throw exceptions instead of returning error
codes, instead of having some failures throw exceptions and other
failures release error codes (the exceptions would be coming from the
standard lib. functions for example)? What is the "ideal" plan for a
bignum package, anyway?

Why not provide both if that is an option? Another thing to consider
is where this code is being used. Now, I personally wrote/copied/
incorporated my own BigInteger class. I used exceptions because I am
using it in a high-level language that expects to see things like
exceptions. However, if you are using an older version of C++ or are
working with low-level routines, error codes can be much better
suited. If you are writing the code for somebody else, ask them what
they want. If you are writing it for yourself, ask yourself what
methodology your prefer.

People who use exceptions rarely understand the responsibility that
can go along with them. Exception-safe code is a sought after ideal
that is rarely realized by novices or even thought of by most
programmers in general. However, the same thing can apply with error
codes. Many beginner C programmers rarely even check for errors like
bad file-reads/opens/writes and continue on with the program until it
dies unexpectantly. Exceptions at least have the benefit of preventing
programmers from ignoring potential issues and stop the program where
something goes wrong.

Exceptions are becoming faster with each new implementation of C++ and
they are almost cost-free in Java and C#. So don't let the warnings
about their inefficiencies scare you away from using them. Remember,
premature optimization is the root all evil (or was it that irrational
numbers were the square root of all evil). Modern programming style
(which is controlled by a bunch of big-headed project consultants and
authors) says exceptions are the way to go. Be careful about who you
decide to listen to. I still say it should always depend on your
environment, including the skillsets of your coworkers, self and tools.
 

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

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top