Error codes vs. exceptions

M

Mike McCarty

Still, I think you generally miss the point. When you write code, you
cannot know a-priori whether the *failure* case for a certain (library)
function is in a performance critical code path. So if a (set of)
certain (library) function(s) reports all failures/errors via
exceptions, this can introduce a significant performance overhead for a
critical code path where the failure of a certain function is actually
expected/probable.

Sounds to me like a pretty good rule-of-thumb: If you find yourself worrying about the performance impact of throwing an exception, the condition is probably not truly 'exceptional' and so should not be handled via exceptions. (Either that or you're prematurely optimizing)

If it is a third-party library that is throwing the exception, complain to them that their error handling paradigm leaves something to be desired.


Mike
 
R

Rui Maciel

Mike said:
Sounds to me like a pretty good rule-of-thumb: If you find yourself
worrying about the performance impact of throwing an exception, the
condition is probably not truly 'exceptional' and so should not be handled
via exceptions.

Indeed. If an exceptional event is so frequent that it has a noticeable
impact on performance then it is safe to say that the event isn't that
exceptional.

And I would go a bit further than this; if an error is triggered so
frequently that the number of times it is caught actually has a noticeable
impact on performance then the developers certainly have more pressing
problems to deal with than worrying about the efficiency of their error-
handling code.


Rui Maciel
 
M

mike3

mike3 wrote:
As an example, it's a good idea to write a lexer so that, when stumbling on
gibberish, it returns an error code signaling just that.  By doing so, it is
possible to set the parser to handle the not so exceptional event of parsing
an ill-formed document, which might include the ability to throw a context-
dependent error message or even resume parsing. This wouldn't be easy or
simple to pull off if your lexer threw an exception when stumbling on
gibberish.

So then does this mean that it's probably a good idea to base the
choice on
which to use (error codes or exceptions) on which one is simpler and
clearer
for the task at hand?
 
M

mike3

mike3 wrote:

That sounds like a recipe for a mild disaster.  After all, if you use atool
without even thinking if it is the right one for your job then how do you
even know it is a good idea to use it?

What I was referring to was the apparent existence of a level of
subjectivity
and of a number of "camps" that i see here: I see debates between the
"error code-ists", "exception-ists", and "mixers", which seems to
suggest
there is no "pat answer" to the problem. Thus I mentioned the need to
pick a camp and stick with it as you can't just keep debating in your
head
and on forums as to which one is best as you just won't get anywhere
that
way. Yu mentioned earlier in your post that the choice is a "design
decisioh",
but that doesn't seem to affect the existence of preferences for one
approach
or the other to making those design decisions with reasonings that
vary from
individual to invididual, i.e. things which are subjective. And it is
in the
_subjective_ parts where you have to "pick and choose". If there was a
pat,
objectively "most logical" option, then there'd be little to no
debate. Yet this
thread has gone for over a hundred messages now. So obviously there is
quite a bit of debate.
A decent rule of thumb that helps see which solution is better suited is as
follows:
- if you are dealing with exceptional circumstances which can be omitted
from the control flow of your code without jeopardizing clarity or
readability (i.e., memory allocation errors, sanity checks failing in an
unexpected way, network connections breaking up, etc...) then exceptions may
be a good way to go.
- if, instead, you are dealing with expectable errors which directly
influence the control flow of your code to the point you can't express an
algorithm without explicitly covering them then returning error codes maybe
a good way to go.

Other than this, it's a matter of understanding how exceptions and error
codes work, particularly their downsides, and make a design decision on
which one to pick to handle a particular error.

So where can one get a better understanding of that, and where can one
find
the downsides (and upsides!) of the two items?
 
G

Gerhard Fiedler

mike3 said:
So then does this mean that it's probably a good idea to base the
choice on which to use (error codes or exceptions) on which one is
simpler and clearer for the task at hand?

This is what I do, mostly -- besides having an overall exception design.

For me, and this has already been said here a few times, return codes
are something local, typically a function that returns a status of some
sort that is directly used by the immediate caller, often as integral
part of the caller's logic. In such cases, exceptions are not useful.
OTOH, when the user of the error information is not clear and may be
several call levels above and stack unwinding is desired, exceptions are
a clear winner. There are some in-between cases, but IME they are not
that frequent -- and it's mostly a choice of (personal or company or
existing code base) style.

For example, I've seen both exceptions and conditions being used to end
a recursion. Depending on the case, to me both have their merits here.

Gerhard
 
R

Rui Maciel

mike3 said:
So then does this mean that it's probably a good idea to base the
choice on
which to use (error codes or exceptions) on which one is simpler and
clearer for the task at hand?

If you really need a conservative rule of thumb to help you decide where and
how to implement exceptions then I would suggest the following rule: employ
exceptions to handle memory allocation errors and sanity checks, and for
every other case just default to employing error codes until your experience
and understanding of exceptions tell you otherwise.

With this rule of thumb, you will end up employing error codes in a good
number of cases where exceptions would be a far better fit, but at least no
one can really criticize you for implementing a technique which is
extensively used in other domains.


Rui Maciel
 
N

none

