Error codes vs. exceptions

B

Balog Pal

DSF said:
This is the problem I have with exception handling: you CANNOT
ignore exceptions. You must in some manner handle the possibility,
else your program ENDS!

Not too realistic, as normally you have some general catch blocks at high
points.
Handling minor, recoverable, errors with
throwing exceptions is like police shooting jaywalkers.

You want to say: minor conditions that the program COULD have handled if the
programmer actually bothered with that -- but did not. So the deployed state
CAN NOT tell it from any other overlooks, and evaluate the severity.

Like in the joke: Computer, cut the patient's left leg. I said the left! I
said the LEG!

You want to cure the sloppy programs by letting them go derailed. And
believe it is for the good. Hmm.
Often, how you recover from an error depends on the specific error
information. If you "try" each individual function that might throw,
you might as well have error codes. The larger the "try" block, the
more difficult to isolate and repair/recover from the damage becomes.

Hardly. In the catch you most often just not the fact that exception
happened. And the action is taken without inspecting its spot or cause.

In C++ at least -- what you say applies to handicapped languages like java,
lacking auto-actions in destructors.
Even there ehe mandated way is to use try/finally instead of try/catch.
So if I wind up using a library written by someone who throws an
exception for every incorrect condition, large or small, I have to
handle all of them or "throw the baby out with the bath water."

The usual C++ programmer, if meat such a hostile library would probably
spend a few minutes to wrap it. And then restrict to use the wrapper that
will provide a single exception, or error codes or whatever. Hardly a big
deal. And unlikely that will be your most violent problem if the library is
really misdesigned.

But that is stuff out of your control. What worth discussion is how to shape
what you can.
I do think they are a great way to handle an error where there is no
elegant return path, such as a boolean return value. (Is it "yes,"
"no," or "out of memory"?

Error conditions more often represent "that didn't work, I'll try
this" than "it's the end of the world!"

Err, WHO'll "try this"?
 
M

mike3

It really depends on what you define as an input parser error.  Lexers are
designed to identify certain string patterns as specific tokens, while
identifying everything else as a very specific token; the "unknown token"
token.  So, stumbling on an unknown token isn't necessarily an exceptional
event.  The same applies to grammar errors.  A set of tokens is either
matched by your grammar or it isn't.  In some cases, a grammar might be
defined so that a specific sequence of tokens is recognized but interpreted
as an error.  Either way, your parser still ends up in a recognized state,
and therefore these errors aren't exceptional situations.

Does this mean that we should throw on a constructor failure? E.g.
consider
if a string is passed to the constructor and the constructor parses it
to set up
the object. The parser fails, so the constructor does not know how to
get the
object into a "recognized state". Time to throw exception? Or default
the object
to some "default" state (which may be bad, as then there could be
unexpected
behavior, esp. if the input string was supplied by the user)? Or put
it in an
"error" state (which adds extra questions: what should the class
members do
if one tries them on an "error"-state object? Should they throw? If
so, then why not
just throw out of the constructor in the first place?)?
That's up to whoever is in charge of designing the software.  More
specifically, it must be decided which situation is expected and requiredto
express the control flow of your program and which situation is exceptional,
and therefore, for reasons such as clarity, it is benefitial to handle it
separately from the main control flow.

So then if I get this right, "exceptional" would mean that it is
something that,
if it happens, _requires_ us to break the usual program flow, whereas
"non-
exceptional" is something that does not? So, a parse error on user
input
might not break the flow, since we can just ask the user again for
input, e.g.

---

do {
string = getInput();
parseErr = parse(parseOutput, string);
if(parseErr)
output("Bad input! Please try again.");
} while(parseErr);

---

vs. what you'd have to do with exceptions:

---

do {
string = getInput();
continueLoop = false;
try {
parse(parseOutput, string);
} catch(BadStringException e) {
output("Bad input! Please try again.");
continueLoop = true;
}
} while(continueLoop);

---

Notice how, immediately, a code smell appears: we have a "try" block
surrounding a single call. So in this case, the error code appears the
more elegant solution.

Or another example: consider taking keypresses for commands. It may
not be an exception that the user presses a key that's not a valid
command.
But if the keypress-getting routine _cannot successfully determine the
state of the keyboard_, that's probably exception and we should throw,
not return an error code.
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.

Rui Maciel

So would a good rule here be "use whichever is simplest and most
trouble-
free"? If error codes are creating trouble (proliferation of
duplicated handlers,
"error-code football", etc.), consider exceptions. If exceptions lead
to trouble
(proliferating try blocks, try blocks in loops, try blocks surrounding
single
calls, degraded performance, etc.), try error codes.
 
L

Luca Risolia

This is the problem I have with exception handling: you CANNOT
ignore exceptions. You must in some manner handle the possibility,
else your program ENDS! Handling minor, recoverable, errors with
throwing exceptions is like police shooting jaywalkers.

In fact, you should *not* use exceptions for error *recovering*. If you
want to to recover from an error, the best is to install an handler for
that purpose. If the handler fails, then throw an exception.
The couple std::set_new_handler / std::bad_alloc() is an example.
 
M

mike3

Yes. As I mentioned for some operations there's a need for both forms.
Fortuneately it's not hard to write converters in either direction, and then
clients can use the better-suited variant.

