[comp.lang.c] Function prototypes

jas@rtech.UUCP (04/30/87)

Henry Spencer (utzoo!henry) notes in passing:

> [Function prototypes] have other problems, and I'm not sure they were a
> good idea....

This got me curious:  what are the problems with function prototypes?
The only one I can think of is that an implicit coercion will not be
done when there is no function prototype in scope.  I would resolve
that by requiring compilers to emit a fairly strongly worded warning
whenever a program called a function without a prototype in scope.  (If
we were designing C from scratch at this point, I'd outlaw it
altogether; but we're not.)  I see nothing wrong with implicit coercion
per se, as long as the coercion is well defined.  Implicit coercion of
one pointer type to another should minimally elicit a warning, like
pcc's when doing this implicit coercion on assignment.

Are there additional arguments against function prototypes?  If so,
what are they?
-- 
Jim Shankland
 ..!ihnp4!cpsc6a!\
                  rtech!jas
..!ucbvax!mtxinu!/

olson@endor.harvard.edu (Eric Olson) (05/01/87)

In article <796@rtech.UUCP> jas@rtech.UUCP (Jim Shankland) writes:
>
>Henry Spencer (utzoo!henry) notes in passing:
>
>> [Function prototypes] have other problems, and I'm not sure they were a
>> good idea....
>
>This got me curious:  what are the problems with function prototypes?
...
>Are there additional arguments against function prototypes?  If so,
>what are they?

I have a big complaint about function prototypes.  I would very much like
all the functions in my code to have prototypes accessible from any other
module, essentially so I get lint-like argument checking during compile.
(I am using LightSpeed C on the Macintosh-- don't yell at me, but it has no
lint.  It does have an option to require prototypes).  So I can prototype
everything in a header file included in all my modules, but then adding a
module forces a complete re-make.

Am I missing something?  Is there a sane way to use prototypes (I already
use them on libraries, since they rarely change).

Any ideas will be appreciated.

-Eric

olson@endor.harvard.edu (Eric Olson) (05/01/87)

In article <1821@husc6.UUCP> olson@endor.UUCP (Eric Olson) writes:
>So I can prototype
>everything in a header file included in all my modules, but then adding a
>module forces a complete re-make.

I meant to say that adding a function to a module forces a complete re-make
of all modules.  Sorry.

-Eric

socha@drivax.UUCP (Henri J. Socha (x6251)) (05/02/87)

In article <796@rtech.UUCP> jas@rtech.UUCP (Jim Shankland) writes:
>Henry Spencer (utzoo!henry) notes in passing:

>> [Function prototypes] have other problems, and I'm not sure they were a
>> good idea....

>This got me curious:  what are the problems with function prototypes?
>The only one I can think of is that an implicit coercion will not be
>done when there is no function prototype in scope.  I would resolve
>that by requiring compilers to emit a fairly strongly worded warning
>whenever a program called a function without a prototype in scope.  

>Are there additional arguments against function prototypes?  If so,
>what are they?

 Well, just to back up (give an example) of what you said:

The Macintosh Lightspeed C (development environment!) from Think Technologies
has an option called  "Function Prototypes Required".  It can be
set as a default that is always enforced (unless explicitly disabled).

They also have a non-standardism (which I feel is a bug) in that
when a function is defined, the prototype form CAN NOT be used.

Therefore if this option is enabled, you must say:

	int main(int argc, char *argv[]);

	int main(argc, argv)	/* can't use prototype form here! (bug) */
	int argc;
	char * argv[];
	{
	.....
	}

Or you will get a compile error that the prototype for main was not given.

YES, (before you flame me) a prototype declaration at function definition
should be allowed but (and finally this is the point):

I WOULD RATHER BE FORCED TO DO UNNECESSARY DECLARATIONS THAN 
NOT KNOW IF ALL FUNCTIONS AND CALLS HAVE BEEN CORRECTLY CODED.

I have been burned more than once by the missing declaration of an external
LONG returning function or a mis-typed call argument.

How about it compiler writers?  Will you also install a switch
that DEMANDS prototype definitions for all functions so that
at least a minimum of type checking can be performed?

Disclaimer:  This is not (necessarily) the view of my employer.
--
-- 
UUCP:...!amdahl!drivax!socha                                      WAT Iron'75
"Everything should be made as simple as possible but not simpler."  A. Einstein

hjg@bunker.UUCP (Harry J. Gross) (05/04/87)

In article <1821@husc6.UUCP> olson@endor.UUCP (Eric Olson) writes:
>I have a big complaint about function prototypes.  I would very much like
>all the functions in my code to have prototypes accessible from any other
>module, essentially so I get lint-like argument checking during compile.
>(I am using LightSpeed C on the Macintosh-- don't yell at me, but it has no
>lint.  It does have an option to require prototypes).  So I can prototype
>everything in a header file included in all my modules, but then adding a
>module forces a complete re-make.
>
>Am I missing something?  Is there a sane way to use prototypes (I already
>use them on libraries, since they rarely change).
>
>Any ideas will be appreciated.

	It's kind of kludgey (sp?), but if you _know_ that the only change to
the header was the addition of a prototype that is currently unused in any other
module, you could *touch* each of the other modules (.c and .obj).  Then make
should leave them alone.  I have sometimes been forced to do this when I have
added a (not currently used, but will be soon) #define to a header file that
would otherwise cause 30 modules to recompile.

	It's a terrible hack, I know, but it beats waiting xx minutes for a
completely unnecessary recompile.  (Of course, if you have no easy way of
updating the modification time on the file, all bets are off - Oh well :-).

	If there is a better way, I don't know what it is, BUT I SURE WOULD
LIKE TO!!!

				Harry Gross
				..!bunker!hjg

tps@sdchem.UUCP (Tom Stockfisch) (05/05/87)

In article <2023@bunker.UUCP> hjg@bunker.UUCP (Harry J. Gross) writes:

>In article <1821@husc6.UUCP> olson@endor.UUCP (Eric Olson) writes:

>>....  So I can prototype
>>everything in a header file included in all my modules, but then adding a
>>module forces a complete re-make.

>	It's kind of kludgey (sp?), but if you _know_ that the only change to
>the header was the addition of a prototype that is currently unused in any other
>module, you could *touch* each of the other modules (.c and .obj).  Then make
>should leave them alone.
>	If there is a better way, I don't know what it is, BUT I SURE WOULD
>LIKE TO!!!


The way I handle it is to have my declaration/prototype header file contain
*only* declarations/prototypes, and then I DON'T MENTION IT IN THE MAKEFILE.
I can't think of a single case when this has caused a typing problem.  The
way you mess up in this case is to change a function's type in its module
and forget to change it in the header file.  Mentioning the header file in
the "Makefile" doesn't catch this error.

Header files with defines and macros are a different story and probably
should be listed in dependency rules.

I think it is a very bad mistake ever to "touch" source files.  You are
perverting information which you will probably need in the future even if
you can't think why now.

|| Tom Stockfisch, UCSD Chemistry	tps%chem@sdcsvax.ucsd.edu
					or  sdcsvax!sdchem!tps

steele@unc.UUCP (05/05/87)

Eric Olson (olson@endor.UUCP) writes:
[about putting all the prototypes in a single header file]
}....  So I can prototype
}everything in a header file included in all my modules, but then adding a
}module forces a complete re-make.