BTW: unlikely to be the Google developers that wrote it but more
likely to be some kind of compromise reached by committee :-(
We had a pretty long thread here titled 'We do not use C++ exceptions at
Google'. No, it was on .moderated, early 2009.

The conclusion was that this guideline is a major WTF, and most likely due
to Google got locked in with a huge rotten codebase. And selection of staff
too, with that rule not trained to know about exception safety and the rest.

Indeed. IMNSHO, Google style guide are not suitable for general use.
They may very well be suitable for the specific case of Google
specific[1] code base and Google specific[2] staffs but they are bad
guidelines for general uses. Unfortunately, because it is published
by Google on Google, far too many peoples appear to consider them as
quality generic guidelines.

In a related note, another style guide that is often cited is the
JSF's ones. They are a very different animal, written for a very
specific purpose. They are good for this specific purpose but again
should not be used for generic C++ programming.

Yannick
 
M

mike3

If you really need a conservative rule of thumb to help you decide where and
how to implement exceptions then I would suggest the following rule: employ
exceptions to handle memory allocation errors and sanity checks, and for
every other case just default to employing error codes until your experience
and understanding of exceptions tell you otherwise.

With this rule of thumb, you will end up employing error codes in a good
number of cases where exceptions would be a far better fit, but at least no
one can really criticize you for implementing a technique which is
extensively used in other domains.

However, this rule seems too strict, as there are places where
exceptions really
seem like a good idea -- e.g. returning any error from a constructor
or overloaded
operator.

What I'm curious about is just what kind of bad things happen when you
use
exceptions _wrong_, when you use them where they're _not_ a good fit,
and what
signs to look for that may indicate you are using them wrong, and an
error code
would really be better.

Or even better, what's the way to do this that does _not_ involve
boiling it down to
"hard rules".
 
N

none

Oh, sure. For example, if your error processing policy requires assigning
severity level to every error before logging it, you will surely be happy to
write a catch block where the severity is best known to be assigned, won't you?

Now think where in the code this place could possibly be. A little hint:
"generally" it's not in main(), regardless of whether you are handling
std::exception or other exception (unless your app/library is so small that you
"generally" call functions from other libraries in main()).

I think your problem is that you are implying and assuming that the
the best place to handle the error (assign a severity value, etc.) is
in the immediate caller.

You may be correct that it is not "generally" in main but I disagree
with you implication that it is "generally" in the immediate caller.

With exception: you can catch and handle the error where appropriate
With return status code: you must check for the error in the immediate
caller even if you are unable to du anything about it. If it is the
case, you must then anreturn another error and so on.
a. It won't be called at the first place, because a specific exception type
won't be caught.

Generally spkeaing, catch statements should be "rare" (as in not
around every single function call). Where you catch, you may want to
catch probably one of:
- all the exceptions types you know how to handle/fix
- all exceptions (with multiple catch) and handle or log all.
Re-throw if desireable.
b. Even if it were somehow auto-magically called, it often would not have enough
data to fill up the logging function's arguments or the dialogue fields without
cracking the exception or error code.

What about:

catch(...) { log << "Critical Fatal Total Error: unknown exception. We
don't know how and why it has happen so PANIC! is the best answer!"; }

This sounds reasonable to me. If some error occured and you don't
even know that such an error is possible, then you really should
consider it as critical.
It is instructive that in some libraries designed for critical applications by
serious ISPs (e.g. IBM's MQ Series C API), the error codes were returned in
output parameters. A programmer using such API does not have a chance to fully
ignore error conditions because s/he needs to define a variable (sometimes more
than one) to hold error codes. IBM's APIs are a good example because no other
company has had more experience with maintaining huge enterprise-level
applications depending on multiple libraries. It's also instructive that some
'newer-style' and best-usable POSIX C API calls have been re-designed same way
(think of strtol() that all but replaced atol()).

I don't consider design choice for a pure C API where exceptions don't
exist as particularly relevant to a discussion on C++ error handling
which has built in exceptions and where exception is the default
language behaviour.
The above paragraph is not talking about exceptions but its subject is directly
related to the topic. Namely, if we arrange APIs error exposure design decisions
in the order from less to more prominent manifestations of possible error
condition by API functions, our order will look approximately as follows:

This is totally backward:
1. errno, GetLastError(), global variables, C++ functions without exception
specifications that throw and the likes. A programmer is not reminded about
possible error conditions in any way except possibly for the documentation.

Are you seriously grouping exceptions with errno? This is hard to believe.
2. C++ functions with exception specifications. A programmer is reminded about
possible error conditions if s/he has function prototype in front of him or her
while coding the call.

3. Error return codes, A programmer is reminded about error condition as in #2
plus they may be able to turn on some "unused function return value" compiler
warning or the like.

4. Errors are encoded in output parameters of API. A programmer cannot
unwillingly ignore an error condition.

This is so back to front. Exceptions absolutely can't be ignored
passively. This is one of the strongest quality of exception above
either return code or output parameters.

Error ouput parameter are easy to ignore: you just don't test them
after the function call.

One of the big problem with return code, errno or even error output
param is that nothing in the code indicates that the programmer made
the deliberate choice of ignoring the error rather than just being
lazy.

With expection, the error will *always* interrupt normal processing
*unless* the programmer explicitely write code to resume normal
processing. This is a *good* thing.

The corrected list above is:

1- errno, GetLastError(), global variable: programmer need to test
something that is outside current scope and location. Programmer needs
to explicitely call a method or read a variable that is unrelated to
current processing in order to check if an error has occured.

2- return code: very easy to ignore or forget about. How often have
you seen the return value of printf() being ignored? Error checking
needs to be done manually.

3- error output parameter: because you have to explicitely define the
paramter, the programmer is less likely forget and compiler flags may
raise a warning.

4- Exceptions: programmer can't ignore them passively. The only way
to ignore exceptions is to explicitely catch them all and
continue. Error checking is automatic.
The code to capture error codes is simpler,
shorter and faster than that needed to capture exceptions.

I don't see how it can ever be less code than exception handling[4].
- It is simpler because error codes are easier tabulated for their
classification or another mapping (potentially driven by the rules that are
configured externally to the program and that are to be applied to programs in
other languages than C++) to the entities required by the application error
handling policy.

- it is shorter (especially with error-code-based or errno-based approach to
error propagation) because well-encapsulated error-checking code is notably
shorter than equally well-encapsulated try {} catch{} code. E.g. compare:

Only for poor quality code!
if (!handleError(apiCall(args..)))
return;

or

if (!apiCall(args..) && !handleError(errno))
return;

to

try {
apiCall(args..);
} catch(XxxException &e) {
if (!handleError(e))
throw;
}

Arggh! Typical problem with error return code apologists. You are
doing it wrong. You are unable to change your mindset of handling
error for each function call one by one. Write the normal path,
handle errors where appropriate. The above is an perfect example of
poor quality code.

Exception code should (at worse) look like:

void foo()
{
try
{
// Normal path
// several lines of code
// implement all the normal processing logic here sequentially.

}
catch( /* something */ )
{
// handle errors
}
}

In many cases, it should look like:

type2 foo1()
{
bar1();
bar2();
bar3();
type t = bar4();
type2 z = bar5(t);
return z;
}

bar1()
{
buzz1();
buzz2();
// ...
}

boss()
{
try
{
type2 t = foo1();
foo2(t);
// ...

}
catch( /* stuff */)
{
// handle errors

}
}

The amount of error handling code can easily become 1/4th to 1/10th of
the amount used by return status code style programming.
/* if apiCall does not encapsulate exceptions from *its library's* underlying
libraries, the above code would be even longer */

- it is fater because compiler can optimize nothrow code. In a (probably futile)
attempt to prevent pointless objections to this statement I am referring you to
the Ultimate Authority of the Standard, see a footnote to 17.4.4.8-2 in ISO/IEC
14882:2003(E) or 17.6.5.12-3 in ISO/IEC 14882:2011(E) about the reason why C
library functions shall not throw:

Why are you talking about C library functions? Of course C library
functions shall not throw. C doesn't have exceptions. We are
discussing C++ here!

It's slower because you have 100 if/else statements in the normal path
where I only have 1 try/catch

It is slower because in the normal success processing path, you
repetitively have to check for error while with exceptio

e.g. compare:

// Critical loop:
for(int i = 0 ; i < BIG_NUM ; ++i )
{
int retVal = foo();
if(SUCCESS != retVal)
{
// handle error
// break/return/exit?
}
}

vs

try
{
for(int i = 0; i < BIG_NUM; ++i)
{
foo();
}
}
catch(...)
{
// handle error
}

The normal path has at least 1 more assignment and 1 more test. Things
become even worse if the function actually return a value (see below)

It's slower because you have to define a object with dummy value, pass
it as argument to a function, copy data into it then finally use it
while I initialise the object directly with valid value and the
compiler can optimize the code with NRVO/RVO

It is slower because the compiler can't optimize code as nothrow
simply because there are no explicit throw/try/catch statements in
your code. If anywhere down the line something uses the C++ standard
library, new or an allocator, then exceptions may occur.
".. This allows implementations to make performance optimizations based on the
absence of exceptions at runtime."

In C++, that's playing ostrich. (as in bury your head in the sand and
claim it's not there because you can't see it) You should always
assume the any function call can throw.
Whom do you mean when you say "you're"? Me a library writer or me a library
user? Me a library writer is supposed to go extra mile to better serve a library
user. Me a library user will have to do more and more complex things when I use
exception-based API which my this and previous and previous-before-previous
posts explain at monotonously increasing and unnecessary for a reasonable reader
level of details.

Strongly disagree. As a library user it is much simpler to use an API
that is exception based than an API that is return code based.
It allows you to "mark everything nothrow" without writing try-catch blocks.
Marking functions nothrow was not counted as part of the cost because it had to
be done regardless of the type of the underlying API if the decision to write
nothrow functions has been made.

There you are again playing ostrich. As discsussed by others in this
thread, if you want nothrow, you must never use new or the standard
C++ library anywhere and you must enable the "no exception" flag in
your compiler. Anything else is unsafe.
due to the problem I mentioned above.
As the rest of the paragraph explained, in increases your productivity by
reducing the time you need to read and understand the code with only explicit
exit paths. Oh, I forgot, you are not spending much time reading code.. this "on
top of it" advantage is probably not for you then.

Strongly disagree. In my experice, reading exception based code is
much simpler since I can read the normal success path and concentrate
on the sanity of the success scenario independently of the error path.
I can then read the error path and concentrate on the sanity of the
error path. And since the code is typically significantly smaller, I
have to spend less time reading.

Typical error return based code in C++ is a nightmare to review. It
is typically exception unsafe because it blindly assume that because a
specific function implementation does not has explicit throw
statements, then exceptions can be ignored. As a result the code will
often be totally exception unsafe and if anything below generate an
exception, then everythign breaks.

Even ignoring this simple fact, the repetitive "doA, check if A
succeeded, handle A-type error, decide if processing can continue, if
not release previosuly acquired resources, doB, check if B succeeded,
handle B-type error, decide if processing can continue, if no release
previously acquired resources including those acquired for doA, ... "
is very verbose and very error prone.
Really? Valid C++ code that does not throw any exceptions itself and calls only
functions with an empty exception specification is guaranteed not to throw an
exception. Incidentally not throwing an exception *is* the definition of nothrow
exception safety guarantee.

please review what "nothrow" actually does and what are the
consequences if a function declared as nothrow does let an exception
escape.
I am not sure about that vague "merely not throwing exceptions" but exposing
only no-throw functions from a library API (which was my original and only
point) brings all the benefits I claimed.

It brings some benefits for some style of programming. It however
bring cost for some other style of programming. I prefer an library
that uses exceptions. Much easier to use.

Let's take the Eigen library for an example:

MatrixXd m(2,2);
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);

Very usable. Now let's try to rewrite this with error code:

MatrixXd * pm = MatrixXdFactory(2.2);
if(NULL != pm)
{
int retVal = 0;
retVal = pm->Set(0,0,3.0);
if(0 == retVal)
{
retVal = pm->Set(1,0,2.5);
if(0 == retVal)
{
retVal = pm->Set(0,1, -1.0);
if( 0 == retVal)
{
float v1 = 0;
retVal = pm->Get(1, 0 , &v1);
if( 0 == retVal)
{
float v2 = 0;
retVal = pm->Get(0,1, &v2);
if(0 == retVal)
{
float v3 = v1 + v2;
retVal = pm->Set(1, 1, v3);
if(0 == retVal )
{
// Success!!!!
}
}
}
}
}
}
}
}
}