So then you _do_ need a translation layer than translates error codes
to exceptions and exceptions to error codes, and thus need a 1-to-1
correspondence between the two, with exception classes for every error
code in your error code enum (it seems that in the examples in the
C++ "bible" (Stroustrup), they have exception classes for every type
of
exception that can occur. Also, I've heard stuff about not including
type
fields in exceptions, and type fields being a bad idea in general, and
it's
better to use different classes + inheritance instead of type
fields.)?
And thus the need to update several statements in the program when
adding new errors: update the error code list and add a new exception
class and also update the translator.

So perhaps maybe I should call an exception :) to the no-type-field
rule
because it's causing gains in complexity and a reduction in
maintainability?
Then we only need to add codes to the error code list and don't need
to
mess with anything else. After all, the rule is supposed to help
maintainability,
so if you're running into a situation where it's hurting it, better to
call an
exception to the rule. Remember that programming is not based on hard
rules. Or don't bother with two-way translation, and allow for
separate and
distinct sets of error codes and exceptions.

(Why is a 1-1 correspondence needed? Because consider if the sets
are different: How do we determine what to translate to? Suppose we
have
error codes for "XPARAMOUTOFRANGE" and "YPARAMOUTOFRANGE"
but only a single "OutOfRange" exception. We can translate the codes
to exception, but how can we do the reverse translation? There's an
ambiguity.)
Don't confuse the situations and it's not that messy really.

What's messy is this:

---

retVal = doSomeErrorCodeReturningFunc1();
if(retVal != SUCCESS)
{
Handle it. May include a detailed response, may just "log" or
report message.
}

retVal = doSomeErrorCodeReturningFunc2();
if(retVal != SUCCESS)
{
Might do the same thing as before! Oops -- code smell: duplicated
code.
}

retVal = doSomeErrorCodeReturningFunc3();
if(retVal != SUCCESS)
{
Ditto.
}

---

And even if we have no code duplication, we may nevertheless turn
a "short method" into a "long method" -- code smell.

And this is "error-reaction logic tangled up with action logic" -- see
below.

I've got a thread going on comp.lang.c about this same thing -- where
in C, exceptions do not exist. The above seems very "Cish".
In that layer you will have functions that return status. IsEmailValid()
shall not throw, but return bool, or some more detailed info on the
diagnosed problem. When you check the input you will call a ton of such
functions, and the caller will know how to act on discrepancy.

No, and I would not expect such a function to throw, either. It's
after
all, a validity _checker_.
While those functions return "error code", it is not really that from the
function's perspective: it does all its work properly and that is the
result.  It only becomes error code if the caller decides to not act, but
pass it upwards.


Yes, that is the main motivation. To separate the action logic and the
error-reaction logic. Not mix and tangle them together.

So does this mean my original idea:

1. throw exceptions from functions whose return value would not be an
error
code but a useful result (or throw from constructors, since they
_can't
return anything_, and to throw from overloaded operators)

2. throw error codes from functions that would otherwise return "void"

was right?

This idea seems to suggest prefer exceptions, and not limit them to a
minority
of errors, otherwise in the majority of errors we'll be mixing
reaction logic with
action logic (see above).
By using the previously mentioned form you already admitted that the error
condition *is* exceptional.

If that invocation form is correct, it implies that
 - involved operations are expected to succeed
 - this spot has no interest in looking at error conditions

If the code expected some problem and had alternative actions in mind for
that condition, it would be there, wouldn't it?

Ah.


Yes. Certainly don't forget to provide at least the basic exception
guarantee, and learn about tha Abrahams principles if you didn't yet.

Hmm.


Those facilities serve some purpose. If you meet that purpose you're okay..
Does not matter how you do that.

In usual good code 95% of catch() blocks just do a conversion to some
different exception or a return code.

The rest minority that actually does something -- and it will just catch
*any* exception (with luck the common base class for all), and just call
.what() for a single string.

And that is more than enough. Top levels only care if stuff worked or not..
Rollback actions are issued via destructors (or simply lack of commit). The
inforlation on the problem trigger is carried in the exception object and
can present itself to a human-readable form so some actual intelligence,
outside the program can figure out what next.

So then a translation layer (error codes <-> exceptions) is vital. So
what
about the problems mentioned at the beginning of this post?
 
P

Pavel

Adam said:
You're going to have to clarify what you mean by WFC. I don't think
it means what you think it means. If you mean the .NET runtime, then
you can't use that in C++, so it's somewhat irrelevant. C++/CLI is
not C++.
Interesting statement. To be considered seriously, however, it requires much
better support than a simple assertion.
With an exception, inheriting from an STL one if you must inherit from
one (since it is 'always' used), though your problem honestly makes
little sense.
I do not think this statement is relevant to my point. The facts of life are:

a. C++ allows using exceptions not inherited from std::exception.
b. Many significant libraries use such exceptions.
c. Not using such libraries while writing your library or app is often not an
option.
d. Even when not using such libraries is an option in a particular case, it is
almost never wise to assume that this library or app *will never be used* with
such a library.
When you write your own library that uses WFC -- and of course STL how shall you
expose your library's errors and where should you catch std::exception (which
are fortunately rare) and guys from System::Exception hierarchy (which are much
more often)?

