Error handling in C

J

James Kuyper

Tony said:
James Kuyper said:
arnuld wrote:
...
Also some time ago some one told me that when I get an error then I can
create a generic function which, when called within some function will
print the name of that function automatically or something like that I
forgot. It had lots of #define(s) . Can someone write that for me ?
In C99 you can use the pre-defined identifier __func__. When used, it
behaves exactly as if the following declaration had been inserted
immediately after the '{' at the start of the enclosing function:

static const char __func__[] = "function-name";

This feature was not available in C89, and I know of no way to do what
you're asking for without making use of __func__.

Unfortunately, one does not have any control of how that function name
string will look. ...

True: the standard mandates that it contain the exact name of the
function. Do you want it to contain something else?
... (I have forgotten how it DOES look since I have a macro
that inserts the declaration shown above with the format I want). __FILE__
is even worse though: Maybe I want a full path with the filename and maybe I
don't. So, I don't use the __FILE__ macro either. I do use the __LINE__
macro though (not much to go wrong with that!). I wonder if it is
implementation defined what __FILE__ and __func__ include (?).

The expansion of the __FILE__ macro is not explicitly
implementation-defined, the standard just says that it is the "presumed
name of the current source file". However, the details of file and path
naming conventions are platform-specific, and are outside the scope of
the C standard. Therefore, the C standard neither mandates nor prohibits
including of the full path as part of the file name.

For __func__ the situation is quite difference. A function's name is a
well-defined concept within the context of a C program, and __func__ is
supposed to contain that function name. I don't see any wiggle room for
variant interpretations.
... Finally, as a
minor peeve: why are the specifiers not both consistent in case (__FILE__ &
__FUNC__ or __file__ & __func__)?

The basic reason is that __FUNC__ is a macro, while __func__ is the name
of an object. There's a widespread convention that names in which all
letters are upper case should be used for all macros, and only for
macros (many people treat enumeration constants the same as macros for
this purpose).

The C standard library contains some violations of this convention, but
that's mainly because C has developed over many decades, and that
convention did not become conventional until long after many features of
the C standard library had become fixed in a form that conflicts with
that convention.
 
T

Tony

blargg said:
Tony said:
Which would be a very acceptable strategy for a large class of
errors if C had a return value type that "exploded" (C++ term) if
it wasn't checked. Then, the "return an error indicator" strategy
becomes robust and not "error prone" as so many C++ afficionados
continually harp as a major justification for exception handling.
[...]

Checking return values still clutters all code between the source of
the error and the handler.

It's hardly "cluttered" IMO:

if(!MyFunc()) // zero return means error in my preferred syntax.
// handle error

Again, not all functions generate errors and less so would have to if C had
pass-by-reference.
I think that's the main argument against
them, even if the compiler caught unexamined return values.

"Installable handlers" are appropriate some places also: one size does not
fit all (including and especially so, exceptions). See, for instance, how
C++ handles memory allocation failure with set_new_handler(), a viable and
simple technique. C++ of course has functions that the compiler calls behind
the scenes so EH is required in those instances (constructors, copy
constructors, assignment operators, conversion operators... etc).
Does your "exploding" return value proposal basically work like this?

error_t f( void );

void user( void )
{
error_t err;

No. No need to declare anything (see "if(!MyFunc())" example above.
...
err = f(); // OK
if ( err != no_error )
...

err = f(); // OK
err = f(); // OK? (overwrites err that was never examined)

While you could have some kind of error code returned, I prefer just yes/no
error indicator. One could code up the exploder to set last error per thread
or whatever. There are many possible ways but the concept is pretty much
that the return val cannot be ignored.
f(); // compile-time error

In C++, I use a class, so it is a runtime error (mine produces a stack
trace).
(void) f(); // OK, explicitly ignore error

Can't do that. The return val will still explode.
}

If the "OK?" line is legal, you can get mostly what you want already:

void f( error_t* err_out ); // if err_out != NULL, sets *err_out

void user( void )
{
error_t err;
...
f( &err );
if ( err != no_error )
...

f( &err );
f( &err );

f(); // compile-time error

f( NULL ); // OK, explicitly ignore error
}

(interestingly, I've implemented a class in C++ very much like your
"exploding" return value, which drops into the debugger at run-time if
the error is never exmained, for use in code that must avoid
exceptions)

I'm a C++ user also and have an exploding return val class also. I got it
off the net somewhere and modified it to my liking. That's where this post
originated from: trying to get the same good concept in C (and as some kind
of built-in, it could probably be better then the C++ class implementation
though the latter works fine for me).

Tony
 
T

Tony

James Kuyper said:
Tony said:
James Kuyper said:
arnuld wrote:
...
Also some time ago some one told me that when I get an error then I can
create a generic function which, when called within some function will
print the name of that function automatically or something like that I
forgot. It had lots of #define(s) . Can someone write that for me ?
In C99 you can use the pre-defined identifier __func__. When used, it
behaves exactly as if the following declaration had been inserted
immediately after the '{' at the start of the enclosing function:

static const char __func__[] = "function-name";

This feature was not available in C89, and I know of no way to do what
you're asking for without making use of __func__.

Unfortunately, one does not have any control of how that function name
string will look. ...

True: the standard mandates that it contain the exact name of the
function. Do you want it to contain something else?

Excuse me for mentioning C++ so much, but I do use that and recognize that
somewhere between C and C++ is that NICE language I chomp at the bit for. If
you have overloaded functions, just having the function name is not going to
tell you what function is being scrutinized. For overloaded functions, I
include the argument type specifiers. It may be moot then if the current
standard doesn't have overloaded functions, but I admit to forward
thinking/hoping.
The expansion of the __FILE__ macro is not explicitly
implementation-defined, the standard just says that it is the "presumed
name of the current source file". However, the details of file and path
naming conventions are platform-specific, and are outside the scope of the
C standard. Therefore, the C standard neither mandates nor prohibits
including of the full path as part of the file name.

For __func__ the situation is quite difference. A function's name is a
well-defined concept within the context of a C program, and __func__ is
supposed to contain that function name. I don't see any wiggle room for
variant interpretations.

The overloaded function issue mentioned above could become relevant someday.
(I'm not up to date on the current C standard).
The basic reason is that __FUNC__ is a macro, while __func__ is the name
of an object. There's a widespread convention that names in which all
letters are upper case should be used for all macros, and only for macros
(many people treat enumeration constants the same as macros for this
purpose).
OK.


The C standard library contains some violations of this convention, but
that's mainly because C has developed over many decades, and that
convention did not become conventional until long after many features of
the C standard library had become fixed in a form that conflicts with that
convention.

Tony
 
T

Tony

James Kuyper said:
arnuld wrote:
...

In my personal opinion, using exit() to deal with a problem is seldom the
right approach. A function should almost always report any problem that
severe to it's caller, and let the calling routine decide how to deal with
it. This approach makes it easier to re-use code. A problem that gives you
reason to halt one program might, when it occurs in a different program
using the same subroutine, be a reason to clean something up (free
allocated memory, close open files, etc.) and try something different.
Note: the "other program" might very well be a future version of the same
program.

This even applies to main(). Much of the code that currently resides in
main() might, in some other program, become a subroutine of main(). Making
the conversion is easier if you use "return EXIT_FAILURE;" rather than
"exit(EXIT_FAILURE);". The practical consequence are almost the same,
unless main() directly or indirectly calls itself.

I have found that discussing EH is difficult without a given context. It
gets a lot easier to address if one gives GUI program or Daemon as a context
for example. At least it narrows down the possibilities. Any "one size fits
all" TECHNIQUE argument is futile IMO. And that's exactly how I categorize
EH strategies: as stylistic techniques that are context (program type or
framework or finer grained even) specific.

Tony
 
J

James Kuyper

Tony said:
....
I have found that discussing EH is difficult without a given context. It
gets a lot easier to address if one gives GUI program or Daemon as a context
for example. At least it narrows down the possibilities. Any "one size fits
all" TECHNIQUE argument is futile IMO. And that's exactly how I categorize
EH strategies: as stylistic techniques that are context (program type or
framework or finer grained even) specific.

I couldn't agree more; it's because the context matters that I strongly
recommend the general rule of deferring error handling to the caller of
the routine where the error was detected, because the calling routine in
general knows more about the context than the called routine. Whether
this general rule should, or even can, be applied in any given case
depends upon the context; but it it the most context-agnostic approach
that can be taken.
 
J

James Kuyper

Tony said:
James Kuyper said:
Tony said:
....
In C99 you can use the pre-defined identifier __func__. When used, it
behaves exactly as if the following declaration had been inserted
immediately after the '{' at the start of the enclosing function:

static const char __func__[] = "function-name";

This feature was not available in C89, and I know of no way to do what
you're asking for without making use of __func__.

Unfortunately, one does not have any control of how that function name
string will look. ...
True: the standard mandates that it contain the exact name of the
function. Do you want it to contain something else?

Excuse me for mentioning C++ so much, but I do use that and recognize that

Any questions you have about C++ should be posted to comp.lang.c++, not
here. You'll find more people both willing to help you with such
questions, and sufficiently competent to answer them, than you will
here. When you talk to me about C++, you're talking with someone with a
great deal of theoretical knowledge of C++, but only three weeks of paid
programming experience in the language (and not a whole lot more unpaid
experience, either).
somewhere between C and C++ is that NICE language I chomp at the bit for. If
you have overloaded functions, just having the function name is not going to
tell you what function is being scrutinized. For overloaded functions, I
include the argument type specifiers. It may be moot then if the current
standard doesn't have overloaded functions, but I admit to forward
thinking/hoping.

It is precisely that issue that delayed introduction of __func__ into
C++. I have a copy of n2723.pdf, a draft version of the next release of
the C++ standard, and __func__ is included, but in C++ it's contents are
implementation-defined. I don't know whether it was part of the 2003
release, or is new in the next release, but it wasn't part of the 1998
release.

....
The overloaded function issue mentioned above could become relevant someday.
(I'm not up to date on the current C standard).

It's not currently an issue. The closest that C currently comes to
having function overloading is the <tgmath.h> library routines; and the
mechanism used to achieve that behavior is not available for
user-defined functions.
 
T

Tony

blargg said:
Tony said:
blargg said:
basically you have to check the return value of every function.

Which would be a very acceptable strategy for a large class of
errors if C had a return value type that "exploded" (C++ term) if
it wasn't checked. Then, the "return an error indicator" strategy
becomes robust and not "error prone" as so many C++ afficionados
continually harp as a major justification for exception handling.
[...]

Checking return values still clutters all code between the source of
the error and the handler.

It's hardly "cluttered" IMO:

if(!MyFunc()) // zero return means error in my preferred syntax.
// handle error

What if you want more than pass/fail?

That's easily doable too. I just prefer the pass/fail and then go to
somewhere else if need be or I want to: such as getlasterr() (similar to
Windows technique). The possibilities are many for how you want to set it
up: installable handlers, inline handling or whatever. I like a return of
zero to mean failure and that gives the nice clean syntax above. You
probably didn't read my whole post before you responded to this portion.

The user would certainly like to
know whether he needs to get more memory, a bigger disk, or just needs
to correct a typo in the pathname.

Call getlasterror().
So you need to save the return
value of MyFunc(),

No you don't.

[snipped additional unapplicable text written because of erroneous
assumption]
Not sure what you mean here, but when I think of errors I think of
things like insufficient memory, file not found, disk full, I/O error,
network timeout, etc., which aren't an artifact of the language.

Precondition violations are errors also. The potential for a null
pass-by-adddress argument are plentiful. Pass-by-reference wipes out all
those checks for null args. Different types or categories of errors may
indeed have different error handling strategies/mechanisms. As do different
program types. Context always matters when considering EH.
The main purpose of the new handler in C++ is to free up memory so the
allocation can be retried.

No it's not. That is just one possibility, but probably the most commonly
known one. It depends on the context what the developer finds appropriate
action. The technique is valid though and highly applicable.
But if you advocate "installable handlers" in general, you end up with
a situation very similar to exceptions.

Yes, without all the exception machinery! I call that a simple and elegant
solution.

[snip commonly known setjmp/longjmp technique example]

Oh, you were talking about emulating exceptions with setjmp/longjmp (where
exceptions came from of course). That's not what I meant by "installable
handlers". By "installable handlers" I meant exactly the example I gave:
C++'s set_new_handler().
Can you give a more realistic example of how your scheme would work in
C if the C library itself used the mechanism too? For example, a
program which used file I/O and some utility functions that allocated
memory during their operation.

Exercise left for the student/reader! (I'm a library developer and am slowly
but surely replacing the C++ std lib in my codebase, so I am less and less
familiar with it than I used to be. Ditto then for the C std lib). Again
though, a "one size fits all" EH strategy is not for me. YMMV.
Does your "exploding" return value proposal basically work like this? [...]
error_t err; [...]
err = f(); // OK
if ( err != no_error )
...

err = f(); // OK
err = f(); // OK? (overwrites err that was never examined)

While you could have some kind of error code returned, I prefer just
yes/no
error indicator. One could code up the exploder to set last error per
thread
or whatever. There are many possible ways but the concept is pretty much
that the return val cannot be ignored.
f(); // compile-time error

In C++, I use a class, so it is a runtime error (mine produces a stack
trace).
(void) f(); // OK, explicitly ignore error

Can't do that. The return val will still explode.

So you have to write

if ( !f() ) { }

(which is what one must write with my C++ version of your concept,
heh)

Well no, not necessarily. Any assignment from f() will keep the return val
from exploding. As noted, the well-known C++ exploding return val is not a
panacea: returning useful vals (other than error indicators) from functions
is hard to let go of.
[...]
(interestingly, I've implemented a class in C++ very much like your
"exploding" return value
[...]
I'm a C++ user also and have an exploding return val class also. I got it
off the net somewhere and modified it to my liking. That's where this
post
originated from: trying to get the same good concept in C (and as some
kind
of built-in, it could probably be better then the C++ class
implementation
though the latter works fine for me).

Yes, a compiler version would generate a compile-time error. With the
C++ approach, every error-handling code path must be executed in order
to ensure they all handle error returns.

I don't find any problem with that at all though. I prefer a minimum of
"black box" stuff going on.

Tony
 
T

Tony

James Kuyper said:
Tony said:
James Kuyper said:
Tony wrote:
...
In C99 you can use the pre-defined identifier __func__. When used, it
behaves exactly as if the following declaration had been inserted
immediately after the '{' at the start of the enclosing function:

static const char __func__[] = "function-name";

This feature was not available in C89, and I know of no way to do what
you're asking for without making use of __func__.

Unfortunately, one does not have any control of how that function name
string will look. ...
True: the standard mandates that it contain the exact name of the
function. Do you want it to contain something else?

Excuse me for mentioning C++ so much, but I do use that and recognize
that

Any questions you have about C++ should be posted to comp.lang.c++, not
here.

You didn't read the whole sentence even!! Shame on you for taking half a
sentence and giving the above (and below) spiel!.
You'll find more people both willing to help you with such questions, and
sufficiently competent to answer them, than you will here. When you talk
to me about C++, you're talking with someone with a great deal of
theoretical knowledge of C++, but only three weeks of paid programming
experience in the language (and not a whole lot more unpaid experience,
either).

Well just don't participate in the threads then that discuss C from outside
of the box that is the CURRENT standard if you don't want to or feel you
can't add anything.
It is precisely that issue that delayed introduction of __func__ into C++.

Well my simple solution was to include the arg types for overloaded
functions as the "func name". It works for my purposes. I don't know what
else the camel group may have been considering that would warrant some kind
of delay in introducing func (compatibility with C probably?).
I have a copy of n2723.pdf, a draft version of the next release of the C++
standard, and __func__ is included,

I think I am using my platform's compiler's implementation though it doesn't
buy me anything. Probably a loss to use cuz I don't know off hand how it
handles overloaded funcs.
but in C++ it's contents are implementation-defined.

It's pretty much just a debug aid for me. I probably won't keep it in for
release builds. I haven't decided yet. It's hardly high up on the pareto
list right now.
...

It's not currently an issue. The closest that C currently comes to having
function overloading is the <tgmath.h> library routines; and the mechanism
used to achieve that behavior is not available for user-defined functions.

Well, I'll say again that my ideal language is somewhere in-between C and
C++.

Tony
 
J

James Kuyper

Tony said:
You didn't read the whole sentence even!! Shame on you for taking half a
sentence and giving the above (and below) spiel!.

I fully understand that you like both C and C++, and would prefer a
language with some of the features of both. That doesn't change the fact
that for any questions you have about features that are currently in
C++, and not in C, you'll find better answers in comp.lang.c++.
Well my simple solution was to include the arg types for overloaded
functions as the "func name".

That sounds reasonable; but I refer you to comp.std.c++ for a better
explanation than I can give of why it's problematic. I'll just say that
several different popular schemes, and the committee was unwilling to
find a consensus on a single format that it was willing to impose upon
all implementations. That's why:
 
K

Kenny McCormack

James Kuyper said:
I couldn't agree more; it's because the context matters that I strongly
recommend the general rule of deferring error handling to the caller of
the routine where the error was detected, because the calling routine in
general knows more about the context than the called routine. Whether
this general rule should, or even can, be applied in any given case
depends upon the context; but it it the most context-agnostic approach
that can be taken.

Whoosh!!!
 
K

Kenny McCormack

Any questions you have about C++ should be posted to comp.lang.c++, not
here.

You didn't read the whole sentence even!! Shame on you for taking half a
sentence and giving the above (and below) spiel!.
You'll find more people both willing to help you with such questions, and
sufficiently competent to answer them, than you will here. When you talk
to me about C++, you're talking with someone with a great deal of
theoretical knowledge of C++, but only three weeks of paid programming
experience in the language (and not a whole lot more unpaid experience,
either).

Well just don't participate in the threads then that discuss C from outside
of the box that is the CURRENT standard if you don't want to or feel you
can't add anything.[/QUOTE]

You're just not allowed to talk like that around here.
These people are constitutionally unable to ignore that which displeases
them.

Think:
1) Discussing your latest abortion in a KofC meeting.
2) Being a Holocaust denier in a B'nai B'rith meeting.
3) Discussing conservatism (*) in a Bush White House staff meeting.

(*) I.e., real conservatism (Barry Goldwater style).
 
K

Kenny McCormack

Tony said:
And it's a techie programmer room. Go figure.

But that's the funny thing. It (CLC) is *not* a "techie programmer room".
It may have been at one point in the past, but it hasn't been since it
got hijacked by a bunch of religious fanatics (*) - about 10-15 years ago.

(*) And I mean that (the phrase "religious fanatics") both literally and
figuratively. Many of the CLC regs are, in fact, Xtian religious
nutcases in real life. So I've heard...
 
K

Kenny McCormack

that for any questions you have about features that are currently in C++,
and not in C, you'll find better answers in comp.lang.c++.

The only fact is that you are are akin to a warrior ant in defense of the
hive. Noted. And buh bye.[/QUOTE]

(Now switching into CLC-pedant-but-totally-missing-the-point,
can-you-say-Whoosh!!!, mode)

Ants live in hills; bees live in hives.
But both are off-topic in comp.lang.c. May I helpfully and oh-so-smugly
suggest you try alt.insects instead?
 
J

James Kuyper

Richard said:
Malcolm McLean said:



No, you can't say that - at least, not truthfully and knowledgeably.
Firstly, either the bug is there or it isn't - if it *is* there,
you might *encounter* it less than once a year in normal use, or
you might not.

He was clearly referring to the triggering of the bug, rather than to
it's existence. His wording may not have been perfect, but it wasn't bad
enough to justify fixating on it like that.
... Secondly, playtesters are not normal users (I've
never actually been one, but I've met several professional
playtesters over the years, and believe me, they ain't normal where
games are concerned). ...

What alternative would you suggest to playtesting? It shouldn't be the
only method used to test for bugs, but I certainly wouldn't trust the
usability of a game program that had never been playtested. He didn't
specify "professional" playtesters; would the use of non-professionals
make you feel any better about that method of testing? Without paying
them, it might be difficult to keep them motivated and focused enough to
be useful.
... Thirdly, your probability theory is faulty -
you're claiming that if a low-probability event doesn't occur in a
given timespan T, it will occur on average less than once per T.
That's simply wrong.

No, it's a very rough estimate of the upper limit, and usable only as
such; but perfectly usable when all the limitations implied by the
phrases "very rough", "estimate", and "upper limit" are kept in mind.
Technically, he overstated his case somewhat, but I see no problem with
using such an approach to estimate whether or not it's cost-effective to
bother fixing a bug that is sufficiently complex that fixing it will
more than double the cost of the game.
Your claim is that shoddy work is acceptable. It is not a view that
I share. Yes, doing things right is expensive. But doing them wrong
is invariably more expensive in the long run.

There is always a trade-off between cost and quality. An absolute
insistence on "doing things right", regardless of the cost, is nonsense.
Spending too much time and money to achieve the ideal of a perfect
program is just as effective a route to bankruptcy as spending too
little. The tricky part is knowing how much is too much, and how much is
too little.
 
J

James Kuyper

Richard Heathfield wrote:
....
In the real world, perfection is *not* required - but excellence is
valued highly. If a bug makes it into production, pretending it
isn't there is hardly a hallmark of excellence. The proper response
to a known bug is to publish its existence, together with a
workaround if one is known and a fix-by date if one is available.

Nothing he said is inconsistent with the approach you've just described.
The discussion has been about how you decide

* when to stop looking for bugs, despite the fact that you know there
probably are more to be found.
* when to delay, possibly indefinitely, fixing a bug that has minor
benefits and major costs.

It hasn't been about how you notify users about the bugs you've found
but haven't fixed yet.

The answer to these questions varies, and quite properly so, with the
importance of the task that the program performs. That importance is a
lot lower for game programs than for programs where the continued
existence of thousands of people depends upon the program executing
correctly.
 
B

Bartc

Richard Heathfield said:
Malcolm McLean said:

No, you can't say that - at least, not truthfully and knowledgeably.
Firstly, either the bug is there or it isn't - if it *is* there,
you might *encounter* it less than once a year in normal use, or
you might not

Your claim is that shoddy work is acceptable. It is not a view that
I share.

In the sort of work I was doing, it wasn't possible to cover everything.

If a client discovered something not quite right, then it would get fixed,
so in a way clients were also testing the software. (When we made a sale, my
boss was delighted, but for me I saw it as yet another customer working
fulltime discovering bugs in my work)

But, obscure bugs discovered after perhaps 1000 man-years of use did tend to
get low-priority. Especially if they weren't easily repeatable..
Yes, doing things right is expensive. But doing them wrong
is invariably more expensive in the long run.

I don't know. One of the selling points of a support contract /was/ getting
bug-fixes. And in our small, informal setup fixes could sometimes be done
within hours, adding more perceived value. (When I find a bug in one of my
MS products I don't bother with it; what's the point?)
 
R

Richard Tobin

Richard Heathfield said:
But doing them wrong is invariably more expensive in the long run.

This seems most unlikely, unless you define "wrong" to mean "such as
to cost more in the long run".

-- Richard
 
C

c prog fan

James said:
There is always a trade-off between cost and quality. An absolute
insistence on "doing things right", regardless of the cost, is nonsense.
Spending too much time and money to achieve the ideal of a perfect
program is just as effective a route to bankruptcy as spending too
little. The tricky part is knowing how much is too much, and how much is
too little.

This nice point is not limited to the particular case in the discussion
IMHO at least.
 
C

c prog fan

Richard said:
Here, I agree. Perfection is an unapproachable ideal. But excellence
is achievable. Telling your customers their disk is dirty (when
what you really mean is that your program screwed up) is not a
route to excellence.

Agreed; For sure trying to eliminate easily encountered bugs as soon as
passible in a must IMHO; Also for thougher ones an error reporing scheme
plus patches and adequate support might be amongst solutions.
 
K

Keith Thompson

Malcolm McLean said:
All our software is perfect. Therefore any errors have to be the
result of a dirty disk.

So you're deliberately lying to your customers. Personally, I find
that far more offensive than the occasionial crash or other bug. You
might be able to make money that way if you don't get caught.

In any case, this is no longer about C.
 

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
474,262
Messages
2,571,059
Members
48,769
Latest member
Clifft

Latest Threads

Top