[comp.std.c] May prototypes be required for lib functs

jon@hitachi.uucp (Jon Ryshpan) (09/07/90)

May an ANSI C compiler maker require that users of a compiler and its
libraries put prototypes for the lib functs in functions which call
these lib functs?

For example it might be desirable for lib functs to pop their own
arguments (callee pops convention).  This is clearly not possible
unless the callee knows the number and types of its arguments.

On the other hand this requirement will break any code which doesn't
include the appropriate header files, eg. string.h for strcpy.  Most
K&R C code doesn't include these headers.

Is such a requirement is *permitted* under ANSI C, and is it a good
idea?

Many Thanks:	

Jonathan Ryshpan		<...!uunet!hitachi!jon>

M/S 420				(415) 244-7369  	
Hitachi America Ltd.
2000 Sierra Pt. Pkwy.
Brisbane CA 94005-1819

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/07/90)

In article <478@hitachi.uucp> jon@hitachi.uucp (Jon Ryshpan) writes:
>May an ANSI C compiler maker require that users of a compiler and its
>libraries put prototypes for the lib functs in functions which call
>these lib functs?

I'm not quite sure what this is supposed to mean, but:

4.1.6 explicitly allows strictly conforming C programs to not include
a standard header if they can properly declare a function without it.

Furthermore, any function need not be declared using prototype ("new-
style") syntax if its parameters have already-default-widened types
(as I think all the standard library functions do).  And any function
need not be declared at all if it returns type int and its parameters
have already-default-widened types.

>For example it might be desirable for lib functs to pop their own
>arguments (callee pops convention).  This is clearly not possible
>unless the callee knows the number and types of its arguments.

I don't see the relevance of this.  C functions are not in general
polymorphic, and whether the caller or callee cleans up the stack
does not matter (not for this discussion, anyway; it could matter
to the implementor when longjmp, asynchronous signals or interrupts,
and struct passing are taken into account).

However, for variadic functions such as printf(), there must be a
new-style declaration in scope for the function call; the declaration
can be provided by a standard header or directly by the program.  The
reason for this is that otherwise the compiler would be unable to
distinguish between invocation of a function with a fixed number of
(default-widened) arguments and a function capable of taking a
variable number of arguments, since the invocations for the two cases
appear syntactically identical.  Only the declaration shows which
is intended, e.g.
	extern int printf(const char *, ...);	/* variable arguments */
	extern int pr_int(const char *, int);	/* always 2 arguments */
Because the compiler may have to generate different linkage for these
two cases (or at least, may prefer to avoid the additional variadic
linkage overhead except when it is actually needed), the standard
requires that prototypes be in scope when variadic functions are
invoked, so that the compiler knows what linkage it needs to generate.

>Is such a requirement is *permitted* under ANSI C, and is it a good
>idea?

Any implementation that requires inclusion of a standard header to
declare a standard function, for example strcpy(), is not standard
conforming.

It is permitted for some of the standard functions to be implemented
in the standard headers as macros, but there must still be actual
function declarations in the headers, as well as function definitions
present in the standard library, specifically to accommodate programs
that directly declare the functions or otherwise deliberately take
pains to avoid using the macro equivalents.  When an implementation
chooses to provide a macro implementation of a standard function, the
associated standard header is required to BOTH declare the function
AND THEN provide any macro equivalent that it wants programs to use
by default.  E.g.

	/* example conforming implementation's <string.h> extract: */
	extern char *strcpy(char *, const char *);
	#define strcpy(s1, s2)	__intrinisic__strcpy(s1, s2)
				/* compiler treats above specially */

Any of the following program extracts are strictly conforming:

	#include <string.h>
	...
	strcpy(a, b);	/* uses a macro if one was provided in
			   <string.h>, otherwise invokes a function */

or

	#include <string.h>
	#undef strcpy	/* in case it's defined in <string.h> */
	...
	strcpy(a, b);	/* accesses the library function */

or

	#include <string.h>
	...
	(strcpy)(a, b);	/* accesses the library function */

or

	/* no <string.h> included anywhere */
	extern char *strcpy();	/* compatible old-style declaration */
	...
	strcpy(a, b);	/* accesses the library function */

dbrooks@osf.org (David Brooks) (09/07/90)

(this followup adds comp.lang.c to comp.std.c)
In article <13759@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn) writes:

|> Furthermore, any function need not be declared using prototype ("new-
|> style") syntax if its parameters have already-default-widened types
|> (as I think all the standard library functions do).  And any function
|> need not be declared at all if it returns type int and its parameters
|> have already-default-widened types.

This loses some of the benefits of prototypes.  But if your parameters
don't have already-widened types, you must beware of the following
situations:

Define your callee with a narrow parameter or two, and accidentally
leave out the prototype from the caller.  Your program breaks.  We all
know this, right?  Not necessarily.

Build a library using prototypes and narrow parameters, and put the
binary out on the streets.  Your customers had better use prototypes
in their application code, which means you have to insist they build
with a prototyping compiler.  Reasonable.

It gets worse.  There are some overly-helpful compilers out there
which do the widening anyway, prototype or no.  This is quite
consistent with the ANSI rules; it works so long as the caller and
callee agree about it.  So you do all your program development with
that compiler, test it up the wazoo, and put the source out on the
streets.  But you got the prototypes mixed up - either you misspelt
the function name in a header file or you left a #include out of a
source.  A paying customer points a less-helpful compiler at it (or
the same compiler, built differently), and it breaks in obscure ways.

Of course, such a compiler would produce at least a comment when it
encounters a default-declared procedure.  But prudence is beginning to
suggest that I balance the advantages of decent parameter type
checking against handling non-reproducible-in-house parameter-passing
bugs.

(people who meet me in other newsgroups know what I'm talking about).
-- 
David Brooks				dbrooks@osf.org
Systems Engineering, OSF		uunet!osf.org!dbrooks
Experience Hackvergnuegen!

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/08/90)

In article <1990Sep7.114544@osf.org> dbrooks@osf.org (David Brooks) writes:
>This loses some of the benefits of prototypes.

Well, yes, but there was that mountain of already-written code that was
correct by previous (K&R) criteria that we wanted to "grandfather in".

>Build a library using prototypes and narrow parameters, ...

I don't recommend excessive use of narrow parameter types, but certainly
if you're going to use them (or even if not!) there should be an associated
header that properly declares them.  A conforming compiler will flag any
type error in the function implementation if the implementation includes
the header.  Otherwise, some form of "lint" should be used by the developer.