Necessity of multi-level error propogation

J

James Kanze

* Jeff Schwab:

[...]
errno is a global variable.

It certainly behaves like one.
And yes, errno is required to be a macro.

In C++. Curiously, both C and Posix also allow it to be an
indentifier with external linkage; until recently, Posix
actually required it to be declared:
extern int errno ;
In the first case 'errno' refers to a concept and/or bottom
level implementation of that concept.
In the second case 'errno' refers to a very limited tech
detail view that few if any find useful to know about, but the
purpose of which is to provide the first case's concept in a
way such that people don't have to worry about the details.

The name 'errno' has to behave as a direct reference to a
variable, so in C it can at most map to an expression that
dereferences the pointer result of a function call (but it can
there not map to a direct function call).
In C++ it can conceivably map directly to a function call, but
it would be rather silly to implement it differently in C++
and C.

Under Solaris, its definition depends on whether the -mt option
was given; in single threaded code, it's a global variable, in
multithreaded, it expands to (*(__errno())).

Curiously enough, I've been unable to find anything in Posix
concerning its semantics in a multithreaded environment;
typically, it resolves to a thread local variable (behind the
function call), and of course, anything else would make it
totally unusable. But I'm curious about things like &errno and
errno = 0. I can't find anything in any of the standards to the
effect that in multiple uses of errno (in the same thread), the
lvalue actually refers to the same int, although of course, if
this were not the case, the common sequence of setting errno to
zero, then calling a function and examining it wouldn't work.
(I suppose that there are some implicit requirements along these
lines, based on the fact that some standard functions set errno,
and expect you to be able to read the value they've set.)
 
J

Jean-Marc Bourguet

Jeff Schwab said:
I recently was critiqued for doing that, and actually, I'm glad I
was. I'm convinced now that the right way to use errno is never to set it
manually; it is purely a conduit for the standard library to give further
details, once an error has already been reported. The right way to check
whether a POSIX or C function has failed is via its return code.

There are functions (in <math.h>) which set errno in case of problems but
don't have another way to report that they failed. Setting errno to 0
before calling them and checking after is the only way I know to make use
of that. Obviously you can prefer to validate the arguments before.

Yours,
 
A

Alf P. Steinbach

* Jeff Schwab:
Not really. For example, variable names respect scopes, but errno does
not.

You are again choosing a contextually non-sensical meaning of a term.

It's just silly.

Stop.

Someone who really thinks errno is a global variable will have a
heck of a time debugging the following code:

Sorry, but here is a counter-example to that unfounded assertion. I really know
(which includes really think) that errno is a global variable. I wouldn't debug
the code since it's very obvious code, but if I had to debug it I wouldn't have
any problem with that either.

Now, a blanket assertion is proven false if there is any counter-example whatsoever.

The above is a counter-example.

#include <errno.h>

class errno_exception
{
int m_errno;
public:

errno_exception( int errno )
: m_errno( errno ) { }

int errno() const {
return m_errno;
}
};

There is no problem understanding this code in C++ since in C++ errno is known
to be a macro.

It's also a global variable. :)

And for a multi-threaded program it's usually implemented as thread-local storage.

In fact, the latter works in GCC 4, if and only if <errno.h> is removed.

Depends what you mean by "works" and "removed".

Regarding the latter I'll assume the least ureasonable meaning I can think of,
namely that you're talking about removing the #include directive.

In that case the code, when present in a complete program, may still not compile
due to [errno.h] being dragged in by some other header, which means, considering
the "if and only if", that by "works" you presumably mean "can be made to
compile and yield the same result as if 'errno' was replaced by 'x'".

Yes, it's a stretch to assume this meaning, by I'm trying to accomodate you by
choosing the least unreasonable meaning I can think of. ;-)

Do you still not see?


Cheers & hth.,

- Alf
 
J

Jean-Marc Bourguet

Jeff Schwab said:
Thanks; which functions are those? I'm looking at strtol, and it returns
one of several, recognizable values on error. (atoi isn't required to set
errno at all.)

About all functions in math.h. The heading text for this section contains:

