Subject: Re: ignoring return values from functions.
To: NetBSD Userlevel Technical Discussion List <tech-userlevel@netbsd.org>
From: Greg A. Woods <woods@weird.com>
List: tech-userlevel
Date: 09/20/2001 12:54:23
[ On Thursday, September 20, 2001 at 14:39:01 (+0700), Robert Elz wrote: ]
> Subject: Re: ignoring return values from functions. 
>
>     Date:        Wed, 19 Sep 2001 18:13:23 -0400 (EDT)
>     From:        woods@weird.com (Greg A. Woods)
>     Message-ID:  <20010919221323.5E5CDEA@proven.weird.com>
> 
>   | It is ALWAYS best to explicitly cast function return values to (void) if
>   | they are expliicltly to be ignored.
> 
> If that were true, then I expect that you also write
> 
> 	(void)(i++);

Ah, no, I meant "_only_ function return values" -- "i++" isn't a
function call.

More pedantically maybe I should have used the phrase "only function
error return values" or something like that....

What's important in this discusion is being pedantic about error
handling.  Obviously one doesn't have to be pedantic about checking the
value of all expressions since almost none of them give any indication
of error in their operation.

> Throwing extra (void) around doesn't help readability or maintainability
> in any way at all - just the opposite, it hides the code that actually does
> something useful in the middle of lots of syntax noise.

For everything but the *printf() family and similar where error handling
can be easily done with ferror() or fclose(), I beg to differ; and as I
just mentioned to Greywolf I could dig out stacks of references beyond
Henry's ten commandments to back that up with well researched analysis
of many real-world programming errors and program faults.  (I'll mention
just one of the better of them at the very end of this message.)

However when I'm writing code that's supposed to be very portable I will
pedantically cast all purposefully ignored function returns to 'void'.
I don't find that code any harder to read, but I will admit it might
take a wee bit more training to get used to it if you haven't grown up
reading code written that way in the first place.

(Actually it's a bit more complex than that -- I tend to assume most
people will know that the return value of printf()s for operator
messages are uselss, i.e. many *printf()s to stdout or stderr, however I
try to be pedantic about calls that write data (as opposed to operator
messages) to files.  Indeed I try to explicitly fclose(stdout) before
exiting in any filter program that generally writes only to stdout, and
of course to check the error return from that close.)

> No-one can possibly be accidentally misled into thinking that
> 
> 	printf(fmt, arg, otherarg);

I didn't mention *printf() style functions again because I thought that
my view on that matter had already been made clear in my initial post to
this thread....

> If a function can usefully return something that might occasionally
> be useful, and that is cheap, then it should do so - like strcpy() etc
> do, perhaps 90% of users of strcpy() don't want the value, but having it
> there is useful sometimes, so it certainly should remain.

Yes, most of the str*() functions are in the same class as *printf() in
this respect.

More specifically the str*() functions are members of a class of
functions which return values that have no implication of their success
or failure, while printf() returns an error indication that can be
obtained from other more appropriate sources.

> If lint could detect that a function has no side effects, and that its
> value was being ignored - that would be a useful warning to print, but
> warning about convenience return values being ignored is simply broken.

No, sorry, it's not.  Yes, stdio is broken by design for offering
functions whose return values are never necessary, but that's not
something that can be fixed any more.  Yes, C has problems
distinguishing between functions that return error values and functions
that return other information (that may not always be of use), but
that's what casting to 'void' is for.  It's a work-around, but it works.

The some versions of 'lint' have supported a hint to disable the warning
for ignored return values, but unfortunately not all do.  The version of
'lint' included in NetBSD has an even more useful feature too:

	Function calls are checked for inconsistencies, such as calls to
     functions that return values in some places and not in others

Unfortunately it still basically treats many functions as returning
'void' whenever their return values are ignored, no matter what they're
really declared as.

Everyone who's experience I trust has demanded that all function returns
be cast to 'void' if their value is explicitly to be ignored in the
context of the call.  For example Ian Darwin's excellent little O'Reilly
book "Checking C Programs with lint" (which unfortunately may not be in
print any more!) says exactly the same thing (of course Ian and Henry
know each other well so this may not come as a surprise).

>   | K&R's style isn't entirely best for production use -- it is purposefully
>   | simplified to make learning the language easier.
> 
> No it isn't, it is exactly the style that K&R actually used (especially
> that in the first edition) for real production code.  It works just fine.
> Your quote from K&P in the later message has no relevance to anything at
> all that I could see.

Well though Research Unix was indeed used "in production", many many
many lessons have been learned since about how programmers do their job,
particularly with the C language, and how they make mistakes, etc.  What
was appropriate 25-30 years ago is no longer appropriate for today's
production environments.

Having read lots of original Unix code I don't find it very hard at all
to spot questionable constructs that could easily cause maintenance
induced errors.  Indeed even reading release notes for various versions
of Unix reveal new bugs and later fixes that were clearly caused by the
use of known questionable programming constructs.  There were literally
hundreds and hundreds of bugs in V7 kernel and userland that were caused
by not checking for error returns where necessary.  There are
undoubtably still some such bugs in NetBSD (even though they supposedly
share no common code base! :-).  "Lazy" (i.e. non-pedantic) programmers
might get working results quicker, but they don't always get better
results in all scenarios.

Indeed in a slightly related project, the AT&T 5ESS switch, a study in
1993 of the several million lines of code therein listed the failure to
"use function return values or cast to 'void'" as one of the top 11
major interface faults causing real bugs in the code.  That's from "A
Software Fault Prevention Approach in Coding and Root Cause Analysis" by
Weider D. Yu, published in Bell Labs Technical Journal, April-June 1998.

-- 
							Greg A. Woods

+1 416 218-0098      VE3TCP      <gwoods@acm.org>     <woods@robohack.ca>
Planix, Inc. <woods@planix.com>;   Secrets of the Weird <woods@weird.com>