// handle error?
return retVal;

Yuck!

Feel free to rewrite the above with error output parameters to
demonstrate how much better again the code would be and how much
easier to read and review.

Yannick
 
R

Rui Maciel

Adam said:
Because it can potentially throw exceptions[1] and Qt is mostly
exception unsafe. You're tilting at windmills,

The problem I described had absolutely nothing to do with Qt being
exception-safe or not. Exception safety refers to how operations should
leave objects in a valid state once an exception is thrown.[1] This was not
what happened, let alone what caused the problem. The state in which any
object was left after the throw was immaterial to killing the process. In
fact, the process would even be terminated if a mock exception was thrown in
a try-block where no object was touched, let alone left in an invalid state.

The problem was due to something similar to:

<code>
catch(...)
{
qWarning("blabla exception caught");
exit(0);
}
</code>

This represents a problem, but it obviously has nothing to do with exception
safety of any operation of any Qt component on any object.


Rui Maciel

[1] http://www.research.att.com/~bs/except.pdf
 
P

Pavel

none said:
I think your problem is that you are implying and assuming that the
the best place to handle the error (assign a severity value, etc.) is
in the immediate caller.
Not for the error generated by the code under your control, no. You may have
good-for-your-purpose conventions on error models and error processing. You can
catch and process *your* errors wherever appropriate -- as you rightly describe
below.