For all functions, a domain error occurs if an input argument is outside
the domain over which the mathematical function is defined. The
description of each function lists any required domain errors; an
implementation may define additional domain errors, provided that such
errors are consistent with the mathematical definition of the function.
On a domain error, the function returns an implementation-defined value;
the value of the macro EDOM is stored in errno.

For cosh() for instance, you have that "A domain error occurs if the
magnitude of x is too large" and the only way I know to check that the
magnitude is too large is checking errno. Probably errno is set only if
there is no support for infinite and the implementation defined result will
be HUGE_VAL, but AFAIK that isn't mandatory.

Yours,
 
J

Jean-Marc Bourguet

Jeff Schwab said:
Sorry, where are you reading that? I don't see it in either the math
library man page (BSD) or the C++ standard. (I believe it, I'm just
curious what "the heading text for this section" refers to.)

Oh, I snipped this in a rewriting. In the C90 standard.

Yours,
 
A

Alf P. Steinbach

* Jeff Schwab:
That's like saying a function is a global variable, just because it
accesses one. errno is a macro, period, with all the attendant problems
that macros cause. See my reply elsethread.

Somewhere else-thread someone quoted from C90 standard, "... value of the macro
EDOM is stored in errno".

Explain how you store a value in a macro.


Thanks in advance,

- Alf
 
A

Alf P. Steinbach

* Jeff Schwab:
In C90, errno doesn't have to be a macro. In C++, it does.

Yes that's right.

But please answer the question instead of trying to evade it.

For C++, how do you store a value in a macro.


Thanks in advance,

- Alf
 
A

Alf P. Steinbach

* Jeff Schwab:
You don't. You may store a value in the lvalue to which errno resolves,
but you can't store anything in errno itself, except with #define.

Yes.

So when the C90 standard says

"stored in errno"

(referring to the act of storing something there), then in that statement
"errno" refers to

A. The macro?

B. The lvalue to which the macro resolves?

For option B, note that a macro is not an lvalue, although it can resolve to one.

So if you choose option B, then you cannot still maintain that "errno" is and
always refers to a macro, as you did up-thread: "That's like saying a function
is a global variable, just because it accesses one. errno is a macro, period"

Of course, since "errno" is a macro, by choosing option B you have then proven,
by deriving a contradiction, that your earlier stance (especially regarding
James' statement) -- namely macro, nothing else, period -- was incorrect.

I'm hoping you won't choose option A.


Cheers & hth.,

- Alf
 
J

James Kanze

Not really. For example, variable names respect scopes, but
errno does not.

That's a good point. I wasn't actually thinking of that level,
of course, and had more runtime behavior in mind, but it's
certainly a significant difference.
Someone who really thinks errno is a global variable will have
a heck of a time debugging the following code:
#include <errno.h>
class errno_exception
{
int m_errno;
public:
errno_exception( int errno )
: m_errno( errno ) { }
int errno() const {
return m_errno;
}
};
In fact, the latter works in GCC 4, if and only if <errno.h>
is removed.

It works with Sun CC if you compile for single threaded:).
As you've pointed out, though technically meaningless under
the current C++ standard, errno behaves as thread-local
storage on all modern Unixen. There is no danger that setting
errno in one thread will somehow affect a different thread.
(As with all things, some compilers apparently need a
particular set of flags before they'll do the right thing.)

What was bothering me the most was that I couldn't find this
guarantee in Posix. (But someone---I think it was you---has
since posted the reference.)
Whereas some versions of glibc prohibit
it.http://cr.yp.to/docs/unixport.html#errno
That's interesting. I wonder whether it's just for historical
reasons, or whether there are benefits. What's the global
variable called?

Well, Sun considers not breaking old code a benefit. If that
old code is mine, so do I:). But I was using the header, like
the standards require, long before the standards were written,
so it's no skin off my back one way or the other. (On the other
hand, I've very definitely seen code with "extern int errno"
inside of a function.)
I recently was critiqued for doing that, and actually, I'm
glad I was. I'm convinced now that the right way to use errno
is never to set it manually; it is purely a conduit for the
standard library to give further details, once an error has
already been reported. The right way to check whether a POSIX
or C function has failed is via its return code.

If it has one. What do you do about strtol? (The real problem
here is that there is no one solution. For historical reasons,
Unix and C functions have quite a number of different ways of
reporting errors.)
 