Harry J. Gross (hjg@bunker.UUCP) writes:
)
)	It's kind of kludgey (sp?), but if you _know_ that the only change to
)the header was the addition of a prototype that is currently unused in any
)other module, you could *touch* each of the other modules (.c and .obj).
)Then make should leave them alone.
)	If there is a better way, I don't know what it is, BUT I SURE WOULD
)LIKE TO!!!


Tom Stockfisch tps@sdchemf.UUCP writes:
>
>The way I handle it is to have my declaration/prototype header file contain
>*only* declarations/prototypes, and then I DON'T MENTION IT IN THE MAKEFILE.
>I can't think of a single case when this has caused a typing problem.

The way I handle it, when I'm being modern and verbose, is to treat each
set of functions/globals that implements and abstract type or object Foo as
a module 'Foo' contained in two files, 'foo.c' and 'foo.h', which roughly
parallel the public and private parts of modules in Modula-2 and Ada (I
speak through my hat, not knowing either).  Any module that uses a foo then
#includes "foo.h".

Your price is more files, and more #included files, and maybe more compilation
time to #include them all (This overhead will probable be negligible on a
real UN*X machine.  On a Mac it's expensive to open files, but a 96-128K
cache speeds up recompilation enough that I don't worry about it under
Lightspeed).

Your payoffs are
1)  An automatic dependency diagram (either a short shellscript to collate
    #include-d files or option-click in the title bar to get a list of modules
    that a given file uses).
2)  You only force recompilation of a file iff the interface to a module it
    uses changes.  Their is no easy way to do this automatically if the
    granularity of header files is any larger than that of code files.

>The way you mess up in this case is to change a function's type in its
>module and forget to change it in the header file.  Mentioning the header file
>in the "Makefile" doesn't catch this error.