But more likely the not an arbitrary selected library you are using does not
obey your error conventions. This is not a library writer's fault. Objectively,
a library cannot follow many useful conventions (e.g. error severity is often
specific to client code's context and cannot be determined in the library).

Therefore, you often need to *convert and enrich* error conditions signaled by
several used libraries, each according to its own conventions, to your
conventions. *These conversions* is what is usually best encapsulated in the
immediate callers (to minimize the amount of code exposed to foreign error
conventions which in turn minimizes the scope of the changes required when you
switch to another library or a new version of same library or add to or replace
your code surrounding the library calls).

Because I have been changing library writer and library user hats often and, as
library user, I found non-throwing libraries much more pliant to conversions of
their errors to the conventions required by my code, I follow the golden rule
when I am designing libraries: do to other library users what I want library
writers do to me when I am a library user -- that's it.
You may be correct that it is not "generally" in main but I disagree
with you implication that it is "generally" in the immediate caller.

With exception: you can catch and handle the error where appropriate
With return status code: you must check for the error in the immediate
caller even if you are unable to du anything about it. If it is the
case, you must then anreturn another error and so on.


Generally spkeaing, catch statements should be "rare" (as in not
around every single function call). Where you catch, you may want to
catch probably one of:
- all the exceptions types you know how to handle/fix
- all exceptions (with multiple catch) and handle or log all.
Re-throw if desireable.
The above is fine -- as long as the exception type hierarchy is under your
control (that is, higher than the conversions at the call stack).
What about:

catch(...) { log<< "Critical Fatal Total Error: unknown exception. We
don't know how and why it has happen so PANIC! is the best answer!"; }

This sounds reasonable to me. If some error occured and you don't
even know that such an error is possible, then you really should
consider it as critical.
It is reasonable if you cannot possibly catch this error where you have useful
information about the error's meaning *for you* (and off the bat I cannot come
up with a case where it would be *technically* impossible).

If, which is often the case, it was your choice to catch that error in a place
where you cannot read its meaning-for-you -- I would not call it reasonable. I
would rather call it a design error.
I don't consider design choice for a pure C API where exceptions don't
exist as particularly relevant to a discussion on C++ error handling
which has built in exceptions and where exception is the default
language behaviour.
I am not sure I understand this statement. In my opinion, exceptions are a C++
feature, yet another tool made available to practitioners, not a behavior.

If what you mean is that the default operator new does not return 0, it is a
reasonable (although maybe not the best) default choice *for this particular
error, which is unique in that most applications will want to terminate when
they run out of memory* (I am saying "maybe not the best" because if I do want
to terminate, I would prefer to abort() (which dumps core, on many systems) as
close as possible to the call site of the failed memory allocation call to
investigate the problem easier).

On the other hand, the other errors, such as i/o or formatting errors (to which
category strtol() mentioned by me falls) are *not* signaled via exceptions by
default in C++ -- for example, you have to call exceptions() on a stream to make
it throw -- which is, I think, also reasonable. It is unfortunate, however,
that, at least in practice, you have to pay for the cost of calling potentially
exception-throwing functions even when you do not turn any stream exception on.
This is totally backward:


Are you seriously grouping exceptions with errno? This is hard to believe.

In terms of giving a programmer a syntactic hint about error condition an API
can raise? Of course I do. Namely, neither of the above gives any syntactic clue
to an API user whether a function call can raise an error condition and, if yes,
how to catch it.
This is so back to front. Exceptions absolutely can't be ignored
passively.
You are probably talking about run-time. I am talking about coding time. At
coding time, exceptions perfectly can be ignored (and in fact quite often "the
can" is being "kicked down the road"). When this happens in a library (I mean,
ignoring the exceptions thrown by a library underlying this library), the onus
is put to the library user -- often without any notice and for their own money.
This is one of the strongest quality of exception above
either return code or output parameters.

Error ouput parameter are easy to ignore: you just don't test them
after the function call.
please see above
One of the big problem with return code, errno or even error output
param is that nothing in the code indicates that the programmer made
the deliberate choice of ignoring the error rather than just being
lazy.
Same with the unspecified exceptions -- nothing *in the code* indicates such
deliberate choice (this is somewhat specific to C++; Java is notoriously different).
With expection, the error will *always* interrupt normal processing
*unless* the programmer explicitely write code to resume normal
processing. This is a *good* thing.
Now you are talking about run time. Also your model of "a programmer" is
limited; in a real-life application it is often that there is more than one
programmer in the picture: programmer lp1 wrote library l1, programmer lp2 wrote
library l2 that calls l1 and neglected to catch any exceptions; then programmer
ap wrote application a that calls l2 and has no clue about l2's use of l1 (it's
not ap's fault because we already know lp2 is sloppy; he did not necessary even
documented it for ap that l2 uses l1).