J

James Kanze

Thanks; which functions are those? I'm looking at strtol, and
it returns one of several, recognizable values on error.

Does it? Consider ``strtol( "0", NULL, 10 )'' and ``strtol(
"x", NULL, 10 )''. The first is correct, the second is an
error.
 
J

James Kanze

That's like saying a function is a global variable, just
because it accesses one. errno is a macro, period, with all
the attendant problems that macros cause. See my reply
elsethread.

Yes, but in the context of my mentionning it "(ever heard of
errno?)", I was referring to the problems due to the fact that
it ends up being a global variable (after, perhaps, a level of
indirection). In reality, of course, it has both all of the
problems of global variables, and all of the problems of macros.
(And of course, when I was learning C, it was a global
variable:).)
 
T

Tony

Lionel B said:
On Mar 7, 5:57 am, "Tony" <[email protected]> wrote:
[...]

"Anytime you can't reasonable handle the error in the immediately
calling function. "

I don't see that as a rule of thumb anymore since error codes can be
reliably propogated up the stack pretty easily without "jumping and
introducing alternative unwind mechanisms".

That was the whole point of James' comment, surely?

Probably not, as he didn't mention anything that recognizes that returning
an error code does not have to be error prone in C++ (in C, yes it is).
Yes, "error codes can
be reliably propogated up the stack", but at the cost of checking error
codes all the way up the stack

It's not so bad and it will cause one to search for ways of implementing
that cannot cause error.
and introducing appropriate return paths...

Nothing to introduce: the call stack unwind happens without any alternative
unwind mechanism. What could be simpler than nothing?
in other words, implementing your own (clunky) unwind mechanism, when a
neat language-level mechanism (exceptions) already exists.

If you're not doing multi-level propogation, and with error return codes you
don't need that, then there is nothing to implement over std C.

Tony
 
T

Tony

"There are really two
cases where exceptions impose themselves:

-- Cases where the error will require abandoning a large block
of processing---in servers, for example, an error which will
require abandonning (and rolling back) the transaction, or
even breaking the connection. In this case, one throw and
one catch may replace literally thousands of if's, as well
as rendering the expression of the algorithms involved
clearer."

I'll keep that in mind when I rearchitect some alpha database code (it's on
my to-do list). It currently doesn't use exceptions.

" -- In constructors, where the alternative is often using zombie
states or some such. If the constructor throws, the object
isn't there, so you don't have to worry about it. Even if
constructors had return codes (and you can easily simulate
them, if you want, using out parameters), exceptions would
be preferable, because they don't leave an incomplete object
laying around."

That may be the side effect of architecting with the RAII pattern. If
instead one chooses to define construction as initialization rather than
resource acquisition (at least in cases where errors can occur), then the
above may not be a justification for exceptions.

"There are probably other cases where they have practical
advantages, but these to are absolutes; the alternatives to
exceptions in these cases are very much more expensive, in terms
of development time and maintainability."

I'm hedging that that is not so. It may be one of those "if all you have is
a hammer" things.

Tony
 
T

Tony

"The only know alternative to exceptions is return codes: comparing
these two alternatives, exceptions come out as the big winner in every
case where the errorhandling will not take place right after the
call."

We'll have to agree to disagree on that. Just because there are the "low
hanging fruit" examples does not make your above assumption or opinion, or
whatever it is, true.

"[snipping a comment by James Kanze that looks as it came from you: you
really should setup your newsreader to perform proper quoting]"

I think it's JK's newsreader that is the problem. And your's too! "99.9%" of
the posts work, so it must be on your ends that cause OE not to do the '>'
thing.
"Anytime you can't reasonable handle the error in the immediately
calling function. "

I don't see that as a rule of thumb anymore since error codes can be
reliably propogated up the stack pretty easily without "jumping and
introducing alternative unwind mechanisms".

"This is a very fragile method, that takes considerable effort by the
programmers. It also heavily increases complexity of the program,
causing everything related to programming (reviewing, testing... )
much more difficult."

We'll have to agree to disagree on that also.
Yes, constructors and overloaded operators MAY be an issue. But maybe
that's
just a matter of coding style: if you choose to architect software using
RAII as a (or THE) design construct, then you will constantly be trying to
catch exceptions from constructors. It may be one of those "if it hurts
when
you do that, don't do that" things.

"No you do not. When you use exceptions, you catch where you handle
error conditions, and nowhere else. Nothing hurts."

You misunderstood my comment. I was saying, that because you are allowing
errors to occur within constructors (most likely because of the choice to
use RAII as a design pattern), then you have to use exceptions (the hurt).
So if using the RAII patter "hurts", don't do that (read, find another way).

Tony
 
T

Tony

Annie Testes said:
Tony said:
[skip] If the goal was to see what
alternatives develop (read, R&D), banning exception use would be not
stupid,
but facilitating. "Double blind" experiment or similar.

If it's for a research or teaching project, it can certainly be
interesting.

I'm doing R&D on a production project.
If only to show how painful error management could be
in the good 'ol days of C :)

That's a silly thing to say or at least a silly assumption (that there are
only 2 alternatives: exceptions and C-techniques).
Reliability:
One common problem in C, is that programers tend to forget to check
and/or
propagate errors. Since C++ has more functionalities, it can give
some help
that doesn't exist in C.

That's exactly what I said above!
Jumping:
In C, it's common to use early return from a function when
encountering a
error, which is a kind of jump.

No, it's a goto. Big difference: it's not multi-level propogation (see
thread topic).

[snipped naive/contrived/undeveloped (pick as appropriate) example]
Alternative unwind mechanisms

Even C has a stack unwinding mechanism, using setjmp and longjmp
(clunkier than exceptions, I think). And although I've never used it,
its mere existence makes me think that there are circumstances where
stack unwinding is clearly better than any alternative.

I never used it in C either, which even moreso makes the technique rather
suspect or at least overly-clever (?).
You can avoid exceptions in constructors by doing two-stage
initialization

Implied above was that RAII isn't a panacea and that "construction is
initialization (only)" is a potential alternative. YMMV.

[snipped example that does not show that RAII is worth the side-effects]

Tony
 
J

James Kanze

I'll keep that in mind when I rearchitect some alpha database
code (it's on my to-do list). It currently doesn't use
exceptions.

It really depends on the application, but I've found them very
effective in transaction oriented systems. In such cases, they
work very well with RAII too: the destructor of the transaction
object does a roll-back if commit hasn't been called. (This
does require some care, however---as a general rule, you
shouldn't do anything which can fail in a destructor. But if
roll-back can fail, you have a problem in maintaining
consistency anyway.)
That may be the side effect of architecting with the RAII
pattern. If instead one chooses to define construction as
initialization rather than resource acquisition (at least in
cases where errors can occur), then the above may not be a
justification for exceptions.

It has nothing to do with RAII. If you can't construct an
object (for whatever reasons), that object shouldn't exist.
Throwing an exception from the constructor is the simplest way
to achieve that.
I'm hedging that that is not so. It may be one of those "if
all you have is a hammer" things.

It's a tool. It sounds to me that your rejection of them is
more along the "if all you have is a hammer". You have
exceptions, return codes, and a few other alternatives. You
should evaluate each case, and use the most appropriate.
Without excluding any of the alternatives a priori.
 
J

James Kanze


No. All of the values it "returns" could be the result of legal
input.
In which case strtol is guaranteed to return 0. That value is
not sufficient to tell whether there was an error, but that's
not what I was claiming. I was proposing that one should
never have to assign to errno. Jean-Marc Bourguet has since
pointed that assignment to errno seems to be necessary for
telling whether cmath functions have failed.

Yes. The problem is that other functions may already have set
errno to a non-zero value.
This is yet another case where a single return path is serving
double-duty: "Did an error occur?" and "If no error occurred,
what was the result of the calculation?" It would be my
preference to wrap strtol in a function that throws exceptions
on error, to avoid the need for little if-statements
everywhere it is called.

I don't know. I think in this case, a lot of the time, you
could (and probably would want to) handle the error immediately
in the calling code. So an exception really isn't appropriate.
In a new design, I'd probably use Fallible. But note that the
function already has a number of "return values": the error
code, the results of the conversion, and the new end pointer.
Globally, I think std::istream handles the case a lot cleaner.
 
T

Tony

"There are really two
cases where exceptions impose themselves:

-- Cases where the error will require abandoning a large block
of processing---in servers, for example, an error which will
require abandonning (and rolling back) the transaction, or
even breaking the connection. In this case, one throw and
one catch may replace literally thousands of if's, as well
as rendering the expression of the algorithms involved
clearer."

I'll keep that in mind when I rearchitect some alpha database code (it's on
my to-do list). It currently doesn't use exceptions.

" -- In constructors, where the alternative is often using zombie
states or some such. If the constructor throws, the object
isn't there, so you don't have to worry about it. Even if
constructors had return codes (and you can easily simulate
them, if you want, using out parameters), exceptions would
be preferable, because they don't leave an incomplete object
laying around."

That may be the side effect of architecting with the RAII pattern. If
instead one chooses to define construction as initialization rather than
resource acquisition (at least in cases where errors can occur), then the
above may not be a justification for exceptions.

"There are probably other cases where they have practical
advantages, but these to are absolutes; the alternatives to
exceptions in these cases are very much more expensive, in terms
of development time and maintainability."

I'm hedging that that is not so. It may be one of those "if all you have is
a hammer" things.

Tony
 
T

Tony

Lionel B said:
On Mar 7, 5:57 am, "Tony" <[email protected]> wrote:
[...]

"Anytime you can't reasonable handle the error in the immediately
calling function. "

I don't see that as a rule of thumb anymore since error codes can be
reliably propogated up the stack pretty easily without "jumping and
introducing alternative unwind mechanisms".

That was the whole point of James' comment, surely?

Probably not, as he didn't mention anything that recognizes that returning
an error code does not have to be error prone in C++ (in C, yes it is).
Yes, "error codes can
be reliably propogated up the stack", but at the cost of checking error
codes all the way up the stack

It's not so bad and it will cause one to search for ways of implementing
functions that cannot cause error.
and introducing appropriate return paths...

Nothing to introduce: the call stack unwind happens without any alternative
unwind mechanism. What could be simpler than nothing?
in other words, implementing your own (clunky) unwind mechanism, when a
neat language-level mechanism (exceptions) already exists.

If you're not doing multi-level propogation, and with error return codes you
don't need that, then there is nothing to implement over std C.

Tony
 
T

Tony

That may be the side effect of architecting with the RAII
pattern. If instead one chooses to define construction as
initialization rather than resource acquisition (at least in
cases where errors can occur), then the above may not be a
justification for exceptions.

"It has nothing to do with RAII."

Sure it does. If you use that pattern, especially if it's the only one in
your toolbox, then your constructors or something called in those
constructors is probably going to have the possibility of raising an
exception.

"If you can't construct an
object (for whatever reasons), that object shouldn't exist."

"construction" is a subjective term. As I said above, your need for
exceptions to handle errors from constructors depends on if you define
"construction" to be "resource acquisition" (large potential to raise error)
or "data member initialization" (small/no potential to raise error).

"Throwing an exception from the constructor is the simplest way
to achieve that."

Avoiding the scenario seems much simpler.
I'm hedging that that is not so. It may be one of those "if
all you have is a hammer" things.

"It's a tool."

So is a bulldozer, but I won't use one of those to shovel snow off of my
sidewalk, even if I did own one and even if it was parked in the driveway.

"It sounds to me that your rejection of them is
more along the "if all you have is a hammer"."

I'm not rejecting them. I'm just developing a better way to handle errors.
It looks doable at this juncture but I have not encountered all the
scenarios yet. I don't need something as capable or general as exceptions
(yet? Time will tell.).

"You have
exceptions, return codes, and a few other alternatives. You
should evaluate each case, and use the most appropriate.
Without excluding any of the alternatives a priori."

For me, it's not what exists, but rather what I can build. I shy away from
overly-general and overly-functional offerings for obvious reasons. I
actually like the semantics of C++ exceptions when used simply and obviously
and I do something similar with my own EH mechanisms.

Tony
 

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,774
Messages
2,569,596
Members
45,135
Latest member
VeronaShap
Top