Including a module's header in its code file solves that.  The only problems
I've run into are in the management of modules large enough to be composes
of submodules.

------------------------------------------------------------------------------
Oliver Steele				  ...!{decfax,ihnp4}!mcnc!unc!steele
		      				steele%unc@csnet-relay.csnet

"Education and religion are two things not regulated by supply and demand.
The less of either the people have, the less they want."
						- Charlotte Observer, 1897

grodberg@kodak.UUCP (05/06/87)

In article <731@sdchema.sdchem.UUCP> tps@sdchemf.UUCP (Tom Stockfisch) writes:
>The way I handle (keeping function prototypes up to date without having make 
do complete recompiles) is to have my declaration/prototype header file contain
>*only* declarations/prototypes, and then I DON'T MENTION IT IN THE MAKEFILE.
>I can't think of a single case when this has caused a typing problem.  The
>way you mess up in this case is to change a function's type in its module
>and forget to change it in the header file.  Mentioning the header file in
>the "Makefile" doesn't catch this error.

    There is an other way you mess up using Tom's method, which is a serious
drawback: if you change a function's type and don't update *ALL* of the
files that have calls to that function, you may never here the compiler
complaining that you are sending in the wrong type, since those modules won't
be recompiled.  
    For example, if you have function foo that calls function bar, and
change bar's type from float to double, you won't hear about the fact that
you forgot to change foo's function call until you have some other reason 
to recompile foo.
-- 
          Jeremy Grodberg

Usenet: ...rochester!kodak!grodberg or kodak!grodberg@cs.rochester.edu
Arpa: 	grodberg@kodak or kodak!grodberg@rochester

hjg@bunker.UUCP (Harry J. Gross) (05/06/87)

In article <731@sdchema.sdchem.UUCP> tps@sdchemf.UUCP (Tom Stockfisch) writes:
>In article <2023@bunker.UUCP> hjg@bunker.UUCP (Harry J. Gross) writes:
>>	It's kind of kludgey (sp?), but [...]
>>[...], you could *touch* each of the other modules 
		[much deleted]
>I think it is a very bad mistake ever to "touch" source files.  You are
>perverting information which you will probably need in the future even if
>you can't think why now.

	Actually, I agree with you completely.  (After all, I did say that it
was a kludge :-)

	I think your solution is excellent.  I don't generally like to leave
header files out of Make files, but in this case, it is an excellent solution.

	Glad I thought of it :-) :-) :-)

-- 
..!bunker\			|	This space reserved for a
..!phri\   \			|	particularly funny quotation
 ..!nyit!gor!hjg (Harry Gross)	|
..!helm/			|	All donations cheerfully examined

stuart@bms-at.UUCP (Stuart D. Gathman) (05/06/87)

In article <1821@husc6.UUCP>, olson@endor.harvard.edu (Eric Olson) writes:

> everything in a header file included in all my modules, but then adding a
> module forces a complete re-make.

I assume this is because all your modules depend on the header file.
In that case, do 'make -t' after adding a module to the header file.
-- 
Stuart D. Gathman	<..!seismo!dgis!bms-at!stuartSI

tps@sdchem.UUCP (05/07/87)

In article <839@kodak.UUCP> grodberg@kodak.UUCP (Jeremy Grodberg) writes:

>In article <731@sdchema.sdchem.UUCP> tps@sdchemf.UUCP (Tom Stockfisch) writes:

>>The way I handle (keeping function prototypes up to date without having make 
>do complete recompiles) is to have my declaration/prototype header file contain
>>*only* declarations/prototypes, and then I DON'T MENTION IT IN THE MAKEFILE.

>    There is an other way you mess up using Tom's method, which is a serious
>drawback.... For example, if you have function foo that calls function bar, and
>change bar's type from float to double, you won't hear about the fact that
>you forgot to change foo's function call until you have some other reason 
>to recompile foo.

It is very unlikely that you would not change foo() if you changed bar().  For
instance, if you change bar() from float to double, you will probably change
foo to double as well.  If you don't change anything in foo.c, then you will
have to remember to 'rm foo.o' before the re-make.
When I make a bunch of type changes (which occurs very infrequently) I
sometimes 'rm *.o' just to be safe.  

I don't agree that my method has a "serious drawback" when I have used it for
several years in working with makefiles, I rarely waste time doing recompiling
all source files, and I have to spend very little time thinking about when an
object file will become truly out of date.




|| Tom Stockfisch, UCSD Chemistry	tps%chem@sdcsvax.ucsd.edu
					or  sdcsvax!sdchem!tps

eager@amd.UUCP (mike eager) (05/07/87)