ap diligently catches documented exceptions of l1 at all correct points; but
once in a while his program has inexplicable problems (whose root cause are
exceptions from l1 but he won't before much later). ap just picked up the tab..

If this situation is not familiar to you, you are either extremely lucky or do
not maintain big software systems a lot.
The corrected list above is:

1- errno, GetLastError(), global variable: programmer need to test
something that is outside current scope and location. Programmer needs
to explicitely call a method or read a variable that is unrelated to
current processing in order to check if an error has occured.

2- return code: very easy to ignore or forget about. How often have
you seen the return value of printf() being ignored? Error checking
needs to be done manually.

3- error output parameter: because you have to explicitely define the
paramter, the programmer is less likely forget and compiler flags may
raise a warning.

4- Exceptions: programmer can't ignore them passively.
See above. This happens all the time and this is not even always the last
non-catching programmer's fault.
The only way
to ignore exceptions is to explicitely catch them all and
continue. Error checking is automatic.
The code to capture error codes is simpler,
shorter and faster than that needed to capture exceptions.

I don't see how it can ever be less code than exception handling[4].
- It is simpler because error codes are easier tabulated for their
classification or another mapping (potentially driven by the rules that are
configured externally to the program and that are to be applied to programs in
other languages than C++) to the entities required by the application error
handling policy.

- it is shorter (especially with error-code-based or errno-based approach to
error propagation) because well-encapsulated error-checking code is notably
shorter than equally well-encapsulated try {} catch{} code. E.g. compare:

Only for poor quality code! ?
if (!handleError(apiCall(args..)))
return;

or

if (!apiCall(args..)&& !handleError(errno))
return;

to

try {
apiCall(args..);
} catch(XxxException&e) {
if (!handleError(e))
throw;
}

Arggh! Typical problem with error return code apologists. You are
doing it wrong. You are unable to change your mindset of handling
error for each function call one by one. Write the normal path,
handle errors where appropriate. The above is an perfect example of
poor quality code.
You are missing the point. Whether or not you have to convert every error of 3rd
party library into your error is often not a choice but a requirement.
Exception code should (at worse) look like:

void foo()
{
try
{
// Normal path
// several lines of code
// implement all the normal processing logic here sequentially.

}
catch( /* something */ )
{
// handle errors
}
}

In many cases, it should look like:

type2 foo1()
{
bar1();
bar2();
bar3();
type t = bar4();
type2 z = bar5(t);
return z;
}

bar1()
{
buzz1();
buzz2();
// ...
}

boss()
{
try
{
type2 t = foo1();
foo2(t);
// ...

}
catch( /* stuff */)
{
// handle errors

}
}

The amount of error handling code can easily become 1/4th to 1/10th of
the amount used by return status code style programming.


Why are you talking about C library functions?
I am not talking about C library functions. I am talking about the fact that the
authors of the (C++) Standard recognized and explicitly acknowledged that
implementations can optimize functions explicitly declared not throwing. (From
half-empty glass perspective, by that they admitted the significance of
pessimization caused by throwing functions).

Of course C library
functions shall not throw.
It would be instructive for you to read the footnotes I referred to to learn
*why exactly they shall not throw* (from the viewpoint of C++ Standard authors,
not mine).

C doesn't have exceptions.
Even if this is a rationale of C++ Standard authors for requiring C functions to
be no-throw, it is not written down in the Standard. The performance rationale
is written down.

We are
discussing C++ here!
And I am fully aware of that. And you are fully aware that I am aware.
It's slower because you have 100 if/else statements in the normal path
where I only have 1 try/catch
It does not make sense to compare the amount of ifs in the pieces of code that
do different things. My two code snippets are comparable to each other in their
functionality (a uniform and informed handling of all error conditions of a
3rd-part library. I defined this purpose for your multiple times). Your code
snippets are doing something else. It would be useful if you defined the purpose
of your code before writing it.
It is slower because in the normal success processing path, you
repetitively have to check for error while with exceptio

e.g. compare:

// Critical loop:
for(int i = 0 ; i< BIG_NUM ; ++i )
{
int retVal = foo();
if(SUCCESS != retVal)
{
// handle error
// break/return/exit?
}
}

vs

try
{
for(int i = 0; i< BIG_NUM; ++i)
{
foo();
}
}
catch(...)
{
// handle error
}

The normal path has at least 1 more assignment and 1 more test. Things
become even worse if the function actually return a value (see below)
The Extra assignment is only in your code because error processing is not
encapsulated (see my code snippet above that does not have that assignment). Of
course, even the way you wrote it, the assignment does not have to appear in the
machine code at all.
It's slower because you have to define a object with dummy value, pass
it as argument to a function, copy data into it then finally use it
while I initialise the object directly with valid value and the
compiler can optimize the code with NRVO/RVO
With return codes, you do not have to pass extra parameters. If you allude to
not being able to return value along with parameters, you can always group the
return code into std::pair. STL containers sometimes do just that. RVO works
just fine on a pair; often the return value and return code never hit main
memory -- as long as there enough registers. Of course, return code does occupy
a register -- but exception and overhead of a throwing function's
prologue/epilogue cost much more than one register.
It is slower because the compiler can't optimize code as nothrow
simply because there are no explicit throw/try/catch statements in
your code.
Yes, it can and it does. Please refer to the Standard and study the assembler
code I posted earlier in the thread "Using printf in C++" or generate and study
the code of your own.

If anywhere down the line something uses the C++ standard
library, new or an allocator, then exceptions may occur.
That they can. What they can't is to be thrown from the function declared as
no-throw. std::terminate() will be called first (and std::terminate() does not
return -- or throw).
In C++, that's playing ostrich. (as in bury your head in the sand and
claim it's not there because you can't see it) You should always
assume the any function call can throw.


Strongly disagree. As a library user it is much simpler to use an API
that is exception based than an API that is return code based.
Not if you need to process errors according to strict policies about which the
library author did not and could not know when s/he wrote it.
There you are again playing ostrich. As discsussed by others in this
thread, if you want nothrow, you must never use new or the standard
C++ library anywhere and you must enable the "no exception" flag in
your compiler.
A compiler with enabled "no exception" flag is not a standard C++ compiler (at
least it was not according to the old standard; I did not check the new one at
this).

As for never using "new" under a no-throw function this is another fallacy,
easily disprovable in practice at that. Please try this simple test on your
favorite most compliant C++ compiler (with the exceptions turned on):


#include <iostream>
#include <vector>

using namespace std;

void
memoryBuster() throw()
{
vector<double> v(10);
for (;;) {
v.resize(v.size() * 2);
}
}

int
main(int, char*[])
{
try {
memoryBuster();
} catch(...) {
cout << "memory buster has thrown" << endl;
}

return 0;
}


With a compliant compiler, you will get:
a. with uncommented throw() specification: terminate() called and no "memory
buster has thrown" output.
and
b. with commented-out "throw()" specification: "memory buster has thrown" output.



Anything else is unsafe.
Strongly disagree. In my experice, reading exception based code is
much simpler since I can read the normal success path and concentrate
on the sanity of the success scenario independently of the error path.
I can then read the error path and concentrate on the sanity of the
error path.
And where are you going to search for the error path? Throughout the whole code
base?
And since the code is typically significantly smaller,
The code that is functionally equivalent will be of comparable size (depending
on the required functionality, slightly smaller or slightly bigger. In my
experience, when you need to adhere to a strict non-trivial error-processing
policy, it is slightly bigger).

I
have to spend less time reading.

Typical error return based code in C++ is a nightmare to review. It
is typically exception unsafe because it blindly assume that because a
specific function implementation does not has explicit throw
statements, then exceptions can be ignored.
How is this relevant to my post? My post recommends to design a library API from
no-throw functions. And, if a function is declared no-throw, then, *yes,
exceptions can be ignored*. In fact, you do not have any other useful choice
when you are calling a no-throw function *because you are never going to catch
an exception from it, neither at call site, nor in main(), nor anywhere in
between*.

As a result the code will
often be totally exception unsafe and if anything below generate an
exception, then everythign breaks.
irrelevant to my posts.
Even ignoring this simple fact, the repetitive "doA, check if A
succeeded, handle A-type error, decide if processing can continue, if
not release previosuly acquired resources, doB, check if B succeeded,
handle B-type error, decide if processing can continue, if no release
previously acquired resources including those acquired for doA, ... "
is very verbose and very error prone.
irrelevant to my posts.
please review what "nothrow" actually does and what are the
consequences if a function declared as nothrow does let an exception
escape.
Please follow your own advice and review what non-throwing functions (those
declared as throw() or equivalently) do and memorize once and for all that they
do *not* "let exceptions escape".
It brings some benefits for some style of programming. It however
bring cost for some other style of programming. I prefer an library
that uses exceptions. Much easier to use.

Let's take the Eigen library for an example:

MatrixXd m(2,2);
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);
Why don't you define the required error-processing functionality of your
application before writing any code for a change?
Very usable. Now let's try to rewrite this with error code:

MatrixXd * pm = MatrixXdFactory(2.2);
if(NULL != pm)
{
int retVal = 0;
retVal = pm->Set(0,0,3.0);
if(0 == retVal)
{
retVal = pm->Set(1,0,2.5);
if(0 == retVal)
{
retVal = pm->Set(0,1, -1.0);
if( 0 == retVal)
{
float v1 = 0;
retVal = pm->Get(1, 0 ,&v1);
if( 0 == retVal)
{
float v2 = 0;
retVal = pm->Get(0,1,&v2);
if(0 == retVal)
{
float v3 = v1 + v2;
retVal = pm->Set(1, 1, v3);
if(0 == retVal )
{
// Success!!!!
}
}
}
}
}
}
}
}
}
It is completely pointless to evaluate the code above whose only purpose IMHO is
to demonstrate strangely looking code. Neither snippet tells me anything about
how either retVal or (invisible and unhandled, contrary to all your "impossible
to ignore" statements) exception is intended to be dealt with.

Specify the possible error conditions and desired error processing policy of
your library, specify how the exception version of the API signals these
conditions and demonstrate how you implement your specified policy over it and I
will show you how to implement same policy with an alternative
(non-exception-based) error-signaling API in a competitively clear and concise
code.

Please notice that I have already specified the case (which is often in
enterprise applications practice) where the code based on exceptions is
necessary bulkier.
// handle error?
return retVal;

Yuck! Bon appetite
Feel free to rewrite the above with error output parameters to
demonstrate how much better again the code would be and how much
easier to read and review.
I will gladly do so when I understand your desired error handling policy and see
your code that is compliant with it.

-Pavel
 
B

Balog Pal

DSF said:
It is much better to give the user feedback more detailed than
"unhandled exception." Sometimes even the most serious error can be
corrected by the user. Back in the days of much less computer memory,
I worked with two programs, let's call then "A" and "B." If memory ran
too low, "A" would display "Sorry. Not enough memory! Program ends."
Program "B" would display "System low on memory. If you are able to
free some memory, do so and select Try Again, else select End to end
the program."

Which would you rather use?

That was 25 years ago? On today systems if you're out of memory, that's
pretty much it: you will not show the dialog with question, cuz the system
is, well out of memory. Random processes will just get killed. If there's
overcommitment enabled, you won't even get to know memory allocation failed,
just die on a normal access.

And it s normal to install a new_handler that stall until memory allocation
succeeds -- and the program need not bother with that condition anymore.
If you don't care where the exception occurred in a block of code,
then how do you fix the problem?

Man, did you ever use exceptions in real life, or just read some article on
the net?
The same exception may be thrown
from more then one place in the block. The larger the block, the more
likely that may occur.

Yes, the exception can be emitted from a couple thousands of spots per
handler in practice, or more. And the emiting place is completely irrelevant
as far as the handling goes. As gettig there the cleanup already happened
via destructors. And the message to issue can be queried from the exception
object you caught.
This is a problem I have with exceptions, and why I agree with those
who believe they should be reserved for EXCEPTIONal errors.

SWhat could not be defined in this pretty fat thread this far -- or in the
mostly accepted books on the topic, so it has little content really. Beyond
saying that using wxceptions as a hack replacing longjmp is hardly good ;-)
In a nutshell:
If a call to a function returns an error condition that does not
apply to me, I am free to ignore it and continue. If same function
throws an exception, I must at minimum, do something.

That sounds like major WTF.

A function can, and in most cases *should* be exception-transparent. Meaning
it need not bother with catching any exceptions emitted from anywhere, only
manage to not break the state if it happens in any way. (please DO read the
Abrahams principles for details).

For error codes there is no such thing as transparency, if it is not
checked, the info is lost forever. Just to know you can ignore it you shall
analyze it. The case where a functions does return error code and all are
acceptable are exceptionally :) rare to the level they can be removed from
discussion.
I would prefer
said function return error codes for minor errors and reserve
exceptions for fatal and near fatal errors.

As what counts "exceptional" and what "expected" is up to the caller, a
single function can hardly serve everyone equally well. But it is easy to
cheate wrapper in either direction and use that instead of the original
instead of fussing about its design.
* But if they may be undocumented, then I may not know which ones I
can trigger.

If a library has no documentation on its behavior, it is not usable in
practice, full stop. Thowing exception is certainly part of behavior.

However a fair description may look pretty general, like 'any finction can
thorow exceptions descending from std::exception, unless explicitly marked
as nothrow'.
As I understand it, and I may be wrong, any unhandled exception
travels upward until it is handled or the program terminates. So I
have three choices:
1. Handle everything here.
2a. Handle the likely exceptions
2b. Let the rest end the program.
3. Include a catch-all (...) after 2a and try to return to the state
before the block was executed because "something went wrong, but I
don't know what."

Or just reflect the library's sepcification in your own documentation, and
be exception-transparent.
What works fine in practice, everyone makes best effort to do the request,
but if there are OS calls, each may produce hundreds of failure conditions.
Ones you don't want to analyse anyway.
Items 2 and 3 cover what I mean by "throw the baby out with the bath
water."

3 is for sure, you kill the information for no sensible reason. Preventing
the upstream to handel it in the common way.

2 is understated, if 2b means you still catch in excess and abort(). If you
just leave the exceptions alone, you can't tell at the spor what the outcome
is, and the program will be terminated. It's up to the caller.
But, as programmer, you control what the application is doing.

To some extent. To fopen you pass some string as filename. It's up to the OS
what it takes into account processing it.
You
know when making a call what are fatal and non-fatal returns (however
they are delivered) based on the context of what your code is doing at
the time.

Or not. The program got the filename string at some level. The fopen may
happen several calls below. At that place the context can be pretty well
missing.

The condition is (may be) certainly "fatal" for the particular function if
its job was to do something with the opened file. So it will abandon, and
the caller deal with the failure info. And decide how fatal it is for him.
True, that does leave a large gray area in deciding what would be
exception worthy vs. return code worthy when designing code others
will use but cannot alter. So I lean towards error returns whilst in
the gray area.

