Functions returning Error codes or actual info

Vasile R. Montan vrm at babcock.cerc.wvu.wvnet.edu
Wed Sep 12 05:41:37 AEST 1990


   As promised, here are the email responses I received on my question
on whether functions should return actual data or error codes.  It
seems the general consensus is to return the error code as the value
of the function and return the actual value in an out-mode parameter.

   Thanks to everyone who responded.

-- Vasile
======================================================================
>From robert at cs.arizona.edu:
Case 1, function value indicates success/failure, usually works well
with the enclosing control structure.  You won't have to store something
into a separate variable and gives a good "functional" approach to your
solution:

    while (get_x(&info))
      deal_with(info);

or

    switch (get_x(&info)) {
      case E1 :
      case E2 :
      case E3 :
    }

======================================================================
>From dinl!noren at ncar.ucar.edu:
Your raised a real good question, and I'd like to see the responses
you get.  I waiver all over the place because I don't have a
satisfactory solution myself.

What I use most often is have the function return the value I want,
using a clearly illeagal to flag an error (e.g., return a NULL on
a function that returns a string).  Prior to the return with the
error-value I set the global errno variable to some meaningful value
(or have my own extensions to it with a wrapper around perror(3) to
display my extension text).  In this way I am using (what I interpret,
there is such a thing) the UNIX (or is it the C library) philosophy of 
function error reporting and even have the library functions do much
of the work in setting errno for me.  For instance, if my socket
handler fails, its usually because a library call to the O.S. or
C library failed, and that function returns an errno indicating why,
which I leave untouched as I return out of my function with an error
value.

This approach doesn't work all the time, but oh well.

Thanks for posing the question.  I hope there is a lot of interesting
discussion.

======================================================================
>From ari at eleazar.dartmouth.edu:
In article <772 at babcock.cerc.wvu.wvnet.edu> you write:
>
>   I am making a set of functions which return different types of
>values: strings, integers, doubles, etc.  For example:
>          char *get_string();
>However, I would also like the function to return an error code if the
>function fails.  I cannot just return a NULL pointer because I want
>the function to be the same as all of the other get_xxx's. I have seen
>this done several different ways in C and am wondering if there is an
>accepted "proper" way of doing it.

I find the easiest to code, most consistent, and most robust approach
is to return the error code as the function's result. As in,
	Error foo(params..., result *output)
I then use statements like:
	err = foo(param, &output);
	if (err)
		return(err); /* propagate error until someone can handle it */
For a while, I tried jumping to a label when an error was detected,
especially if there was a lot of cleaning up to do. For instance,
	char *newptr = NULL;
	char *otherptr = NULL;

	newptr = (char *) malloc(100);
	if (! newptr) {
		err = ERR_MEMORY;
		goto error;
	}
	otherptr = (char *) malloc(200);
	if (! otherptr) {
		err = ERR_MEMORY;
		goto error;
	}
	err = do_something();
	if (err)
		goto error;
	return(0); /* zero is no error */
    error:
	if (newptr) free(newptr);
	if (otherptr) free(otherptr);
	return(err);
However, the above scheme led to confusing code (of course
it did, it has goto's!). I now use a combination of 
return statements and nested if's. For instance, the above
chunk of code would be written:
	char *newptr, *otherptr;
	
	newptr = malloc(100);
	if (newptr) {
		otherptr = malloc(200);
		if (otherptr) {
			err = do_something();
			if (! err)
				return(0);
			/* we're now falling through to
			   all of the cleanup code */
			free(otherptr);
		}
		free(newptr);
	}
	if (! err) err = ERR_MEMORY;
	return(err);

Or something like that. Anyway, making everything nest and then
falling through to cleanup code seems to be reliable. Of course,
this isn't really about error codes, but I thought it would
be interesting.	-- 
======================================================================
>From gary at avo.hp.com Tue Sep 11 02:45:35 1990
> Most often in C, I see the error code being returned so it can be used
> inside a control statement.  This forces the actual information to be
> returned in an "out" mode parameter:

> CASE 1:	if (error_code = get_string(parm, &info)) {...}

In the project I've just started, we have agreed on this standard for
consistency.  There is one exception: if you have a function that would
work nicely as a predicate in an "if" statement AND if the error  case
can be handled reasonably in the return parameter, then you can return 
the predicate value.  An example might help.  Let's say you maintain
some list of objects.  And you want a function IsEmpty(list).  Clearly
you want  to be able to write:
    if (IsEmpty(List1))...
Further, the only likely error case is List1 being a NULL instead
of your list-head; and clearly in this case you can return TRUE.
Another example: You want NumInList(list).  Now, you can use a
negative return value to indicate an error.

> I have also seen functions which set a global variable to indicate
> that an error has occurred:

> CASE 2:	info = get_string(parm);
> 	if (error_code) {...}

I hate this.  Using a global like this has lots of problems.
If you make another call, you could blot out a useful error code.  
(This is always a problem with errno in UNIX -- you have to save it before
any other I/O clears it.)

> CASE 3:	info = get_string (parm, &error_code);
> 	if (error_code) {...}

I think this is pretty common.  You write your functions as if there
were no error handling then you append a final parameter for error
control.  There is nothing wrong with this method.

>    BTW, I have some other functions which do not return any
> information, so they always return an error code.  Does this mean I
> should use CASE 1 just to keep them consistent?
It certainly is one of the advantages that CASE 1 has.

======================================================================
>From taumet!taumet!steve at uunet.UU.NET Tue Sep 11 11:51:31 1990

My suggestion is to decide on the abstraction you want for the functions,
then write them that way.

I would NEVER use a global variable for passing error information.
This technique (errno) is used in the standard C library.  The ANSI
committee recognized this was a horrible idea, but it was existing
practice, and could not reasonably be changed.  One problem comes
when a function which might set the variable calls another which
also might set the variable -- the interactions get very hard to
manage.  Another problem comes in multi-threaded programs or
programs with interrupts.  You might see the variable set and assume
it was your error, when it was an error from some other task.

Using an "out" variable for data and returning a status code has some
advantages, it seems to me, since you can then write
	if( get_data(&info) ) {
		... ok ...
	}
	else {
		... fail ...
	}
or
	switch( get_data(&info) ) {
	    case OK:
		...
	    case EOF:
		...
	    case ERROR:
		...
	    etc ...
	}



More information about the Comp.lang.c mailing list