In general, if you have to catch std::exception or System::Exception
outside of the entry point of your program[1][2],
First of all, this does not make sense: the whole point of exceptions is that
you can catch them in more than one place.

Regardless, it is irrelevant: even if the above were true, it would still result
in duplicating code to implement app- or library- specific error handling policy
for STL's std::exception and other library's exceptions not derived from
std::exception.

you've done
something pretty wrong. I think you're trying to create artificial
problems.
Ad hominem? So far I have refrained from conveying my impression from this
discussion: that you are either in denial or have had little experience with
maintaining a useful library or application.
Surely, write a catch block?
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()).

You seem to be confusing the response to
a condition with the condition itself,
As you are not providing any support for your statement, we should just believe
it, correct?
but I don't see anything here
that requires "special" handling or creates a problem.
It seems to me you cannot "see anything here" because you are carefully avoiding
actually reading my posts.
Nevermind that
I'm not quite sure how you'd mix the 3 libraries you mentioned in the
first place.
Any two of them would be enough, as my post clearly said -- but you chose not to
read it before answering, correct?
Huh? The only way I can observe an exception from a given library is
if I'm actually calling functions within the library. As such,
needing to handle the exception can't possibly make me more dependent
on the library!
Yes, it can. Do you really believe that dependency on a library is a boolean
yes/no variable?

Then just wait till your business manager asks you how much time it will take to
migrate an app from library XYZ to library ABC and answer him "No" (or "Yes" if
you are a yes-man; the outcome will probably be same if he or she is an
effective manager).

You're going to need to provide an actual example of
what you're talking about.
I already all but provided you the example of the code you will have to write to
encapsulate the dependency on exceptions and on error codes. As you chose not to
understand that, I do not have any reason to believe that providing even more
details will serve to any purpose but your attempts on nit-picking.
If you're writing exception handlers to catch exceptions from
libraries you do not call (directly or indirectly),
You really did not read my post before answering it, did you? Otherwise, how
could you possibly mistake "the library calls your application uses" for
"libraries that you do not call"?

then you're doing
something completely and totally wrong.
then.. of course. It is so convenient to blame others for things they did not
do, isn't it?
No, the code that responds to an error condition really shouldn't care
how you received the condition initially:
Sure it should even if because it syntactically has to. If you received the
condition via std::exception, you have to write "catch(...[std::]exception..)"
and if it was via AbcException that is not derived from std::exception, you have
to write catch(...AbcException..).
if you're going to log a
message in response to an error, the logging code doesn't care if you
got an exception, bungled about with return codes, or just made things
up. The same goes for displaying dialogs and just about every other
conceivable response.
Vague "logging code" and "any other conceivable response" may not care (you like
vagueness, don't you?), but the more specific code "calling logging function" or
"filling up dialogue fields" has to care, because otherwise:

a. It won't be called at the first place, because a specific exception type
won't be caught.
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.
The entire point of exception handling is that if you cannot (or need
not) respond to the condition, /you pretend that it does not exist/
[3].
I would not put it this way but, yes, the whole point of exception handling is
to facilitate handling error conditions at desired points in code. I am glad we
agree on this. I am sure you remember that I specifically stated that the
exceptions should not be thrown across library boundaries, not in general.

"Kicking can down the road" to your own code is ok. Kicking it across library
boundary is IMHO as irresponsible and morally objectionable as the
loved-by-USA-politicians kicking national debt to the next generation. Why are
you sure your library user will enjoy letting your stray std::exception to
terminate his or her program or catching it somewhere at his main() where he
only expects exceptions from his own functions or none at all?
If you're doing something else, then you're designing your code
entirely incorrectly. Such behavior is the height of
irresponsibility, because it is a fool's errand.
You love denouncing actions you just made up, don't you? In conjunction with
your aptitude for kicking the can down the road, this makes me wonder: are you a
politician by any chance?

C++ libraries need
not declare every exception they throw, so without walking every line
of code of every library you use (directly or indirectly), one cannot
even accomplish what you say should be done.
Exactly. Now you are at last talking about the subject of my post. If one of
your requirements is to log every error in a particular manner, it is
significantly more labor-consuming to do that "walking ever line of code" (which
you will have to do, no matter how much you hate the idea) with the
exception-based API than with error code-based API. First, the error code
dispatching is easier to formalize and then drive from configuration. Second,
error code-based error processing methods usually include some error types in
API function prototypes (as opposed to exceptions) so you will always have a hint.

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()).

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:

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.

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.

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:

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

or

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

to