Summary:Too many cross-module connections


In article <1822@husc6.UUCP> olson@endor.UUCP (Eric Olson) writes:
>>So I can prototype
>>everything in a header file included in all my modules, but then adding a
>>module forces a complete re-make.

Glen Myers' book __Reliable_Software_through_Composite_Design__ gives a 
list of the features of good programs.  He describes several forms of 
coupling between programs.  By having a reference to every function 
included in every file, you have coupled all of the files together in
a low quality fashion.  The result is the re-make.

The solution is to reference only the functions which are actually used
in each file.  This will keep unrelated things from interacting.  You may
need more include files.  You may find that the programs document themselves
a wee bit better, since they now mention only things which??thes't be

henry@utzoo.UUCP (Henry Spencer) (05/11/87)

> > [Function prototypes] have other problems, and I'm not sure they were a
> > good idea....
> 
> This got me curious:  what are the problems with function prototypes?

The three that immediately come to mind are:

1. Considerable disaster potential when moving code between environments
which differ in how they do prototyping.  Remember that many (most?)
prototypes will be picked up with header files, not explicitly typed by
the programmer.  What happens when that code gets moved to a system with
a non-prototyping compiler?  (Such compilers won't go away overnight.)
Less drastic and still more subtle, what happens when the code gets moved
to a system which doesn't declare quite the same set of functions in the
prototypes in its headers?  (Don't tell me this shouldn't happen, it will.)

2. Some of the coercions provided by function prototypes are distinctly
unsafe, e.g. the shortening ones.  Again, prototypes from header files
are a nice way to have this happen without the programmer noticing it --
and it's not clear that even lint will complain.

3. They are a significant change to C, implementation experience with them
is distinctly limited, and they aren't vitally needed.  There is room for
debate about whether X3J11 violated the "standardize the existing language,
not a new one" rule, and function prototypes are the most conspicuous
evidence for violation.
-- 
"The average nutritional value    Henry Spencer @ U of Toronto Zoology
of promises is roughly zero."     {allegra,ihnp4,decvax,pyramid}!utzoo!henry

brett@wjvax.UUCP (Brett Galloway) (05/11/87)

In article <2023@bunker.UUCP> hjg@bunker.UUCP (Harry J. Gross) writes:
>	It's kind of kludgey (sp?), but if you _know_ that the only change to
>the header was the addition of a prototype that is currently unused in any other
>module, you could *touch* each of the other modules (.c and .obj).  Then make
>should leave them alone.  I have sometimes been forced to do this when I have
>added a (not currently used, but will be soon) #define to a header file that
>would otherwise cause 30 modules to recompile.

A drawback of this method is that you can inadvertently touch something that
should have been updated because of some *other* change (an error that I
have made).  I wrote a short program called 'twiddle` to store the modify
date of the argument, exec /usr/ucb/vi on it, then restore the modify date
after vi exits.  Thus, when I want to make an inocuous change to a header
file, I just 'twiddle` it.  I find that this saves much time *and* much grief.

-- 
-------------
Brett Galloway
{pesnta,twg,ios,qubix,turtlevax,tymix,vecpyr,certes,isi}!wjvax!brett

thomas%spline.uucp@utah-gr.UUCP (Spencer W. Thomas) (05/13/87)

Henry Spencer claims that use of function prototypes is dangerous
because of what may happen if a function prototype is missing in one
environment, but present in another.  Lightspeed C for the Macintosh
has an option that causes it to require function prototypes for all
external functions.  Seems to me that use of this option would prevent
the type of disaster envisioned by Henry.

=Spencer   ({ihnp4,decvax}!utah-cs!thomas, thomas@cs.utah.edu)

henry@utzoo.UUCP (Henry Spencer) (05/20/87)

> Henry Spencer claims that use of function prototypes is dangerous
> because of what may happen if a function prototype is missing in one
> environment, but present in another.  Lightspeed C for the Macintosh
> has an option that causes it to require function prototypes for all
> external functions.  Seems to me that use of this option would prevent
> the type of disaster envisioned by Henry.

Unfortunately, it's not as helpful as one would like:  the dangerous
problems are the ones where the prototype for xxx is being picked up from
a header file, and you assume that the header file is the same in both
environments, and it's not.  If *both* environments have such a compiler
option, fine; the point is that successful compilation with such an option
in one environment tells you nothing about what's going to happen in another.
-- 
"The average nutritional value    Henry Spencer @ U of Toronto Zoology
of promises is roughly zero."     {allegra,ihnp4,decvax,pyramid}!utzoo!henry

edw@ius2.cs.cmu.edu (Eddie Wyatt) (05/20/87)

  It seems to me that function prototypes violate a basic good
rule about high programming language design which is : avoid having the
user declare the same information more than once.  As I understand,
function declarations are still require in all there entirety. The
same information is presented in function prototypes.   The user
is require to state approximately the same thing twice.  The functionality
of function prototypes is still useful though, to push the information
about a function around to other modules.  

  There is an ugly(?) alternative that will provide the same functionality.
It will remove the burden from the user to the compiler.  A optional approach
is have the compiler generate the function prototype information ahead of
time.  Maybe another preprocessor stage to the compiler.  *.fp files
could automatically be create with all the function prototype information
in them.  The user would have a small task of including the appropriate *.fp
files (not necessarily using #include). If stronger scoping requirements
are needed then maybe a version of import should be adopted.  How about:

	extern foobar() : foobarmodule;
or
	foobar() : foobarmodule;

In which case the user wouldn't have to include any new files since
the information about what new files to use would be in the import from
field of the external statement.

  I haven't worked out all the details to a feature like this.  I'm sure
there are some posible bad interactions between already existing features,
but it's food for thought.

-- 
					Eddie Wyatt

e-mail: edw@ius2.cs.cmu.edu

eager@amd.UUCP (mike eager) (06/17/87)

In article <1169@ius2.cs.cmu.edu> edw@ius2.cs.cmu.edu (Eddie Wyatt) writes:
   It seems to me that function prototypes violate a basic good
 rule about high programming language design which is : avoid having the
 user declare the same information more than once.  ....

>	extern foobar() : foobarmodule;
>or
>	foobar() : foobarmodule;
>
>In which case the user wouldn't have to include any new files since
>the information about what new files to use would be in the import from
>field of the external statement.
>
Unless I miss my guess, this is MODULA.
Yes, someone else has worked out some of the details.  See also ADA.

schmidt@zola.ics.uci.edu (Doug Schmidt) (04/30/89)

I'm about to port a C++ program to C.  The original C++ program uses
function prototypes heavily.  It seems ashame to remove all the extra
type checking, but the port must run on both ANSI and non-ANSI C
compilers.  Therefore, I'd like to know whether anyone has devised a
useful set of preprocessor conventions that allow relatively
transparent conversion between compilers that accept prototypes and
those that don't.

Dealing with external declarations seems fairly straight-forward:

/* prototype.h */
#ifdef __STDC__
#define P(X) X
#else
#define P(X)
#endif

Then all extern decls could look like:

#include "prototype.h"
int foo (P(int foobar));

and the preprocessor will correctly substitute in a prototype for ANSI
compilers or do nothing, for non-ANSI compilers.  This should work in
general, right?

However, for function definitions things get messy.  For example, I
could use the old:

#ifdef __STDC__
int foo (int foobar)
#else
int foo (foobar)
    int foobar;        
#endif

trick, but this gets ugly real quick.

Does anyone have a set of macros that helps simplify and beautify the
process?!

thanks,

   Doug
--
On a clear day, under blue skies, there is no need to seek.
And asking about Buddha                +------------------------+
Is like proclaiming innocence,         | schmidt@ics.uci.edu    |
With loot in your pocket.              | office: (714) 856-4043 |

throopw@bert.dg.com (Wayne A. Throop) (05/08/89)

> schmidt@zola.ics.uci.edu (Doug Schmidt)
> I'd like to know whether anyone has devised a
> useful set of preprocessor conventions that allow relatively
> transparent conversion between compilers that accept prototypes and
> those that don't.

There are many tradeoffs to be made.  What I finally ended up using
for situations where non-ansi compilers had to be accomodated has
some drawbacks in that it requires some redundancy and prevents
accurate typechecking of some of it.  But it doesn't look too
terribly awful, and it allows declaration of routines and contracts.

It involves three macros, P, PT, and PP.  The first is used in declaring
functions.  Where one might declare

    int foo( short bar, long bletch )
    {
        ...
    }

one would write

    int foo P(( bar, bletch ),
                short bar PP
                long  bletch )
    {
        ...
    }
    
And in place of

    int (*foo)( short, long );

one would write

    int (*foo) PT(( short, long ));

The definitions of the macros is left as an excersize to the reader.
It isn't too difficult, basically setting the appropriate separators
for PP, deciding whether to expand the argument to PT, and finally
deciding whether to expand the first argument to P and what punctuation
to put around the second one.

--
If it could be demonstrated that any complex organ existed which coult not
possibly have been formed by numerous, successive, slight modifications,
my theory would absolutely break down.
                              --- Charles Darwin
--

Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw