Why leave the error handling to the caller?

C

Chad

On to top of page 163 in the book "The C Programming Langauge" by K &
R, they have the following:

char *strdup(char *s)
{
char *p;
p=(char *)malloc(strlen(s)+1);
if( p != NULL)
strcpy(p,s):
return p;
}

They then go on to say

"strdup passes that value on, leaving error-handling to its caller"

Why leave the error-handling to it's caller? Couldn't a person go like

char *strdup(char *s)
{
char *p;
if (p=(char *)malloc(strlen(s)+1) == NULL)
exit(1);
else
strcpy(p,s):
return p;
}


Chad
 
J

jacob navia

Chad said:
On to top of page 163 in the book "The C Programming Langauge" by K &
R, they have the following:

char *strdup(char *s)
{
char *p;
p=(char *)malloc(strlen(s)+1);
if( p != NULL)
strcpy(p,s):
return p;
}

They then go on to say

"strdup passes that value on, leaving error-handling to its caller"

Why leave the error-handling to it's caller? Couldn't a person go like

char *strdup(char *s)
{
char *p;
if (p=(char *)malloc(strlen(s)+1) == NULL)
exit(1);
else
strcpy(p,s):
return p;
}


Chad

Exiting the program is not the only possibility here.
The function has no way to know what the general context is, and can't
decide what is the best thing to do.

For starters, I would NOT like that I risk that a library routine
exits my program without any possibility for me to avoid this
or to take an alternative course of action.
 
R

Richard Heathfield

Chad said:
On to top of page 163 in the book "The C Programming Langauge" by K &
R, they have the following:

char *strdup(char *s)
{
char *p;
p=(char *)malloc(strlen(s)+1);
if( p != NULL)
strcpy(p,s):
return p;
}

Better:

#include <stdlib.h>
#include <string.h>

char *dupstr(const char *s)
{
size_t len = strlen(s) + 1;
char *p = malloc(len);
if(p != NULL)
{
memcpy(p, s, len);
}
return p;
}
They then go on to say

"strdup passes that value on, leaving error-handling to its caller"

Why leave the error-handling to it's caller?

Because it is not a library function's job to make that kind of
decision. A library function should do what it is supposed to do or
explain why it can't, and *nothing else*. The decision to abend a
program is not the library's to make.
 
M

Malcolm McLean

Chad said:
On to top of page 163 in the book "The C Programming Langauge" by K &
R, they have the following:

char *strdup(char *s)
{
char *p;
p=(char *)malloc(strlen(s)+1);
if( p != NULL)
strcpy(p,s):
return p;
}

They then go on to say

"strdup passes that value on, leaving error-handling to its caller"

Why leave the error-handling to it's caller? Couldn't a person go like

char *strdup(char *s)
{
char *p;
if (p=(char *)malloc(strlen(s)+1) == NULL)
exit(1);
else
strcpy(p,s):
return p;
}
There quite a strong case for a safemalloc() library function that does
terminate with an error message on fail. However we don't have it.

Another problem is vandalism of stderr. Microsoft do this. The call
executes, but no error message appears. Which means that stderr is useless
in portable routines.

More modern languages have exception handling, which is the best solution,
in the right hands. The program will terminate with an error message, unless
the caller catches the exception. However in practise inexperienced people
often make a real hash of exceptions, usually by catching too many too low,
or throwing oddballs like "negative root error" where they meant "illegal
argument", or sometimes by using them for non-error flow control.

So we are stuck with handling out of memory conditions by freeing and
returning an error code, which usually means that caller has to to the same,
until eventually control reaches a high level routine which has access to
the user interface.
 
R

Richard Heathfield

Malcolm McLean said:

There quite a strong case for a safemalloc() library function that
does terminate with an error message on fail.

No, there isn't. Library routines have no business deciding to terminate
the program.
Another problem is vandalism of stderr. Microsoft do this. The call
executes, but no error message appears.

Not so. In their hosted environment, stderr is handled correctly. And in
their freestanding environment, they don't have to support it at all.
So we are stuck with handling out of memory conditions by freeing and
returning an error code, which usually means that caller has to to the
same, until eventually control reaches a high level routine which has
access to the user interface.

In other words, decision-making power is where it belongs - in the hands
of the programmer.
 
G

Gordon Burditt

Why leave the error-handling to it's caller? Couldn't a person go like
char *strdup(char *s)
{
char *p;
if (p=(char *)malloc(strlen(s)+1) == NULL)
exit(1);
else
strcpy(p,s):
return p;
}

I certainly don't want to fly on any airplanes where you wrote the
flight control programs.

I would also be very annoyed if attempting to paste a large document
into the current one caused the editor to exit without giving me a
chance to save the old one, even if the paste failed due to lack
of memory.

There is the problem that a low-level routine does not know how to
handle errors. If it needs to interact with the user, how should
it do it? Text out stderr? (might not be visible) An X window
popup? A Microsoft Window popup?
 
C

Coos Haak

Op Sun, 17 Jun 2007 09:06:39 -0700 schreef Chad:
On to top of page 163 in the book "The C Programming Langauge" by K &
R, they have the following:

char *strdup(char *s)
{
char *p;
p=(char *)malloc(strlen(s)+1);
if( p != NULL)
strcpy(p,s):
return p;
}
Get a newer edition: K&R2 doesn't even mention strdup. With reason, see
elsethread.
 
C

Coos Haak

Op Sun, 17 Jun 2007 20:47:19 +0200 schreef Coos Haak:
Op Sun, 17 Jun 2007 09:06:39 -0700 schreef Chad:

Get a newer edition: K&R2 doesn't even mention strdup. With reason, see
elsethread.

It was not in the index, but the same definition now is placed at the top
of page 143 ;-(
 
J

Johan Bengtsson

Chad said:
On to top of page 163 in the book "The C Programming Langauge" by K &
R, they have the following:

char *strdup(char *s)
{
char *p;
p=(char *)malloc(strlen(s)+1);
if( p != NULL)
strcpy(p,s):
return p;
}

They then go on to say

"strdup passes that value on, leaving error-handling to its caller"

Why leave the error-handling to it's caller? Couldn't a person go like

char *strdup(char *s)
{
char *p;
if (p=(char *)malloc(strlen(s)+1) == NULL)
exit(1);
else
strcpy(p,s):
return p;
}


Chad
The only possible other solution would be to pass an error handling
function, something like this:

char *strdup(char *s,int (*errorHandler)(size_t request,void *arg),void
*arg)

And thereby allowing the programmer to write their own error handler
returning further instructions to the strdup function like fail or
retry. Allowing the function to be NULL and in that case display a
diagnostic message and terminate.
I do however *not* think that this would be a generally better solution.
 
M

Malcolm McLean

Richard Heathfield said:
Malcolm McLean said:



No, there isn't. Library routines have no business deciding to terminate
the program.
The problem is that the code is disproprtionate. Let's say I want to set up
a structure with a list of strings.

typedef struct
{
char **str;
int N;
} STRINGS;

STRINGS *strings(char *data)
{
STRINGS *answer;
char *ptr, *next;
int i;

answer = malloc(sizeof(STRINGS));
if(!answer)
goto error_exit;
answer->str = 0;
answer->N = strcount(data, ',');
if(!answer->N)
return answer;
answer->str = malloc(answer->N * sizeof(char *));
if(!answer->str)
goto error_exit;
for(i=0;i<answer->N;i++)
answer->str = 0;
ptr = data;
for(i=0;i<answer->N;i++)
{
next = strchr(ptr, ',');
if(!next)
next = ptr + strlen(ptr);
answer->str = malloc(len + 1);
if(!answer->str)
goto error_exit;
stncpy(answer->str, len, ptr);
answer->str[len] = 0;
ptr = next + 1;
}
return answer;
error_exit:
killstrings(answer);
return 0;
}

void killstrings(STRINGS *s)
{
int i;

if(s)
{
if(s->str)
{
for(i=0;i<s->N;i++)
free(s->str);
}
free(s->str);
free(s);
}
}

Now that is about as simple as an arbitrary dataset is likely to be - just
an array of strings. I counted 14 lines purely to handle malloc() failures.
The code is untested and there is probably something wrong with it. There
are all sorts of gotchas. For instance the 14 lines include code to
initialise all the pointers to zero so that the kill function doesn't crash
when it tries to free them, N has to be tested for zero because malloc()
might return 0, though in fact it is probably not sensible to call with a
empty set. Actually looking over it I can see an error in the way I count
the strings in the input, easy to do because the logic is lost in error
handling code that is unlikely ever to be called in real use.

To my mind this is a very serious disadvantage of the "caller handles error"
paradigm.
 
P

pete

Chad wrote:
Why leave the error-handling to it's caller? Couldn't a person go like

char *strdup(char *s)
if (p=(char *)malloc(strlen(s)+1) == NULL)
exit(1);

You might want strdup to be a function
that you can use in different situations,
where you have another option besides exit,
for when malloc returns a null pointer value.
 
E

Eric Sosman

Malcolm said:
Richard Heathfield said:
Malcolm McLean said:



No, there isn't. Library routines have no business deciding to terminate
the program.
The problem is that the code is disproprtionate. [...]
> [code snipped; see up-thread]

Now that is about as simple as an arbitrary dataset is likely to be -
just an array of strings. I counted 14 lines purely to handle malloc()
failures.

Line counts in code that won't even compile aren't all
that persuasive.

But even so: I counted 58 lines in all. If you expended
*no* lines on error-checking you'd still have 44. The Sixth
Commandment envisions a ratio of two lines of error-handling
per line of payload ("yea, even though the checks triple the
size of thy code"), so why are you complaining about a rate
less than one-twelfth as great? The Commandment describes an
extreme circumstance, true, but instructs the Righteous to be
prepared to work at least twelve times as hard as you do.
To my mind this is a very serious disadvantage of the "caller handles
error" paradigm.

The "serious disadvantage" seems to be that it makes lazy
programmers work harder than they'd like to.

If you require every "leaf" function to do its own error-
handling, you defeat reusability. Nice little function here
that does something my program needs -- oh, too bad, can't
use it because the actions it takes on error don't mesh with
my program's strategy. Contrast with a function that informs
its caller "Sorry; couldn't do it" and allows the caller to
decide what to do next in light of its greater understanding
of overall context -- it's the latter function that can be
reused, not the former.

Should fprintf() halt the program on an I/O error?

Should fopen() halt the program if unable to open the file?

Should strtod() halt the program on a malformed input?

Then why should malloc() halt the program on a whim?
 
M

Malcolm McLean

Eric Sosman said:
Malcolm said:
Richard Heathfield said:
Malcolm McLean said:

<snip>

There quite a strong case for a safemalloc() library function that
does terminate with an error message on fail.

No, there isn't. Library routines have no business deciding to terminate
the program.
The problem is that the code is disproprtionate. [...]
[code snipped; see up-thread]

Now that is about as simple as an arbitrary dataset is likely to be -
just an array of strings. I counted 14 lines purely to handle malloc()
failures.

Line counts in code that won't even compile aren't all
that persuasive.

But even so: I counted 58 lines in all. If you expended
*no* lines on error-checking you'd still have 44. The Sixth
Commandment envisions a ratio of two lines of error-handling
per line of payload ("yea, even though the checks triple the
size of thy code"), so why are you complaining about a rate
less than one-twelfth as great? The Commandment describes an
extreme circumstance, true, but instructs the Righteous to be
prepared to work at least twelve times as hard as you do.
To my mind this is a very serious disadvantage of the "caller handles
error" paradigm.

The "serious disadvantage" seems to be that it makes lazy
programmers work harder than they'd like to.

If you require every "leaf" function to do its own error-
handling, you defeat reusability. Nice little function here
that does something my program needs -- oh, too bad, can't
use it because the actions it takes on error don't mesh with
my program's strategy. Contrast with a function that informs
its caller "Sorry; couldn't do it" and allows the caller to
decide what to do next in light of its greater understanding
of overall context -- it's the latter function that can be
reused, not the former.

Should fprintf() halt the program on an I/O error?

Should fopen() halt the program if unable to open the file?

Should strtod() halt the program on a malformed input?

Then why should malloc() halt the program on a whim?
The Turing machine has run out of tape. That's a different kind of problem
to the others you have described.
 
F

Flash Gordon

Malcolm McLean wrote, On 17/06/07 22:40:
Eric Sosman said:
Malcolm said:
Malcolm McLean said:

<snip>

There quite a strong case for a safemalloc() library function that
does terminate with an error message on fail.

No, there isn't. Library routines have no business deciding to
terminate
the program.

The problem is that the code is disproprtionate. [...]
[code snipped; see up-thread]
Should fprintf() halt the program on an I/O error?

Should fopen() halt the program if unable to open the file?

Should strtod() halt the program on a malformed input?

Then why should malloc() halt the program on a whim?
The Turing machine has run out of tape. That's a different kind of
problem to the others you have described.

It is a recoverable error in many situations just as all the others are.
For example, when stressing my company notebook several times I've had
VMware tell me it was out of memory giving the option to retry. I closed
down some other stuff, told it to retry, and continued with my work. Far
better than your approach of having it die on me.
 
M

Malcolm McLean

Eric Sosman said:
But even so: I counted 58 lines in all. If you expended
*no* lines on error-checking you'd still have 44. The Sixth
Commandment envisions a ratio of two lines of error-handling
per line of payload ("yea, even though the checks triple the
size of thy code"), so why are you complaining about a rate
less than one-twelfth as great? The Commandment describes an
extreme circumstance, true, but instructs the Righteous to be
prepared to work at least twelve times as hard as you do.
Here's a class with exactly the same functionality written in Java. This
time I compiled it.

class Strings
{
String[] str;
public Strings(String data)
{
int i;
int start = 0;
int end = 0;
int N = 1;

for(i=0;i<data.length();i++)
if(data.charAt(i) == ',')
N++;
str = new String[N];
for(i=0;i<N;i++)
{
start = end;
end = data.indexOf(',', start+1);
if(end == -1)
end = data.length();
if(start != 0)
start = start + 1;
str = data.substring(start, end);
}
}
}

It is much shorter and easier to follow, purely because Java does your
memory management for you. The string functions are different but very
comparable. One big difference is that we no longer need a kill function,
but even excluding that, the removal of the need to check malloc() is a real
advantage.
The "serious disadvantage" seems to be that it makes lazy
programmers work harder than they'd like to.
Exactly. It would be nice to live in a world with no deadlines and no money
where efficiency didn't matter. Some countries are like that. But not
Britain.
 
W

Walter Roberson

Malcolm McLean said:
The Turing machine has run out of tape. That's a different kind of problem
to the others you have described.

Of course it's a different kind of problem: it would mean that you've
reached the end of infinity, probably having forgotten to save some
Scotch to find out whether infinite aging makes Scotch infinitely better.
 
E

Eric Sosman

Malcolm said:
[...]
Here's a class with exactly the same functionality written in Java.

Good-oh. Got a COBOL version, too?
This time I compiled it.

A wise precaution.
It is much shorter and easier to follow, purely because Java does your
memory management for you. [...]

Note, too, that Java uses exactly the pattern you despise:
It leaves the question of how to recover from an error up to
the invoker. If the JVM runs out of memory, it does *not*
just throw your program down the nearest oubliette. Instead,
it throws and OutOfMemoryError -- which the caller can catch,
if it wants, and then take restorative measures. Pray explain
just how this supports your position that error handling should
be the prerogative of the low-level mechanisms and not of the
agency that invokes them.

You have shot your own foot.
 
C

Chad

I certainly don't want to fly on any airplanes where you wrote the
flight control programs.

During my Mechanical Engineering days, ALL of my design work passed
through the Senior Engineer and Chief Scientist. I'm pretty sure that
if I wrote the flight control programs, my design work would go
through a similar process. And I'm pretty sure that if I made some
bonedheaded coding error, someone would catch it. And after they
caught it, I would probably hear "Chad, if your ever make that kind of
error again, we will have to find someone else that can do the job."

For the record, I did make one semi critical error on a design project
when working as a Mechanical Engineer. My manager at work told me "I
don't like having the Chief Scientist tell me there was a calculation
error. Chad, if you can't handle this type of work, I'm going to have
to find someone that can handle it."

I would also be very annoyed if attempting to paste a large document
into the current one caused the editor to exit without giving me a
chance to save the old one, even if the paste failed due to lack
of memory.

I remember back when I was connecting to the Universities computers
via dial up modem from my parents house. There was an unamed editor
that on the universities systems that exhibited this kind of behavior.
There is the problem that a low-level routine does not know how to
handle errors. If it needs to interact with the user, how should
it do it? Text out stderr? (might not be visible) An X window
popup? A Microsoft Window popup?

I don't do Micrsoft Windows.
 
R

Richard Heathfield

Malcolm McLean said:
The problem is that the code is disproprtionate.

If you try hard enough, the code /can/ be disproportionate.
Let's say I want to set up a structure with a list of strings.

I looked at your code, and found it lacking in any significant evidence
that you are a regular clc subscriber.

Now that is about as simple as an arbitrary dataset is likely to be -
just an array of strings. I counted 14 lines purely to handle malloc()
failures.

Get back to me when you've written it properly.
The code is untested and there is probably something wrong with it.

You'd be amazed. Still, look on the bright side - it didn't actually
call gets().
To my mind this is a very serious disadvantage of the "caller handles
error" paradigm.

To my mind it was just a bunch of broken code.
 
R

Richard Bos

Malcolm McLean said:
The Turing machine has run out of tape. That's a different kind of problem
to the others you have described.

IYAM running out of disk space is closer to a Turing machine running out
of space than running out of working memory, so please explain why that
argument does not go for fopen().

More importantly, though, explain why it's such a good idea to destroy
half an hour's typing work just because you do not have the memory to
paste a big graphic into the letter.

Richard
 

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,015
Latest member
AmbrosePal

Latest Threads

Top