Instaed of facing that no info means no info. What justifies leaning
nowhere. Keep leaning on actual information, and just admint that the rest
was processed as an arbitrary decision.
I do think exceptions are a good idea for handling errors that are
in deeply nested code but should be handled higher up. (But not so far
up that it is out of the scope of the current code.)

Tha least part meaning?
 
B

Balog Pal

DSF said:
Too high and you won't know how to fix the problem.

Says who? "Last operation failed for reason:" + ex.what() can be completely
okay, especially if the ssytem below uses the strong exception guarantee.
No. I am saying that exceptions can go to the extreme and therefore
should only be used to handle extreme situations or those where there
is no alternative path of error return.

We're getting back to undefined buzzwords. If you want to keep that
assertion please go the mile and define them for everyones use.
I've never addressed sloppy programming. And, to use your metaphor,
I'm saying that the dining car being out of ice is no reason to hit
the emergency stop. An undocumented exception in a library is just
like a steward on the train who will stop it over no ice.

Using the undocumented library in the first place is what a serious endineer
strictly refuses. Or if it;s forced, jsut write the South Park disclaimer on
the label please.

Why do you think real practices bother with certified materials, standards
and so on? Why you think software should be allowed just do undocumented and
unexpected things?
You don't
know where he is or if he'll even stop the train...until he tries.

Think that again.
How do you take action (short of ending the program) without knowing
the cause of the problem?

Well, probably that is the thing called 'program design'. O,O

Just like a wlyweight or a safety walve works: does it know why the pressure
went up? Does a circuit breaker know if you have a short or just switched
on an extra owen? No, it just does its job. At that level it may be crude,
but whoever wanted more relevant action already had and passed the chance.
 
P

Pavel

Balog said:
That was 25 years ago? On today systems
What "systems"?
if you're out of memory, that's pretty
much it: you will not show the dialog with question, cuz the system is, well out
of memory.
Program B would pre-allocate resources to reliably display those critical
messages, be it now, 25 or 40 years ago.

-Pavel
 
G

gremnebulin

Hi.

I've heard about this, and wonder when is it right to use codes, and
when to use exceptions for reporting errors? I've heard various stuff,
such as that exceptions should only be used to indicate "exceptional"
conditions. Yet what does that mean? I've heard that, e.g. a user
inputting invalid input should not be considered "exceptional", but
something like running out of memory should be. Does this mean that if
we have a program, and we have a "parse()" function that parses a
string input by the user, that this function should return an error
code on parse failure, instead of throwing an exception? Yet we'll
probably also come across places where it's good to use an exception,
in the same program! Which means we get into _mixing error codes and
exceptions_. And what's the best way to do that?

Also, how exactly does one go about determining what is and is not
"exceptional"? Two examples were mentioned of things exceptional and
non-exceptional, but what about something else, like say in a game,
where you have a grid representing a game level, and a request for a
tile of the level is made with a coordinate that is off the map (like
a 64x64 map and something requests a tile at (100, 100).). Would it be
OK for the function working on the tile to throw? Or should it give an
"out of range" error code? And as for that mixing: consider, e.g. C++
and probably many other languages: a function has a single definite
return type. Suppose our grid had a function that extracts an
attribute from a cell. What to do when there's an out-of-bounds
request? Throw exception? See, what I've currently been doing, and
it's probably silly, is to use exceptions when our function needs to
return a value, and error codes when it could otherwise return "void".
This doesn't seem like a good idea. But what to do? Make every
function return an error code, using pointers to output variables to
store output, and only use exceptions for a rare few kinds of "system-
related" error? Yet one can hardly deny the niceness of being able to
say "x = f() + <foo>" (inside a "try" block, perhaps) instead of

if(f(&x) != SUCCESS)
 { // handle error }
x += foo;

:)

Note how we can easily get LONG methods full of repeated code with
error codes (repeated error handlers to handle similar errors at
various function calls calling error-code-emitting functions, if one
wants to be more graceful than simply aborting with an error to the
next level up (which complicates what error codes a function can
return, since it can return its own codes in addition to those
returned by the functions below it, and those may have functions below
THEM, and so on...).). And who likes duplicated code? eww. This seems
a disadvantage of error codes.

Or, and this is what I've been thinking of, use exceptions for every
error that the user does not have control over, like invalid input
strings. Would that be OK or excessive use of exceptions? And if we
are to mix error codes and exceptions, does this mean we should have
the lists of codes and exceptions correspond + a translator to
translate between the two?

The problem with both approaches is that you are specifying
implementation directly,
rather than using an interface to specify something which can be
implemented, by someone else, in
different ways. Exception handling is better that error codes because
the whatever handles the exception
can be quite decoupled from the exception raiser, but you still have
the problem that
the exception raising mechanism itself is not always desirable to
employ. A throw is a throw is a throw.

Good error handling should use a wrapper some kind of my_raise_error()
call, an interface that has
no direct meaning to the language in question , and so can be swapped
between various implementational strategies, such as halting
immediately, logging a message, jumping nonlocally, etc, according to
the requirements of the client, and even according to the runtime
environment and
other dynamic factors. It is a common and benevolent pattern for error
handling levels and styles
to be set at programme invocation. Such parameters are a kind of
global variable, but a harmless one
because they are read-only and so do no allow non-local channels of
communication.

my_raise_errors() are usually implemented in a procedural context,
where errors are detected in the first
place with the crappy error_code system. What we still don;t have is
something that gives us the best of both worlds,a system that hast
the expressiveness of try{..}catch{..} and the flexibility
of my_raise_error(), mainly because we don't have user modifiable
syntax in all but experimental languages.
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top