try {
apiCall(args..);
} catch(XxxException &e) {
if (!handleError(e))
throw;
}
/* 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:

".. This allows implementations to make performance optimizations based on the
absence of exceptions at runtime."
At best, it can be equal, and in practice, it must necessarily be more
because you're explicitly doing things that are done implicitly with
exception handling.
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.
Hardly. Just returning error codes doesn't suddenly let you mark
everything nothrow,
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.

due to the problem I mentioned above.
I fail to see how calling a method marked nothrow increases my
productivity at all.
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.
It doesn't reduce what I need to be worried or
concerned about unless the code actually provides the nothrow safety
guarantee, which is considerably difficult.
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.
Merely not throwing exceptions is of very little benefit to the
programmer.
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.
Funny, I spend more time trying to figure out what was written in all
these technical papers I'm supposed to implement than looking at
anyone's source code. YMMV I guess.


Unfortunately, the equation is changed, but based on what you've said
I think you did the calculation completely incorrectly in the first
place. Actually supporting your position with real code,which I
strongly encourage you to do, is going to be rather difficult.
I provided more than enough use cases (and a little code snippet above, in this
post) to support my position whereas you chose to provide no use cases, let
alone the code, to support yours.
Adam

[1] Or the entry point to a thread or similiar concurrency construct
[2] Though one should almost certainly 'catch(...)' in these contexts.
[3] Handling it here includes cleaning up resources and then kicking
the can down the road.
[4] The occasional syntax foible aside.

-Pavel
 
P

Pavel

Adam said:
Qt clearly says on its wrapper that it is not exception safe and has
said so since its inception AFAIK. Broken software doesn't really
impact my position
Reality check #1:
Good luck saying it to your manager who is asking your to fix the "broken"
software.

Reality check #2:
According to your willingness to "kick the can down the road" (that is, to
define top-level library APIs throwing exceptions), you could be an author of
that Eigen package (imagine that Eigen is perfect in all other regards so you
don't have a reason to disown it). Then, good luck attempting to sell a new
version to the Rui's company after they ask you for support on the above case
and *you tell your users that they are at fault for writing broken software and
not RTFMing on Qt*.

Sarcasm aside, products should be designed for real rather than ideal world, and
you imperfect user (who incidentally pays your bills) tends to live in the former.
any, though I suppose I should have mentioned
upfront that there's a depressing amount of software out there that
lacks exception safety.

Adam

-Pavel
 
A

Adam Skutt

In fact, you should *not* use exceptions for error *recovering*.

Then what good are they?
If you
want to to recover from an error, the best is to install an handler for
that purpose. If the handler fails, then throw an exception.
The couple std::set_new_handler / std::bad_alloc() is an example.

I really don't think so. Can you imagine what std::fopen() would look
like? Before C++11, such an idea would be a nightmare. It also has
the whole "many errors are only recoverable sometimes" problem.

Regardless, I'm not sure std::set_new_handler is a good exception,
since it requires lots of handwaving and assumptions to be
effective.

Adam
 
R

Rui Maciel

Adam said:
Qt clearly says on its wrapper that it is not exception safe and has
said so since its inception AFAIK.

Exception safety has nothing to do with this. Quite the opposite. This was
a case where an innocuous run-time failure, which did not caused any
relevant ill effect (IIRC it was a divide by zero problem), was made to have
consequences which greatly outweighed the nature of the problem. So, in
short, a small annoyance was needlessly turned into a serious issue just
because an exception was mishandled.

Broken software doesn't really
impact my position any, though I suppose I should have mentioned
upfront that there's a depressing amount of software out there that
lacks exception safety.

All non-trivial software is, to some extent, broken, and is bound to fail
once the right (or, better yet, wrong) conditions are met. This is the main
reason behind the need for exception handling, particularly how it provides
a way to handle any type of exception, including unpredictable and unknown
ones, and grant the programmer a simple way to either recover nicely from
them or be able to fail gracefully.

Knowing this, if you base your assertions on the idea that the code you
write isn't broken, or even that you don't use routines which are broken,
then not only are you basing your decisions on the expectation of meeting an
impossible scenario but you are also setting yourself to eventually get some
bumps and bruises somewhere in the future.


Rui Maciel
 
A

Adam Skutt

Reality check #1:
Good luck saying it to your manager who is asking your to fix the "broken"
software.

Yes, I agree. If you know you're using Qt, then your design is broken
if it doesn't consider its lack of exception safety. People who
blindly mix exception-safe and unsafe code deserve what they get.

However, the mere fact that exception unsafe code exists in the world
isn't justification for avoiding exceptions, on the off possibility
someone might use your code with such broken software.
Reality check #2:
According to your willingness to "kick the can down the road" (that is, to
define top-level library APIs throwing exceptions), you could be an author of
that Eigen package (imagine that Eigen is perfect in all other regards soyou
don't have a reason to disown it). Then, good luck attempting to sell a new
version to the Rui's company after they ask you for support on the above case
and *you tell your users that they are at fault for writing broken software and
not RTFMing on Qt*.

Except I wouldn't do that, if I was going to get paid to write
wrappers to convert the exceptions to error codes I would happily do
so. Absent pay though, it's his problem and he can keep the broken
pieces. ;)
Sarcasm aside, products should be designed for real rather than ideal world, and
you imperfect user (who incidentally pays your bills) tends to live in the former.

And if they want Qt support upfront then they should specify it as a
requirement. Designing for the real world does not involve
considering every last possible and framework that could interact with
my software, as such a thing is simply not possible.
 
A

Adam Skutt

Interesting statement. To be considered seriously, however, it requires much
better support than a simple assertion.

Huh? I'm the one asking for clarification, so asking me to provide
clarification to your statement is utter nonsense. The fact I took an
educated guess doesn't relieve you of the need to clarify.
Any two of them would be enough, as my post clearly said -- but you chosenot to
read it before answering, correct?

You mentioned it all three again, you said, "When you write an
application that uses all 3 or even only two libraries -- what errors
shall you use inside your application?"
I already all but provided you the example of the code you will have to write to
encapsulate the dependency on exceptions and on error codes. As you chosenot to
understand that, I do not have any reason to believe that providing even more
details will serve to any purpose but your attempts on nit-picking.

You have provided no source code, whole or snippet, at any point.

Since you clearly do not recall and don't understanding the meaning of
what you
wrote, there's zero hope for anyone else to understand it. So I see
no point to discuss this with you further.

Adam
Huh?  The only way I can observe an exception from a given library is
if I'm actually calling functions within the library.  As such,
needing to handle the exception can't possibly make me more dependent
on the library!

Yes, it can. Do you really believe that dependency on a library is a boolean
yes/no variable?

Then just wait till your business manager asks you how much time it will take to
migrate an app from library XYZ to library ABC and answer him "No" (or "Yes" if
you are a yes-man; the outcome will probably be same if he or she is an
effective manager).

You're going to need to provide an actual example of> what you're talkingabout.

I already all but provided you the example of the code you will have to write to
encapsulate the dependency on exceptions and on error codes. As you chosenot to
understand that, I do not have any reason to believe that providing even more
details will serve to any purpose but your attempts on nit-picking.


If you're writing exception handlers to catch exceptions from
libraries you do not call (directly or indirectly),

You really did not read my post before answering it, did you? Otherwise, how
could you possibly mistake "the library calls your application uses" for
"libraries that you do not call"?

then you're doing> something completely and totally wrong.

then.. of course. It is so convenient to blame others for things they didnot
do, isn't it?


No, the code that responds to an error condition really shouldn't care
how you received the condition initially:

Sure it should even if because it syntactically has to. If you received the
condition via std::exception, you have to write "catch(...[std::]exception..)"
and if it was via AbcException that is not derived from std::exception, you have
to write catch(...AbcException..).
if you're going to log a
message in response to an error, the logging code doesn't care if you
got an exception, bungled about with return codes, or just made things
up.  The same goes for displaying dialogs and just about every other
conceivable response.

Vague "logging code" and "any other conceivable response" may not care (you like
vagueness, don't you?), but the more specific code "calling logging function" or
"filling up dialogue fields" has to care, because otherwise:

a. It won't be called at the first place, because a specific exception type
won't be caught.
b. Even if it were somehow auto-magically called, it often would not haveenough
data to fill up the logging function's arguments or the dialogue fields without
cracking the exception or error code.
The entire point of exception handling is that if you cannot (or need
not) respond to the condition, /you pretend that it does not exist/
[3].

I would not put it this way but, yes, the whole point of exception handling is
to facilitate handling error conditions at desired points in code. I am glad we
agree on this. I am sure you remember that I specifically stated that the
exceptions should not be thrown across library boundaries, not in general..

"Kicking can down the road" to your own code is ok. Kicking it across library
boundary is IMHO as irresponsible and morally objectionable as the
loved-by-USA-politicians kicking national debt to the next generation. Why are
you sure your library user will enjoy letting your stray std::exception to
terminate his or her program or catching it somewhere at his main() wherehe
only expects exceptions from his own functions or none at all?

 > If you're doing something else, then you're designing your code> entirely incorrectly.  Such behavior is the height of
irresponsibility, because it is a fool's errand.

You love denouncing actions you just made up, don't you? In conjunction with
your aptitude for kicking the can down the road, this makes me wonder: are you a
politician by any chance?

   C++ libraries need> not declare every exception they throw, so without walking every line
of code of every library you use (directly or indirectly), one cannot
even accomplish what you say should be done.

Exactly. Now you are at last talking about the subject of my post. If oneof
your requirements is to log every error in a particular manner, it is
significantly more labor-consuming to do that "walking ever line of code"(which
you will have to do, no matter how much you hate the idea) with the
exception-based API than with error code-based API. First, the error code....

read more »
 
B

Balog Pal

Pavel said:
Exactly. Now you are at last talking about the subject of my post. If one
of your requirements is to log every error in a particular manner, it is
significantly more labor-consuming to do that "walking ever line of code"
(which you will have to do, no matter how much you hate the idea) with the
exception-based API than with error code-based API.

IMO&IME obsession with logging normally lurks in rotten projects, and is a
big player in the rot. Efforts could be put to write correct code instead,
and the kind that is light, focused, readable. Then it will just work. (You
can still log the few enviromental events causing exit at a central
location, and without care which operation was involved...)

But if you need something like that I still don't see why is it that hard.
I'd create a simple RAII class that you put as local at every function
entry, call its OK() at the exit. In case of unexpected exit it emits a log
line from dtor. You can simply add checkpointing or insert info on actions
you will do. I guess i could write a simple macro to automate most of the
magic. Leaving way less clutter than the retcodes version.
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.

That sounds weird. Can you explain why is that good? With the usual way I
can make the functions CHECKRETURN and use the compiler to flag places where
return was ignored, without extra effort. I'm not aware of similar support
on arbtrary arguments used for output.
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.

With a C API? how hard it is to pass NULL for the output param? And even if
you force to pass a variable what makes the code look at it more than the
return code?
 
A

Adam Skutt

Exception safety has nothing to do with this.  Quite the opposite.  This was
a case where an innocuous run-time failure, which did not caused any
relevant ill effect (IIRC it was a divide by zero problem), was made to have
consequences which greatly outweighed the nature of the problem.  So, in
short, a small annoyance was needlessly turned into a serious issue just
because an exception was mishandled.

Exception safety has everything to do with it. If Qt were exception
safe, the exception would have been propagated upward, to where you
can handle it, and life would have continued onward. Or you would
have written your code to make sure the exception was handled before
it unwound/entered Qt.
All non-trivial software is, to some extent, broken, and is bound to fail
once the right (or, better yet, wrong) conditions are met. This is the main
reason behind the need for exception handling, particularly how it provides
a way to handle any type of exception, including unpredictable and unknown
ones, and grant the programmer a simple way to either recover nicely from
them or be able to fail gracefully.

Knowing this, if you base your assertions on the idea that the code you
write isn't broken, or even that you don't use routines which are broken,
then not only are you basing your decisions on the expectation of meetingan
impossible scenario but you are also setting yourself to eventually get some
bumps and bruises somewhere in the future.

Ok, fine. What exactly would you propose as an alternative here? I
don't see how the consequences of your particular example are
avoidable nor any sensible alternatives. At best, you seem to be
saying, 'This won't free you from seeing bugs' but I never claimed it
would.

Broken code might do anything, including shoot daemons out your nose.
As such, it's probably not worth worrying about too heavily (unless
you know it exists) until you encounter it. I certainly don't see how
it should impact a general design principal here.

Adam
 
B

Balog Pal

Pavel said:
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.

The what? Did you read what those do in C++? Or typical rationalt to (not)
use them, ie. at Boost with cool explanation? Or at MS that just refused to
implement this braindead feature? Looking ahead in time, as in C++11 this
feature became deprecated.
 
R

Rui Maciel

mike3 said:
Does this mean that we should throw on a constructor failure? E.g.
consider
if a string is passed to the constructor and the constructor parses it
to set up
the object. The parser fails, so the constructor does not know how to
get the
object into a "recognized state". Time to throw exception?
<snip/>

If you've adopted exceptions to handle a specific type of exceptional
circumstance then an exception should be thrown when you stumble on that
exceptional circumstance. This includes constructors which, incidentally,
is one of the examples covered in "The C++ Programming language" by
Stroustrup.

Nevertheless, from your description I suspect that your design is a bit
iffy. Parser errors aren't exceptional occurrences; they are quite
expectable. In addition, it appears your algorithm explicitly depends on
the outcome of this operation. This leads me to believe that you are trying
to employ exceptions on a problem where they are a less than adequate
solution.

So then if I get this right, "exceptional" would mean that it is
something that,
if it happens, _requires_ us to break the usual program flow, whereas
"non-
exceptional" is something that does not? So, a parse error on user
input
might not break the flow, since we can just ask the user again for
input, e.g.

---

do {
string = getInput();
parseErr = parse(parseOutput, string);
if(parseErr)
output("Bad input! Please try again.");
} while(parseErr);

---

vs. what you'd have to do with exceptions:

---

do {
string = getInput();
continueLoop = false;
try {
parse(parseOutput, string);
} catch(BadStringException e) {
output("Bad input! Please try again.");
continueLoop = true;
}
} while(continueLoop);

---

Notice how, immediately, a code smell appears: we have a "try" block
surrounding a single call. So in this case, the error code appears the
more elegant solution.

Right, this is a poor use of exceptions. The main problems I see with this
example, other than handling a parser error as an exception, is that you do
care where the flow of execution ends up after throwing the exception, and
the control flow of your algorithm depends on catching an exception. Any of
these indicators are a good sign that you shouldn't be using exceptions for
this.

Or another example: consider taking keypresses for commands. It may
not be an exception that the user presses a key that's not a valid
command.
But if the keypress-getting routine _cannot successfully determine the
state of the keyboard_, that's probably exception and we should throw,
not return an error code.

In principle, this would be a good place to throw exceptions. We would be
dealing with exceptional scenarios, there isn't an algorithm explicitly
relying on the outcome of this test and, once an exception is thrown, we
don't care or need to know where the flow of execution ends up.

So would a good rule here be "use whichever is simplest and most
trouble-
free"? If error codes are creating trouble (proliferation of
duplicated handlers,
"error-code football", etc.), consider exceptions. If exceptions lead
to trouble
(proliferating try blocks, try blocks in loops, try blocks surrounding
single
calls, degraded performance, etc.), try error codes.


Well, simplicity is something which everyone should aim for, and it is
always a good idea to avoid problems. Yet, I believe your conclusions are a
bit off. Let me explain.

Exceptions shouldn't lead to the proliferation of try blocks, and you
typically don't need to wrap single statements with a try block. As you
don't care or need to know where the flow of execution ends up then you just
wrap a big bundle of statements in a try block and handle the exceptions
somewhere else.

And exceptions should not degrade performance. They are designed to handle
exceptional circumstances, and by definition exceptional circumstances are
rare.

If you have some trouble deciding if or when to use exceptions, think of the
classical case where exceptions do make sense: handling memory allocation
errors. If your exceptional circumstances are as frequent as having to deal
with memory allocation failures, if your exceptional circumstances influence
the way you express your algorithms as a memory allocation test does, and if
you don't really care where the flow of execution ends up after detecting
these exceptional circumstances then it is a good idea to handle these
exceptional circumstances by throwing exceptions.


Rui Maciel
 
M

mike3

"Adam Skutt" <[email protected]>

That opens slippery slope for mixing program logic errors with environmental
factors outside our control.

So does this mean that we should prefer _error codes_, not exceptions,
and
reserve exceptions only for the _rare_ "environmental" error? Now I'm
even more
confused. How do you handle a failing constructor?

<snip>
 
A

Adam Skutt

That sounds weird. Can you explain why is that good?  With the usual way I
can make the functions CHECKRETURN and use the compiler to flag places where
return was ignored, without extra effort. I'm not aware of similar support
on arbtrary arguments used for output.

I suspect it's actually due to the fact that Websphere MQ was
originally written for IBM mainframes and has always supported COBOL
and PL/I as programming languages. I don't know, but it may have been
originally written in one or the other (maybe still?).

In the FORTRAN<->C bridging I've done, this is exactly how OUT and
INOUT Fortran parameters were handled. I wouldn't be surprised to
find out it's the same here, though I don't know with any certainty.
With a C API? how hard it is to pass NULL for the output param? And even if
you force to pass a variable what makes the code look at it more than the
return code?

Even if you make them define a variable, they don't have to ever
actually read from it. Which is why you can almost always pass NULL
for these sorts of output parameters in POSIX.

Adam
 
L

Luca Risolia

Then what good are they?

They are good at abandoning the current computation, but in many cases
you don't want to loose what it has already done, first you want to
recover by asking the caller for some *optional* help. This is best done
with a call to an helper function. Since thrown exceptions always return
to the caller they are not good candidates at helping to fix the problem
and carrying the computation on from the point the problem has occurred.
 
P

Pavel

Alf said:
Hi.

I've heard about this [Error codes vs. exceptions], and wonder when is it
right to use codes, and when to use exceptions for reporting errors?

Use whatever's more clear and practical in any given case.

And note that "codes" include the case of zero information about what failed,
where there's only one success indicating code (like boolean "true") and one
failure indicating code (like boolean "false")...

Also note that there are many other techniques for handling failures that occur
in a function f:

* let f return a possibly empty result
e.g. check out Barton/Nackman "Fallible" and Boost "optional"


* let f call an error handling routine, that's possibly configurable
a great many libraries do this

* let f terminate the program
a bit harsh, but e.g. a JPEG library used by ImageMagick did this

* let f involve the user and try to fix the problem
this was used in Windows for missing removable media, missing
DLLs and so on, with a box that like DOS did earlier said "abort,
ignore, retry" (or the like)

* let f simply have undefined behavior
may sound pretty stupid but is used by e.g. the C++ standard library
for functions where failures only are caused by failed preconditions

Regardless, you have the option of letting f log the incident, or not.

But generally, e.g. when I'm going to call a C library function or Windows API
function that (as they typically do) has some ad hoc fault indication + some
error code scheme, then I usually wrap it in a function or expression that
throws a C++ exception. That's generally more convenient even for local handling
of the failure. And one main reason is that it encapsulates and gets rid of all
the myriad ad hoc schemes for checking whether the function failed and for
responding to it -- it's a standardization and simplification.

As an example,

<code>
// `errno` may be an expression macro, so it cannot (portably) be qualified with
"::".
int stdErrno() { return errno; }
void clearStdErrno() { errno = 0; }

bool hopefully( bool const condition ) { return condition; }
bool throwX( string const& s ) { throw runtime_error( s ); }


long longFrom(
char const spec[],
int const radix = 0,
bool const acceptTrailingChars = false
)
{
char* pEndOfScan = 0; // The unsafe type is required by `strtol`.

clearStdErrno();
long const result = strtol( spec, &pEndOfScan, radix );
string const failureDescription = 0?string()
: pEndOfScan == spec?
"longFrom failed: the number spec was not accepted by strtol()."
: stdErrno() == ERANGE?
"longFrom failed: the specified number value is too large for strtol()."
: stdErrno() != 0?
S() << "longFrom failed: strtol() failed, errno = " << stdErrno() << "."
: *pEndOfScan != '\0' && !acceptTrailingChars?
"longFrom failed: extraneous character(s) after valid number spec."
:
"";
hopefully( failureDescription.length() == 0 )
|| throwX( failureDescription );
return result;
}
</code>
Thanks for the great real-world example, Alf!

I am largely agree to your post but, as I often use strto.. family of functions,
I have few comments:
As shown by the various exception messages there are (at least) 4 ways that
"strtol" can fail.
Please note that one of them requires your application-specific flag,
`acceptTrailingChars' to be detected. It is an often case that an application
requires this flag (sometimes, the result of strtol shall be in a range (even if
as broad as range of int because there is no equivalent strtoi standard
function). This supports my earlier point that often some application-level
tweaking has to be done directly after calling a 3rd-party library function. In
your case if strtol were a C++ function and threw exception where it now sets
errno, you would still need to write some error-processing code right after
calling the function for this condition.
I would speculate that in many programs using the "strtol" function directly,
not all failure paths are checked -- but the wrapper replaces the sequence of
four ad hoc checks with simple standard C++ exception handling.Of course, with
a C++11 conforming compiler there's little need to call "strtol" because in
C++11 iostreams do that for you
As a frequent user of strtoXxx and streams I tend to disagree. To even
approximately match the performance of my current code, using streams would make
me implement my own streambuf over given fixed buffer (which I really do not
need as I am fine with the more mundane approach of parsing from specific
position in my directly-manipulated read buffer (which is essentially a RAII
holder over character buffer, that is a much simpler class than streambuf)).

I thought of using strings as buffers and parsing istringstream but rejected the
idea. The direct manipulations with the contents of std::string-underlying
stringbuffer are UB and using string modifiers would trigger contents copy on
COW strings (for non-COW strings I would pay for the contents copy even earlier,
when I construct stringbuffer instance).

(C++03 instead used the "scanf" family, with
possible Undefined Behavior). But I think it illustrates the case for wrapping
in C++.

So, C = preference for codes, error handlers functions etc., and C++ =
preference for exceptions. But in some cases, as with e.g. calls across binary
module boundaries, you can't assume C++ exception support. Then you have to
design for the code to be callable as C, i.e. no exceptions.

All that said, I'm primarily responding because I'm really curious about where
this great need for MECHANICAL RULES comes from?

If programming could be reduced to mechanical rules, if that was a good idea,
then, u know, it could be automated, and then you would not have this problem of
finding the rules because you wouldn't be programming: it would be done by
machine... So, instead of seeking mechanical rules to be applied mindlessly, I
suggest seeking up concrete EXAMPLES of failure handling.
Then apply
INTELLIGENCE and understanding of concrete situations that you face, and just
strive to Keep Things Simple. ;-)
Fully agree with all of the above.
Cheers & hth.,

- Alf

-Pavel
 
A

Adam Skutt

They are good at abandoning the current computation, but in many cases
you don't want to loose what it has already done, first you want to
recover by asking the caller for some *optional* help. This is best done
with a call to an helper function. Since thrown exceptions always return
to the caller they are not good candidates at helping to fix the problem
and carrying the computation on from the point the problem has occurred.

That entirely depends on context. Sometimes you can provide an
exception guarantee that makes this possible, though normally you
cannot. Frequently, you can structure the code such that these
natural savepoints are encapsulated in different classes, so that the
state is preserved regardless. In other cases, you want to abandon the
current computation and then proceed on with a different one, for
which exceptions are fine.

But yes, there are cases where an actual conditional handler mechanism
might be more appropriate.

Adam
 
P

Pavel

Adam said:
Yes, but that's hardly justification for the additional complications,
especially since there is no guarantee that "missing optional file"
will be handled in the caller, or that asking the user can be
performed in the caller. It's a dubious argument at best.


That slippery slope was opened before the application was even ever
started. How does a file open call distinguish between a string
supplied by the user and a constant within the program? It can't, so
it's a waste of time to try. The same applies to database connection
strings, usernames and passwords, and a whole host of other inputs
that are sometimes hardcoded into applications.


Sure, but the sort of things that were being discussed aren't ipso
facto preconditions of that sort. Asking for a non-existent tile on
the map might be a programming bug (e.g., an invalid position
calculation) or it might be invalid input (e.g., invalid input from a
client in a multiplayer game).

Again, even when you do know it must be a programming error, abrupt
termination is hardly a good policy. It's possible the bug can be
worked around by the user,
It is not smart to spend coding and run time on any logic for recovery after
unknown-in-advance programming errors because there is an infinite number of
these and the next one found will not be one anyone have expected -- because if
it were expected it would have been already fixed. assertions (which usually do
not generate code in release versions) are good enough. To dump core as soon as
possible after the error could be detected (which is what assert does, if
compiled in) is usually the best most useful information for a programmer to
find and fix the bug as soon as possible.
even to simply save their work and quit.
Why are you so sure they will be able to save work or will be lucky enough to
save *their* work rather than that of another unsuspecting guy or will not blow
up the city while trying to save their work after the program went off the
expected path?
Merely raising the exception achieves the desired response and leaves
space for better handling of programming bugs, compared to immediate
termination.
What it achieves is destroying the information needed to find and fix the bug.
Not all bugs yield undefined behavior, so this statement is just
false. If your code is exception transparent, then it doesn't matter
whether a function threw due to a programming error or invalid input
from the user or because the operation actually failed.

And if you can't write exception transparent code, then this whole
discussion is moot.


So?
So you just have destroyed the information helpful for finding and fixing the bug.
I'm not sure I believe that would be the most likely cause. But even
it if were, trying to do something sensible and failing has the same
result as terminating on the spot. If you make things worse trying to
cleanup, then your cleanup code was broken to begin with. It was
going to make things worse even under defined failure conditions.

Adam

-Pavel
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top