[comp.lang.c] ANSI C Prototypes

ptf@cs.bham.ac.uk (Paul Flinders <FlindersPT>) (10/30/90)

Can anbody suggest an elegant solution to the following. 

given the file foo.c which contains code for a module within a program, 
foo.h which defines prototypes for routines in foo.c and bar.c which
includes foo.h I tend to include foo.h in foo.c so that the compiler
will tell me if I have been foolish enough to change a routines
parameters without changing foo.c (the code in foo.c is not yet cast
in stone so changes are a posibility). This works fine _except_ for
varargs functions eg.

in foo.h:
	extern void ddprintf(const char *fmt, ...);

BUT in foo.c:

	void ddprintf(va_alist)
	va_dcl;

this causes problems. The only solution that I've thought of is to
define some preprocessor token in foo.c _before_ the include of foo.h
and then #ifdef the extern declaration out. This seems a little messy
though. Anybody else have any solutions to this.

Paul Flinders			| "It is bad luck to be superstitious."
FlindersPT@cs.bham.ac.uk	|		-- Andrew W. Mathis

jtc@van-bc.wimsey.bc.ca (J.T. Conklin) (10/30/90)

In article <1005@christopher-robin.cs.bham.ac.uk> ptf@uk.ac.bham.cs (Paul Flinders <FlindersPT>) writes:
>
>This works fine _except_ for varargs functions eg.
>
>in foo.h:
>	extern void ddprintf(const char *fmt, ...);
>
>BUT in foo.c:
>
>	void ddprintf(va_alist)
>	va_dcl;

You seem to be using the old style <varargs.h> interface which is not
compatible with prototypes.  Try something like

	#include <stdarg.h>

	void
	ddprintf(const char *fmt, ...)
	{
		va_list	args;

		va_start(args, fmt);
		vfprintf(stderr, fmt, args);
		va_end(args);
	}

    --jtc

-- 
J.T. Conklin	UniFax Communications Inc.
		...!{uunet,ubc-cs}!van-bc!jtc, jtc@wimsey.bc.ca

steve@taumet.com (Stephen Clamage) (10/31/90)

ptf@cs.bham.ac.uk (Paul Flinders <FlindersPT>) writes:

>given the file foo.c which contains code for a module within a program, 
>foo.h which defines prototypes for routines in foo.c and bar.c which
>includes foo.h I tend to include foo.h in foo.c so that the compiler
>will tell me if I have been foolish enough to change a routines
>parameters without changing foo.c (the code in foo.c is not yet cast
>in stone so changes are a posibility). This works fine _except_ for
>varargs functions eg.

>in foo.h:
>	extern void ddprintf(const char *fmt, ...);

>BUT in foo.c:

>	void ddprintf(va_alist)
>	va_dcl;

>this causes problems.

As well it should.  What you show is not ANSI C, despite the Subject line
of your posting.  It is the BSD C approach to variadic functions, which is
not quite the same as the ANSI approach.  In ANSI C,
	void ddprintf(const char *fmt, ...);
and
	void ddprintf(va_alist);
are not at all the same.  It sounds like you are using <varargs.h> with
an ANSI compiler, instead of using <stdarg.h>.

If you use the ANSI C <stdarg.h> header, the function delcaration in foo.h
and the function defintion in foo.c become identical.  But you need to
decide if the function is to take a variable *list* of arguments, or *one*
argument of type va_list.  The two notions are not the same.  Refer to the
discussions of <stdarg.h>, printf() and vprint() in any good ANSI C text,
or in the Standard, for more details.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

stanley@phoenix.com (John Stanley) (10/31/90)

ptf@cs.bham.ac.uk (Paul Flinders <FlindersPT>) writes:

> Can anbody suggest an elegant solution to the following. 
> 
> in foo.h:
> 	extern void ddprintf(const char *fmt, ...);
> 
> BUT in foo.c:
> 
> 	void ddprintf(va_alist)
> 	va_dcl;
> 
> this causes problems. The only solution that I've thought of is to
> define some preprocessor token in foo.c _before_ the include of foo.h
> and then #ifdef the extern declaration out. This seems a little messy
> though. Anybody else have any solutions to this.

There are two simple solutions:

   1) Use a VAX. VMS C does not (at least, did not) care about the extern
missing from extern declarations. This means the same include that
defines a global int i may be included in all files. The linker on VMS
happily ignores multiple definitions and layers everything with the same
name to the same address.

   2) Do what you thought of. It is pretty common, in my experience, that
the main routine has a #define MAIN, with the included files having:

	#ifndef MAIN
	#define EXTERN
	#else
	#define EXTERN extern
	#endif

This way, all global variables get defined in the main routine, and
declared in the rest.  You probably could just #define extern to nothing,
but that would screw up any externs in any files follwing that one.



"Arinth is a beautiful planet." "Oh, have you been there?"
  "Yes, but not yet." The Doctor. (TB)

boyd@necisa.ho.necisa.oz (Boyd Roberts) (10/31/90)

In article <1005@christopher-robin.cs.bham.ac.uk> ptf@uk.ac.bham.cs (Paul Flinders <FlindersPT>) writes:
>
>in foo.h:
>	extern void ddprintf(const char *fmt, ...);
>
>BUT in foo.c:
>
>	void ddprintf(va_alist)
>	va_dcl;
>
>this causes problems.

Right on!  The function prototypes are just stupid.  I shouldn't have to go:

    extern gore	*good(const char *vomit);

to get the functionality of type checking of function arguments.
The compiler and loader should do it.  The computer is there
to do the tedious stuff for you.  The computer is (was) your friend.

There should be enough stuff in the `.o' to specify all the calls
to my function good() complete with the types of all the arguments.
The `.o' that contains good() should contain extra information of the
types of the arguments to good().  Then the loader should cross
check all the calls with the declaration and then _refuse_ to load 
unless everything check out just right.  Yes, that's right -- _refuse_.

Now this would remove function prototyping once and for all.

But, before you say `what about var args'.  Things that are
var arged should be specified as such on the command line.  You
say how many arguments to check.

Now, I've seen it done and it's good.  My old mate brucee did
it `back in the hippy days' and it runs at the Labs.  Ever heard
of `cyntax'?  Check out cyntax(1) in the Ninth Edition manual.


Boyd Roberts			boyd@necisa.ho.necisa.oz.au

``When the going gets wierd, the weird turn pro...''

rjc@uk.ac.ed.cstr (Richard Caley) (11/01/90)

In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:

    Right on!  The function prototypes are just stupid.  I shouldn't have to go:

	extern gore	*good(const char *vomit);

    to get the functionality of type checking of function arguments.
    The compiler and loader should do it.  


Ok, so what happens to code which never sees the loader?

I want my errors when I compile things, not when some poor guy tries
to use it.

Even in less extreme cases, having the loader do all the checking
would mean you got no error checking until you did a full compile and
link. For even fairly small programs that will be unacceptable ``Well,
yeah, we really do need the cray, we have to recompile the entire
system every three minutes during development.''

Which is not to say that the linker shouldn't _also_ check the types.
So should the run time system and the disc driver, if I had my way :-)

--
rjc@uk.ac.ed.cstr	Paranoid of Leith

salomon@ccu.umanitoba.ca (Dan Salomon) (11/02/90)

In article <RJC.90Nov1145211@brodie.uk.ac.ed.cstr> rjc@uk.ac.ed.cstr (Richard Caley) writes:
>In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>
> > Right on!  The function prototypes are just stupid.
> > I shouldn't have to type:
> >      extern gore	*good(const char *vomit);
> > to get the functionality of type checking of function arguments.
> > The compiler and loader should do it.  
>
> I want my errors when I compile things, not when some poor guy tries
> to use it.
>

You are both right.  Repeatedly typing function prototypes is a tedious
waste of time, but we really would like type errors reported at compile
time.

A possible solution would be to have a utility for building libraries
of function prototypes from source code.  The compiler could check
source code usage against usage in this library.  The linker would have
to repeat the check to make sure that the prototype library didn't get
out of synch with the object library.
-- 

Dan Salomon -- salomon@ccu.UManitoba.CA
               Dept. of Computer Science / University of Manitoba
	       Winnipeg, Manitoba, Canada  R3T 2N2 / (204) 275-6682

peter@ficc.ferranti.com (Peter da Silva) (11/03/90)

In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
> You are both right.  Repeatedly typing function prototypes is a tedious
> waste of time, but we really would like type errors reported at compile
> time.

Ever hear of include files?

> A possible solution would be to have a utility for building libraries
> of function prototypes from source code.

The Manx compiler for the Amiga has a flag that lets you build include files
from the source.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

stever@Octopus.COM (Steve Resnick ) (11/03/90)

In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
>In article <RJC.90Nov1145211@brodie.uk.ac.ed.cstr> rjc@uk.ac.ed.cstr (Richard Caley) writes:
>>In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>>
>> > Right on!  The function prototypes are just stupid.
>> > I shouldn't have to type:
>> >      extern gore	*good(const char *vomit);
>> > to get the functionality of type checking of function arguments.
>> > The compiler and loader should do it.  
>>
>> I want my errors when I compile things, not when some poor guy tries
>> to use it.
>>
>
>You are both right.  Repeatedly typing function prototypes is a tedious
>waste of time, but we really would like type errors reported at compile
>time.
>
>A possible solution would be to have a utility for building libraries
>of function prototypes from source code.  The compiler could check
>source code usage against usage in this library.  The linker would have
>to repeat the check to make sure that the prototype library didn't get
>out of synch with the object library.
>-- 
>

	Some C compilers do this for you. Microsoft C 6.0 will, as an option
	generate declarations (prototypes) to stdout when asked to. This has
	saved me the tedium of typing all those prototypes in myself! :)


Cheers!
Steve


-- 
----------------------------------------------------------------------------
steve.resnick@f105.n143.z1.FIDONET.ORG - or - apple!camphq!105!steve.resnick
Flames, grammar errors, spelling errrors >/dev/nul
The Asylum OS/2 BBS - (408)263-8017 IFNA 1:143/105.0

salomon@ccu.umanitoba.ca (Dan Salomon) (11/03/90)

In article <MKU65ZE@xds13.ferranti.com> peter@ficc.ferranti.com (Peter da Silva) writes:
>In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
>> You are both right.  Repeatedly typing function prototypes is a tedious
>> waste of time, but we really would like type errors reported at compile
>> time.
>> A possible solution would be to have a utility for building libraries
>> of function prototypes from source code.
>
>Ever hear of include files?
>

Actually, include files aren't quite the same as a library of function
prototypes.  The distinction is the same as between an object library,
and a file full of object modules.  In addition to the problem of
keeping the include files up to date,  there is also the problem of
filling up your symbol table with dozens of unused symbols.
Processing all the prototypes in an include file is slower than a search
for and processing of only the ones actually needed.

This is especially a problem when a large collection of functions are
grouped together in a common directory, but in separate files.  Such an
organization speeds up the compilation of large projects, by allowing
the recompilation of only the out-of-date functions.  If each of the
files has to include a large number of prototypes, the some of the
speed up is lost.

Once your set of functions is running, what tool do you use to assemble
all the prototypes into up-to-date include files?  Such tools exists for
object modules, perhaps similar tools are needed for function
prototypes.
-- 

Dan Salomon -- salomon@ccu.UManitoba.CA
               Dept. of Computer Science / University of Manitoba
	       Winnipeg, Manitoba, Canada  R3T 2N2 / (204) 275-6682

rjc@uk.ac.ed.cstr (Richard Caley) (11/03/90)

In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:

    In article <RJC.90Nov1145211@brodie.uk.ac.ed.cstr> rjc@uk.ac.ed.cstr (Richard Caley) writes:

    > I want my errors when I compile things, not when some poor guy tries
    > to use it.

    You are both right.  Repeatedly typing function prototypes is a tedious
    waste of time, but we really would like type errors reported at compile
    time.

Since there is, as far as I know, no difference between the prototype
and the definition except for the `;', there is no extra typing to do.
Just copy the definition. Now I realise there are some brain dead
editors out there and I have upon occasion written C using `cat', but
I assume that anything anyone is willing to use for more than trivial
coding can manage a textual copy.

--
rjc@uk.ac.ed.cstr

peter@ficc.ferranti.com (Peter da Silva) (11/04/90)

In article <1990Nov3.122432.24738@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
> Actually, include files aren't quite the same as a library of function
> prototypes.  The distinction is the same as between an object library,
> and a file full of object modules.

True, there is an efficiency consideration. I don't tend to have a single
module or collection of modules that require more than a handful  of files,
so this hasn't been a problem.

Plus, who says include files have to be stored in files? The C standard
doesn't. This is an implementation detail. :-> Yeh, it's a problem, but
it's not a show stopper.

> In addition to the problem of
> keeping the include files up to date,

That is the real problem. You need a tool to extract the prototypes from
a file. Beyond that, make will do the rest.

> Processing all the prototypes in an include file is slower than a search
> for and processing of only the ones actually needed.

Precompiled include files. Some compilers have 'em. I've never had to use
'em myself, but my home machine has plenty of MIPs and IoStones.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

Markku.Savela@tel.vtt.fi (Markku Savela) (11/04/90)

In article <-BV6Q16@xds13.ferranti.com> peter@ficc.ferranti.com (Peter da Silva) writes:
>> In addition to the problem of
>> keeping the include files up to date,

>That is the real problem. You need a tool to extract the prototypes from
>a file. Beyond that, make will do the rest.

  Yes, but this is a bad habit. The include file should be the external
specification of the module. Any changes in external specification
shouldn't be so frequent, if you have the code done right.
--
Markku Savela (savela@tel.vtt.fi), Technical Research Centre of Finland
Telecommunications Laboratory, Otakaari 7 B, SF-02150 ESPOO, Finland

jaakola@cc.helsinki.fi (11/05/90)

In article <RJC.90Nov3153129@brodie.uk.ac.ed.cstr>, rjc@uk.ac.ed.cstr (Richard Caley) writes:
> Since there is, as far as I know, no difference between the prototype
> and the definition except for the `;', there is no extra typing to do.
> Just copy the definition.

Why do *I* have to do the copying for all my functions, while there's
the compiler which a) is supposed to work for me and not vice versa
             and   b) should know ANSI peculiarities better than I do?

It's okay for me to make an include file, where I declare functions that
are visible to other modules. But consider the following example:

	static void auxiliary_func(a) /* private helper-function */
	int a;
	{
		...
	}

	void visible_fun(b)
	int b;
	{
		...
		auxiliary_func(b+1);   /* should be checked */
		...
	}

The point is, why should I have to tell the compiler twice the type of
auxiliary_func? The prototype should be necessary only if I make a
forward reference to make it easier for the compiler to check types in a
single pass. I think the function definition tells unambiguously the
types, so I should not have to watch warnings such as "function
definition used as a prototype" (Microsoft C 5.1).

Ok, I could turn off such warnings in Microsoft C 5.1, but this turns
off some useful checks as a side-effect! Please, Microsoft, make a
"prototypes-off" switch into Microsoft C for me!

I do not want to bother keeping prototypes of such axiliary
helper-functions up-to-date. "You could use an automatic prototype
builder," I hear you say. Yeah, I could, but why couldn't that builder
be built into the compiler, so that I didn't have to burden my brains to
use it? After all, those helper-functions tend to change much when I
fine-tune the implementation of my module.

Another ANSI-misfeature is the ability to make declarations such as:

	int foo(int b, char *s)

I like much more the old style

	int foo(b,s)
	int b;      /* the b means ... blahblahblah */
	char *s;    /* the s means ... */

This is better, because you can have function name and args on the same
line, so that you can query all function names by grep-like tools (grep
outputs only single lines, which is very compact and - I think -
readable format). If you have much args, this way you can still put all
of them on a single line.

After the first line I tell the types of each arg and use comments after
each args as shown above.

hurtta@cs.Helsinki.FI (Kari Hurtta) (11/05/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:

   It's okay for me to make an include file, where I declare functions that
   are visible to other modules. But consider the following example:

	   static void auxiliary_func(a) /* private helper-function */
	   int a;
	   {
		   ...
	   }

	   void visible_fun(b)
	   int b;
	   {
		   ...
		   auxiliary_func(b+1);   /* should be checked */
		   ...
	   }

   The point is, why should I have to tell the compiler twice the type of
   auxiliary_func? The prototype should be necessary only if I make a
   forward reference to make it easier for the compiler to check types in a
   single pass. I think the function definition tells unambiguously the
   types, so I should not have to watch warnings such as "function
   definition used as a prototype" (Microsoft C 5.1).

   Ok, I could turn off such warnings in Microsoft C 5.1, but this turns
   off some useful checks as a side-effect! Please, Microsoft, make a
   "prototypes-off" switch into Microsoft C for me!

Why not:
	   static void auxiliary_func(int a)
	   {
		   ...
	   }

	   void visible_fun(int b)
	   {
		   ...
		   auxiliary_func(b+1);  
		   ...
	   }

- K E H
( hurtta@cc.Helsinki.FI
  hurtta@cs.Helsinki.FI
  HURTTA@FINUH.BITNET
)

mcdaniel@adi.com (Tim McDaniel) (11/06/90)

Markku.Savela@tel.vtt.fi (Markku Savela) writes:

> Any changes in external specification shouldn't be so frequent, if
> you have the code done right.

You assume
(1) that there is a "right" way to do a module for a project;
(2) that that "right" way is determinable before it is coded;
(3) that the programmer can determine this "right" way a priori.

Assumption (2) fails at my workplace.  I've done a dozen or two little
tweaks to the major module I've developed: a variable exported here,
an argument type changed there, et cetera.  I didn't know that we'd
want to ship exceptions across the network.  Nor did I know that
someone needed VAXC$ERRNO.

An automatic exporter tool is a great convenience, because I don't
have to worry about keeping the .h up to date with the .c.  I have a
hard time just remembering to comment out the #define STANDALONE_DEBUG
before checking it in!  It is particularly nice here, because the tool
does a "diff" between the new .h and the old one, and if they're the
same, deletes the new one and leaves the old one untouched.
Unnecessary makes are avoided even when you "mig *.c" to check
everything.

By the way, a compiler option to output prototypes is not useful.  It
should output declarations for all external identifiers, and only
those.
--
Tim McDaniel                 Applied Dynamics Int'l.; Ann Arbor, Michigan, USA
Work phone: +1 313 973 1300                        Home phone: +1 313 677 4386
Internet: mcdaniel@adi.com                UUCP: {uunet,sharkey}!amara!mcdaniel

peter@ficc.ferranti.com (Peter da Silva) (11/06/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
> 	static void auxiliary_func(a) /* private helper-function */
> 	int a;
> 	{
> 		...
> 	}

If you do this:

	static void auxilary_func(int a)
	{
		...
	}

Everything will work fine. You only need declare it once. Just do it right
the first time and you won't have to do it again (my father always used to
tell me that).
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

rmartin@clear.com (Bob Martin) (11/06/90)

In article <1906@necisa.ho.necisa.oz> 
boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>
>    Right on!  The function prototypes are just stupid.  I shouldn't have to go:
>
>	extern gore	*good(const char *vomit);
>
>    to get the functionality of type checking of function arguments.
>    The compiler and loader should do it.  
>

The C++ compiler does this.  If you declare a function one way
and use it in another, say with different type arguments, then the
program won't link.  C++ does this by mangling the return type and
argument types into the function name.  Thus when you declare
a function:  int x(int y)  the compiler generates a new name
for the function which is what the loader sees.  This name would
specify that the function's name was x, that it returned an int and
that it had a single int parameter.

So if you screw up your declarations, or attempt to use implicit
declarations (bad style), you cannot get a runtime error becuase
of a type mismatch.  The linker will catch the problem.

--

By the way, this technique of mangling the function names means that

	int x(int y);
		and
	double x(double y);

are completely different functions.  You can declare both of them,
define them separately and call the proper function by making sure
that the types are correct.

+----------------------+---------------------------------------------+
| Robert C. Martin     | The authors opinions are his own and do not |
| rmartin@clear.com    | represent the opinions of his employer.     |
| uunet!clrcom!rmartin |                                             |
+----------------------+---------------------------------------------+


-- 

+----------------------+---------------------------------------------+
| Robert C. Martin     | The authors opinions are his own and do not |
| rmartin@clear.com    | represent the opinions of his employer.     |

rjc@uk.ac.ed.cstr (Richard Caley) (11/06/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:

    It's okay for me to make an include file, where I declare functions that
    are visible to other modules. But consider the following example:

	    static void auxiliary_func(a) /* private helper-function */
	    int a;
	    {
		    ...
	    }

	    void visible_fun(b)
	    int b;
	    {
		    ...
		    auxiliary_func(b+1);   /* should be checked */
		    ...
	    }

    The point is, why should I have to tell the compiler twice the type of
    auxiliary_func?

You don't. In the above case you havn't even told it once. If you
did...
	
	static void auxiliary_func(int a)
	
	{
	...
	}

you wouldn't need to type anything more. Or if you did that would be a
massive compiler misfeature. This even saves you a couple of
characters :-)


    Ok, I could turn off such warnings in Microsoft C 5.1, but this turns
    off some useful checks as a side-effect! Please, Microsoft, make a
    "prototypes-off" switch into Microsoft C for me!

If the compiler really does complain in the case where you give the
full type in the function definition then I agree that the writers had
better start watching out for mobs of irate users coming after them
with pitchforks and flaming torches. Someone must make a working C
compiler for your machine...

--
rjc@uk.ac.ed.cstr

chris@mimsy.umd.edu (Chris Torek) (11/06/90)

>In article <1005@christopher-robin.cs.bham.ac.uk> ptf@cs.bham.ac.uk
>(Paul Flinders <FlindersPT>) writes:
>>[my foo.h contains
>> 	extern void ddprintf(const char *fmt, ...);
>> and my foo.c contains
>> 	void ddprintf(va_alist)
>> 	va_dcl;
>> and so I get an error].  The only solution that I've thought of is to
>>define some preprocessor token in foo.c _before_ the include of foo.h
>>and then #ifdef the extern declaration out.

In article <BPaVR3w161w@phoenix.com> stanley@phoenix.com (John Stanley)
provides a correct answer to a completely different question:
>   1) Use a VAX. VMS C does not (at least, did not) care about the extern
>missing from extern declarations. This means the same include that
>defines a global int i may be included in all files.

This refers to object declarations / definitions, as opposed to
function declarations / definitions.  VAX/VMS C uses the same model
that most modern systems use, in which

	int foo;

means: `There is an object called foo which is declared the same way
in some other source file(s), possibly but not necessarily with ``extern''
added on the front.'  (This is also known as `the common model' since
it implements FORTRAN's named COMMON blocks.)  Other systems, however,
treat the above as both a declaration and a definition: `There is
an object called foo.  This is it.  This is the only one.'  On these
systems writing `extern int foo;' means:  `There is an object called
foo.  This is not it.  Get me that object.'  (This is also known as
the `def/ref' model: a declaration without `extern' is a DEFinition,
while one with `extern' is a REFerence, and there must be exactly one
definition for everything.)

The def/ref model is all that is required of a C system.  Some find
the common model more convenient; some find it more error-prone; since
FORTRAN virtually requires it, most systems have it, but sometimes in
a very limited form.

John Stanley goes on to provide an incorrect answer (but one which, if
interpreted in the context of the question answered above, becomes correct):
>   2) Do what you thought of. It is pretty common, in my experience, that
>the main routine has a #define MAIN, with the included files having:
>
>	#ifndef MAIN
>	#define EXTERN
>	#else
>	#define EXTERN extern
>	#endif
>
>This way, all global variables get defined in the main routine, and
>declared in the rest.

This works fine for variables, but is not correct for function prototypes
(more on this in a moment).

>You probably could just #define extern to nothing,
>but that would screw up any externs in any files follwing that one.

Well, you can then `#undef extern'.  A more likely problem is that
many compilers refuse to allow `#define'ing anything that resembles
a keyword (incorrectly, by ANSI X3.159-1989, since keywords do not
exist during the preprocessing phase of the translation).

Okay, so what is wrong with:

	extern void ddprintf(const char *fmt, ...);

followed by

	void ddprintf(va_alist) va_dcl {

?  The answer is in fact contained in the FAQ answers, but here it
is again.  EXCEPT IN RESTRICTED CIRCUMSTANCES, PROTOTYPE DECLARATIONS
ARE NOT COMPATIBLE WITH OLD-STYLE DEFINITIONS.

Note that both

	extern void ddprintf(const char *fmt, ...);

and

	void ddprintf(const char *fmt, ...);

are declarations.  The word `extern' is ignored when declaring
functions.  The difference between a function declaration and a
function definition is that a declaration ends with a balancing close
parenthesis followed by a semicolon, while a definition ends with a
balancing close parenthesis followed by an opening `{'.  It is
incorrect to put `extern' in front of a definition: `extern' is *not*
ignored when DEFINING functions, only when DECLARING them.

A prototype declaration tells the compiler:  `This name is the name of
a function.  It has a fixed number of arguments, and each of these has
a fixed type.  The function returns a fixed type.  Remember all of
these facts.'  If the declaration contains a `, ...' before the closing
parenthesis, this changes to:  `This name is the name of a function.
It has a variable number of arguments, preceded by a fixed number of
arguments.  Each of the latter has a fixed type.  The function returns
a fixed type.  Remember all of these facts.'

When the compiler discovers a prototype DEFINITION, it treats that as
another declaration (or the first declaration, if there have been no
other declarations for the function) and makes sure the arguments
match in number and types, and that the return type matches; it then
compiles code for the function body.

When the compiler discovers an old-style declaration or definition,
however, this tells the compiler:  `This name is the name of a
function.  It may or may not have some arguments.  The arguments,
if any, are not variable.  The function returns a fixed type.  Remember
all of these facts.'

The latter can sometimes, but by no means always, be reconciled with
a prototype declaration that gave the same name and return type, but
also gave the number and types of the arguments.  It is not too difficult
to define exactly when this can be done, although the result can be
surprising.

A: The arguments must be fixed, not variable.  The ANSI standard
   permits compilers to use different calling sequences for fixed-
   and variable-argument functions, and therefore requires the
   programmer to announce variable-argument functions `in advance',
   as it were, using a prototype.  All others are assumed to be
   fixed-arguments.

B: None of the arguments in the prototype being reconciled against
   the definition may have a type which differs from its image under
   promotion.

What B really means is that no argument can have type `char', `short',
or `float' or any of their signed or unsigned variants.  In other
words, although

	int putc(int c, FILE *f);
	int putc(c, f) int c; FILE *f; { ... }	/* ok */

is legal, the following is not:

	int put1(char c, FILE *f);
	int put1(c, f) char c; FILE *f; { ... }	/* bad */

Since the image of `char' under promotion is `int', this fails rule
B.  Likewise,

	float mul(float, float);
	float mul(x, y) float x, y; { ... }	/* bad */

is illegal, because the image of `float' under promotion is `double'.

The reason for restriction B has to do with backwards compatibility
(being able to run the mounds of code that use old-style definitions)
without requiring systems to promote *all* arguments (being able to
pass `float's around without turning them into `double's, etc).  Any
C system can *allow* the codes marked `bad' to work, but no C system
is *required* to do so.

So (after 162 lines), the answer is: declare your function as

	/* with optional `extern' included here */
	extern void ddprintf(const char *fmt, ...);

and define it as

	void ddprintf(const char *fmt, ...) {

and any ANSI C system is obliged to compile it correctly.  Do anything
else and all bets are off.  Since older C compilers do not recognize the
`, ...' syntax, you may have to write:

	#ifdef HAVE_PROTOTYPE_DECLARATIONS
	void ddprintf(constchar *fmt, ...);
	#else
	void ddprintf();
	#endif
and
	#ifdef HAVE_PROTOTYPE_DEFINITIONS
	void ddprintf(const char *fmt, ...) {
	#else
	void ddprintf(va_alist) va_dcl { char *fmt;
	#endif
		va_alist ap;
	#ifdef HAVE_PROTYTYPE_DEFINITIONS
		va_start(ap, fmt);
	#else
		va_start(ap); fmt = va_arg(ap, char *);
	#endif
		...
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

darcy@druid.uucp (D'Arcy J.M. Cain) (11/06/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
>Why do *I* have to do the copying for all my functions, while there's
>the compiler which a) is supposed to work for me and not vice versa
>             and   b) should know ANSI peculiarities better than I do?
>
>	static void auxiliary_func(a) /* private helper-function */
>	int a;
>	{
>		...
>	}
>
>	void visible_fun(b)
>	int b;
>	{
>		...
>		auxiliary_func(b+1);   /* should be checked */
>		...
>	}
>
>The point is, why should I have to tell the compiler twice the type of
>auxiliary_func? The prototype should be necessary only if I make a
All ANSI compilers that I have used do exactly that.  You do of course
have to declare the function in an ANSI compliant way but once you do
it is effectively prototyped from that point on.  In the above example
replace the first two lines with:

	static void auxiliary_func(int a) /* private helper-function */

The only time that I have to retype (or copy) a prototype is when the
function is used in a different module.

>
>Another ANSI-misfeature is the ability to make declarations such as:
>
>	int foo(int b, char *s)
>
>I like much more the old style
>
>	int foo(b,s)
>	int b;      /* the b means ... blahblahblah */
>	char *s;    /* the s means ... */
>
>This is better, because you can have function name and args on the same
>line, so that you can query all function names by grep-like tools (grep
>outputs only single lines, which is very compact and - I think -
>readable format). If you have much args, this way you can still put all
>of them on a single line.
So that just means we need smarter tools, not that we should stand still
for fear of disturbing something.

>
>After the first line I tell the types of each arg and use comments after
>each args as shown above.
Where suitable I do the same thing:
	int foo(
	int b,      /* the b means ... blahblahblah */
	char *s)    /* the s means ... */

-- 
D'Arcy J.M. Cain (darcy@druid)     |
D'Arcy Cain Consulting             |   I support gun control.
West Hill, Ontario, Canada         |   Let's start with the government!
+ 416 281 6094                     |

stanley@phoenix.com (John Stanley) (11/07/90)

rjc@uk.ac.ed.cstr (Richard Caley) writes:

> In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
> 
>     It's okay for me to make an include file, where I declare functions that
>     are visible to other modules. But consider the following example:
> 
> 	    static void auxiliary_func(a) /* private helper-function */
> 	    int a;
> 	    {

>     The point is, why should I have to tell the compiler twice the type of
>     auxiliary_func?
> 
> You don't. In the above case you havn't even told it once. If you
> did...

   No, he most certainly DID tell the compiler the type of the function.
This is one of those prior art things that the ANSI committee had to
maintain. It is called a function definition (old style), and has scope of
'global to file'. He even told the compiler the type of arguement a. The
compiler should most certainly know the type of the function when it sees
it later within the other function. Because it is the old-style
definition, the compiler is not required to keep track of the type of the
parameter a required by that fucntion, so there will be no type checking
on a. There most certainly should be type checking on the function itself.
And the compiler should absolutely not generate an error on this syntax. 

> 	static void auxiliary_func(int a)
> 	
> 	{
> 
> you wouldn't need to type anything more. Or if you did that would be a
> massive compiler misfeature. This even saves you a couple of
> characters :-)

   Moving the declaration of a into the parameter list converts this
definition into new style. The only difference between old and new in
this case is that the compiler is now required to type check the
_parameter_ passed to a.

   Through other communication with Mr. Jaakola, it turns out that this
is a misfeature of Microsoft C. 






<> "Aneth!  That's a charming place!" "You've been to Aneth?" 
<> "Yes, but not yet." -- The Doctor and Seth, "The Horns of Nimon".
><
<> "Sanity check!" "Sorry, we can't accept it, it's from out of state." - me

gwyn@smoke.brl.mil (Doug Gwyn) (11/07/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
>Why do *I* have to do the copying for all my functions, while there's
>the compiler which a) is supposed to work for me and not vice versa
>             and   b) should know ANSI peculiarities better than I do?

Oh, for Christ's sake, nobody is forcing you to do anything.  If you
don't like prototypes just don't use them!

>Another ANSI-misfeature is the ability to make declarations such as:
>	int foo(int b, char *s)
>I like much more the old style
>	int foo(b,s)
>	int b;      /* the b means ... blahblahblah */
>	char *s;    /* the s means ... */

And what prevents you from using the old style if you like it better?

By the way, some people like to use the new style definitions like this:
	int  foo(		/* returns ... */
		int   b;	/* the b means ... blahblahblah */
		char *s;	/* the s means ... */
		)

>This is better, because you can have function name and args on the same
>line, so that you can query all function names by grep-like tools

But you have no idea what the argument types are that way; the new style
is better in that respect.

All that your posting really said is that you're comfortable doing things
a particular way and do not wish to change.  Nobody is making you change.

scs@adam.mit.edu (Steve Summit) (11/08/90)

In article <14382@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>Oh, for Christ's sake, nobody is forcing you to do anything.  If you
>don't like prototypes just don't use them!
>...what prevents you from using the old style if you like it better?

Section 3.9.5, which is a red flag to any responsible programmer;
a standard which marks a feature as "obsolescent" is clearly
stating that the feature may be withdrawn later, that it is being
included only for the benefit of existing code, and that new code
is well advised not to use it.  (I'm sure Doug knows this.)
Actually, I thought I might be editorializing a bit there, but
the definition of obsolescent in section 1.8 says the same thing.

I'm still using old-style definitions and declarations, but I
expect to start taking flak for it soon, because the party line
today seems to be that prototypes are a Good Thing.  I don't
particularly like them, mostly because of the maintenance issues
several people have already mentioned.  (At least now I know I'm
not the only one who feels that way!)  I'll probably convert to
them myself, though, as soon as all the compilers I use support
them, or whenever I start using tricks like the one Karl Heuer
will remind us of next.

I feel it's as likely that old-style function syntax will be
removed in 10 years or so as it was that =+ and the like would be
removed 10 years or so after they were officially deprecated.
(Oh, look: they _were_ removed :-) .)

                                            Steve Summit
                                            scs@adam.mit.edu

brad@SSD.CSD.HARRIS.COM (Brad Appleton) (11/09/90)

I grabbed a PD prototype generator utility written in C (that I think works
for both ANSI and K&R) not too long ago - its pretty small - here it is:

I have a 10 line awk script that does it too but the C program is obviously
a wee bit more portable ;-)

Its only two files (a .c and a .h file). Actually its only one file - it can
create its own .h file but you need to get it running first :-)

Anyway - im sure some of you will find some use for it!
Its written in K&R but Im sure it would be no problem to convert to ANSI.


______________________ "And miles to go before I sleep." ______________________
 Brad Appleton           brad@ssd.csd.harris.com       Harris Computer Systems
                             uunet!hcx1!brad           Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~


------------------------------ cut here ---------------------------------------

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by brad on Thu Nov  8 11:41:53 EST 1990
# Contents:  mkproto.c mkproto.h
 
echo x - mkproto.c
sed 's/^@//' > "mkproto.c" <<'@//E*O*F mkproto.c//'
/* Program to extract function declarations from C source code */
/* Written by Eric R. Smith and placed in the public domain    */
/* Thanks are due to Jwahar R. Bammi for fixing several bugs   */
/* and providing the Unix makefiles. S. Manoharan included a   */
/* Getopt() function and modified the code to handle C++ style */
/* comments and member functions.                              */

#if defined(__STDC__) && !defined(minix)
#include <stddef.h>
#include <stdlib.h>
#else
#define EXIT_SUCCESS  0
#define EXIT_FAILURE  1
extern char *malloc();
#endif

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DEBUG(s) { if (dodebug) fputs(s, stderr); } /* */
/*#define DEBUG(s) /* */

#define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
#define ABORTED ( (Word *) -1 )
#define MAXPARAM 20       /* max. number of parameters to a function */
#define NEWBUFSIZ (20480*sizeof(char)) /* new buffer size */

typedef enum { false = 0, true = 1 } Boolean;

Boolean newline_seen = true;  /* are we at the start of a line */
Boolean dostatic = false;  /* do static functions? */
Boolean donum    = false;  /* print line numbers? */
Boolean dopromote = true;  /* promote certain sc-specifiers */
Boolean dohead   = true;   /* do file headers? */
Boolean dodebug = false;   /* do debugging? */
Boolean docond   = false;  /* conditionalize for non-ANSI compilers */

long linenum  = 1L;        /* line number in current file */
int glastc   = ' ';        /* last char. seen by getsym() */
int inquote = 0;           /* in a quote?? */

typedef struct word {
   struct word *next;
   char   string[1];
} Word;

#include "mkproto.h"

/*
 * Routines for manipulating lists of words.
 */

Word *word_alloc(s)
char *s;
{
   Word *w;

   w = (Word *) malloc((unsigned)sizeof(Word) + strlen(s) + 1);
		/* ++jrb */
   (void)strcpy(w->string, s);
   w->next = NULL;
   return w;
}

void word_free(w)
Word *w;
{
   Word *oldw;
   while (w) {
      oldw = w;
      w = w->next;
      free((char *)oldw);
   }
}

/* return the length of a list; empty words are not counted */
int List_len(w)
Word *w;
{
   int count = 0;

   while (w) {
      if (*w->string) count++;
      w = w->next;
   }
   return count;
}

/* Append two lists, and return the result */

Word *word_append(w1, w2)
Word *w1, *w2;
{
   Word *r, *w;

   r = w = word_alloc("");

   while (w1) {
      w->next = word_alloc(w1->string);
      w = w->next;
      w1 = w1->next;
   }
   while (w2) {
      w->next = word_alloc(w2->string);
      w = w->next;
      w2 = w2->next;
   }

   return r;
}

/* see if the last entry in w2 is in w1 */

int foundin(w1, w2)
Word *w1, *w2;
{
   while (w2->next)
      w2 = w2->next;

   while (w1) {
      if (!strcmp(w1->string, w2->string))
         return 1;
      w1 = w1->next;
   }
   return 0;
}

/* add the string s to the given list of words */

void addword(w, s)
Word *w; char *s;
{
   while (w->next) w = w->next;
   w->next = word_alloc(s);
}

/* given a list representing a type and a variable name, extract just
 * the base type, e.g. "struct word *x" would yield "struct word"
 */

Word *typelist(p)
Word *p;
{
   Word *w, *r;

   r = w = word_alloc("");
   while (p && p->next) {
      if (p->string[0] && !ISCSYM(p->string[0]))
         break;
      w->next = word_alloc(p->string);
      w = w->next;
      p = p->next;
   }
   return r;
}

/* typefixhack: promote formal parameters of type "char", 
 * "unsigned char", "short", or "unsigned short" to "int".
 */

void typefixhack(w)
Word *w;
{
   Word *oldw = 0;

   while (w) {
      if (*w->string) {
         if ( (!strcmp(w->string, "char") ||
             !strcmp(w->string, "short") )
             && (List_len(w->next) < 2) )
         {
            if (oldw && !strcmp(oldw->string, "unsigned")) {
               oldw->next = w->next;
               free((char *)w);
               w = oldw;
            }
            (void)strcpy(w->string, "int");
         }
      }
      w = w->next;
   }
}

/* read a character: if it's a newline, increment the line count */

int ngetc(f)
FILE *f;
{
   int c;

   c = getc(f);
   if (c == '\n') linenum++;

   return c;
}

/* read the next character from the file. If the character is '\' then
 * read and skip the next character. Any comment sequence is converted
 * to a blank. [ Both C and C++ style comments are considered - sam ]
 */

int fnextch(f)
FILE *f;
{
   int c, lastc, incomment;

   c = ngetc(f);
   while (c == '\\') {
      DEBUG("fnextch: in backslash loop\n");
      c = ngetc(f);   /* skip a character */
      c = ngetc(f);
   }
   if (c == '/' && !inquote) {
      c = ngetc(f);
      if (c == '*') {  /* C comments */
         incomment = 1;
         c = ' ';
         DEBUG("fnextch: comment seen\n");
         while (incomment) {
            lastc = c;
            c = ngetc(f);
            if (lastc == '*' && c == '/')
               incomment = 0;
            else if (c < 0)
               return c;
         }
         return fnextch(f);
      }
      else if ( c == '/' ) {  /* C++ comments */
         incomment = 1;
         c = ' ';
         DEBUG("fnextch: C++ comment seen\n");
         while ( c != '\n' ) {
            c = ngetc(f);
         }
	 incomment = 0;
         return fnextch(f);
      }
      else {
         if (c == '\n') linenum--;
         (void)ungetc(c, f);
         return '/';
      }
   }
   return c;
}


/* Get the next "interesting" character. Comments are skipped, and 
 * strings are converted to "0". Also, if a line starts with "#" 
 * it is skipped.
 */

int nextch(f)
FILE *f;
{
   int c;

   c = fnextch(f);
   if (newline_seen && c == '#') {
      do {
         c = fnextch(f);
      } while (c >= 0 && c != '\n');
      if (c < 0)
         return c;
   }
   newline_seen = (c == '\n') ? true : false;

   if (c == '\'' || c == '\"') {
      DEBUG("nextch: in a quote\n");
      inquote = c;
      while ( (c = fnextch(f)) >= 0 ) {
         if (c == inquote) {
            inquote = 0;
            DEBUG("nextch: out of quote\n");
            return '0';
         }
      }
      DEBUG("nextch: EOF in a quote\n");
   }
   return c;
}

/*
 * Get the next symbol from the file, skipping blanks.
 * Return 0 if OK, -1 for EOF.
 * Also collapses everything between { and }
 */

int getsym(buf, f)
char *buf; FILE *f;
{
   register int c;
   int inbrack = 0;

   DEBUG("in getsym\n");
   c = glastc;
   while ((c > 0) && isspace(c)) {
      c = nextch(f);
   }
   DEBUG("getsym: spaces skipped\n");
   if (c < 0) {
      DEBUG("EOF read in getsym\n");
      return -1;
   }
   if (c == '{') {
      inbrack = 1;
      DEBUG("getsym: in bracket\n");
      while (inbrack) {
         c = nextch(f);
         if (c < 0) {
            DEBUG("getsym: EOF seen in bracket loop\n");
            glastc = c;
            return c;
         }
         if (c == '{') inbrack++;
         else if (c == '}') inbrack--;
      }
      (void)strcpy(buf, "{}");
      glastc = nextch(f);
      DEBUG("getsym: out of in bracket loop\n");
      return 0;
   }
   if (!ISCSYM(c)) {
      *buf++ = c;
      *buf = 0;
      glastc = nextch(f);
      DEBUG("getsym: returning special symbol\n");
      return 0;
   }
   while (ISCSYM(c)) {
      *buf++ = c;
      c = nextch(f);
   }
   *buf = 0;
   glastc = c;
   DEBUG("getsym: returning word\n");
   return 0;
}

/*
 * skipit: skip until a ";" or the end of a function declaration is seen
 */

int skipit(buf, f)
char *buf; FILE *f;
{
   int i;

   do {
      DEBUG("in skipit loop\n");
      i = getsym(buf, f);
      if (i < 0) return i;
   } while (*buf != ';' && *buf != '{');

   return 0;
}

/*
 * Get a parameter list; when this is called the next symbol in line
 * should be the first thing in the list.
 */

Word *getparamlist(f)
FILE *f;
{
   static Word *pname[MAXPARAM]; /* parameter names */
   Word   *tlist,      /* type name */
   *plist;      /* temporary */
   int     np = 0;      /* number of parameters */
   int     typed[MAXPARAM];  /* parameter has been given a type */
   int   tlistdone;   /* finished finding the type name */
   int   sawsomething;
   int     i;
   int   inparen = 0;
   char buf[80];

   DEBUG("in getparamlist\n");
   for (i = 0; i < MAXPARAM; i++)
      typed[i] = 0;

   plist = word_alloc("");

   /* first, get the stuff inside brackets (if anything) */

   sawsomething = 0;   /* gets set nonzero when we see an arg */
   for (;;) {
      if (getsym(buf, f) < 0) return NULL;
      if (*buf == ')' && (--inparen < 0)) {
         if (sawsomething) {   /* if we've seen an arg */
            pname[np] = plist;
            plist = word_alloc("");
            np++;
         }
         break;
      }
      if (*buf == ';') {   /* something weird */
         return ABORTED;
      }
      sawsomething = 1;   /* there's something in the arg. list */
      if (*buf == ',' && inparen == 0) {
         pname[np] = plist;
         plist = word_alloc("");
         np++;
      }
      else {
         addword(plist, buf);
         if (*buf == '(') inparen++;
      }
   }

   /* next, get the declarations after the function header */

   inparen = 0;

   tlist = word_alloc("");
   plist = word_alloc("");
   tlistdone = 0;
   sawsomething = 0;
   for(;;) {
      if (getsym(buf, f) < 0) return NULL;

      /* handle a list like "int x,y,z" */
      if (*buf == ',' && !inparen) {
         if (!sawsomething)
            return NULL;
         for (i = 0; i < np; i++) {
            if (!typed[i] && foundin(plist, pname[i])) {
               typed[i] = 1;
               word_free(pname[i]);
               pname[i] = word_append(tlist, plist);
               /* promote types */
               if ( dopromote ) typefixhack(pname[i]);
               break;
            }
         }
         if (!tlistdone) {
            tlist = typelist(plist);
            tlistdone = 1;
         }
         word_free(plist);
         plist = word_alloc("");
      }
      /* handle the end of a list */
      else if (*buf == ';') {
         if (!sawsomething)
            return ABORTED;
         for (i = 0; i < np; i++) {
            if (!typed[i] && foundin(plist, pname[i])) {
               typed[i] = 1;
               word_free(pname[i]);
               pname[i] = word_append(tlist, plist);
               /* promote types */
               if ( dopromote ) typefixhack(pname[i]);
               break;
            }
         }
         tlistdone = 0;
         word_free(tlist); 
         word_free(plist);
         tlist = word_alloc("");
         plist = word_alloc("");
      }
      /* handle the  beginning of the function */
      else if (!strcmp(buf, "{}")) break;
      /* otherwise, throw the word into the list 
			    (except for "register") */
      else if (strcmp(buf, "register")) {
         sawsomething = 1;
         addword(plist, buf);
         if (*buf == '(') inparen++;
         if (*buf == ')') inparen--;
      }
   }

   /* Now take the info we have and build a prototype list */

   /* empty parameter list means "void" */
   if (np == 0)
      return word_alloc("void");

   plist = tlist = word_alloc("");
   for (i = 0; i < np; i++) {

      /* If no type provided, make it an "int" */
      if ( !(pname[i]->next) ||
          (!(pname[i]->next->next)&&strcmp(pname[i]->next->string,
	  "void"))) {
         addword(tlist, "int");
      }
      while (tlist->next) tlist = tlist->next;
      tlist->next = pname[i];
      if (i < np - 1)
         addword(tlist, ", ");
   }
   return plist;
}

/*
 * emit a function declaration. The attributes and name of the function
 * are in wlist; the parameters are in plist.
 */

void emit(wlist, plist, startline)
Word *wlist, *plist;
long  startline;
{
   Word *w;
   int count = 0;

   DEBUG("emit called\n");
   if (donum)
      (void)printf("/*%8ld */ ", startline);

   for (w = wlist; w; w = w->next) {
      if ( w->string[0] == ':' )
	 return; /* C++ member function detected */
      if (w->string[0])
         count ++;
   }

   if (count < 2)
      (void)printf("int ");

   for (w = wlist; w; w = w->next) {
      (void)printf("%s", w->string);
      if (ISCSYM(w->string[0]))
         (void)printf(" ");
   }
   if (docond)
      (void)printf("PROTO((");
   else
      (void)printf("( ");
   for (w = plist; w; w = w->next) {
      (void)printf("%s", w->string);
      if (ISCSYM(w->string[0]))
         (void)printf(" ");
   }
   if (docond)
      (void)printf("));\n");
   else
      (void)printf(");\n");
}

/*
 * get all the function declarations
 */

void getdecl(f)
FILE *f;
{
   Word *plist, *wlist = NULL;
   char buf[80];
   int sawsomething;
   long startline;      /* line where declaration started */
   int oktoprint;
again:
   word_free(wlist);
   wlist = word_alloc("");
   sawsomething = 0;
   oktoprint = 1;

   for(;;) {
      DEBUG("main getdecl loop\n");
      if (getsym(buf,f) < 0) {
         DEBUG("EOF in getdecl loop\n");
         return;
      }
      /* try to guess when a declaration is not an external 
		    function definition */
      if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
          !strcmp(buf, "=") || !strcmp(buf, "typedef") ||
          !strcmp(buf, "extern")) {
         (void)skipit(buf, f);
         goto again;
      }
      if (!dostatic && !strcmp(buf, "static")) {
         oktoprint = 0;
      }
      /* for the benefit of compilers that allow "inline" 
		  declarations */
      if (!strcmp(buf, "inline") && !sawsomething)
         continue;
      if (!strcmp(buf, ";")) goto again;

      /* A left parenthesis *might* indicate a function definition */
      if (!strcmp(buf, "(")) {
         startline = linenum;
         if (!sawsomething || !(plist = getparamlist(f))) {
            (void)skipit(buf, f);
            goto again;
         }
         if (plist == ABORTED)
            goto again;

         /* It seems to have been what we wanted */
         if (oktoprint)
            emit(wlist, plist, startline);
         word_free(plist);
         goto again;
      }
      addword(wlist, buf);
      sawsomething = 1;
   }
}

void main(argc, argv)
int argc; 
char *argv[];
{
   FILE *f;
   char *iobuf;
   char *title = "UNSPECIFIEDHEADERTITLE";
   extern void Usage();
   int opch; extern int Optind; extern char *Optarg;


   while ( ( opch = Getopt(argc,argv,"h:snpPD") ) != -1 )
      switch ( opch ) {
      case 'h'   :
	 (void)strcpy(title, Optarg);
	 break;
      case 's'   :
	 dostatic = true;
	 break;
      case 'n'   :
	 donum = true;
	 break;
      case 'p'   :
	 docond = true;
	 break;
      case 'P'   :
	 dopromote = false;
	 break;
      case 'D'   :
	 dodebug = true;
	 break;
      default    :
	 Usage(argv[0]);
	 exit(0);
      } /* ensw */

   iobuf = malloc((unsigned)NEWBUFSIZ);

   (void)printf("#ifndef %s\n", title);
   (void)printf("#define %s\n", title);

   if (docond) {
      (void)printf("#ifdef __STDC__\n");
      (void)printf("# define\tPROTO(s) s\n");
      (void)printf("#else\n");
      (void)printf("# define PROTO(s) ()\n");
      (void)printf("#endif\n\n");
   }
   if ( Optind > argc )
      getdecl(stdin);
   else
      for ( ; Optind < argc; ++Optind ) {
         DEBUG("trying a new file\n");
         if ( ( f = fopen(argv[Optind],"r") ) == (FILE *)0 ) {
	    (void)fprintf(stderr,"%s: cannot open %s\n",
	       argv[0], argv[Optind]);
	    continue;
         }

         /* do the file operations here */
	 /*
         if (iobuf)
            (void)setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ); /* */
         if (dohead)
            (void)printf("\n/* %s */\n", argv[Optind]);
         linenum = 1;
         newline_seen = true;
         glastc = ' ';
         DEBUG("calling getdecl\n");
         getdecl(f);
         DEBUG("back from getdecl\n");
         DEBUG("back from fclose\n");

         (void)fclose(f);
      } /* enfo */

   if (docond) {
      (void)printf("\n#undef PROTO\n");   /* clean up namespace */
   }
   (void)printf("\n#endif\n");
   exit(EXIT_SUCCESS);
}

void Usage(progname)
char *progname;
{
   (void)fprintf(stderr,"Usage: %s [options][files ...]\n",progname);
   (void)fprintf(stderr,
      "\t-n: put line numbers of declarations as comments\n");
   (void)fprintf(stderr,
      "\t-s: include declarations for static functions\n");
   (void)fprintf(stderr,
      "\t-p: make header files readable by non-ANSI compilers\n");
   (void)fprintf(stderr,
      "\t-P: don't promote any sc-specifiers\n");
   (void)fprintf(stderr,
      "\t-h HeaderTitle: use HeaderTitle to title the header file\n");
   (void)fprintf(stderr,
      "\t-D: operate on debug mode\n");
   exit(EXIT_FAILURE);
}



#ifndef lint
static char *scid = "s. manoharan edinburgh univ";
#endif

#include<stdio.h>

char *Optarg; int Optind;


int Getopt(argc,argv,options)
int argc; char **argv; char *options;
{
   char *str, *ptr; char opch; char *Strchr();
   static int flag = 0; static int Argc; static char **Argv;

   if (  flag == 0 ) {
      Argc = argc; Argv = argv; flag = 1; Optind = 1;
   }

   if ( Argc <= 1 ) return -1;

   if ( --Argc >= 1 ) {
      str = *++Argv;
      if (*str != '-') return -1; /* argument is not an option   */
      else {  /* argument is an option */
	 if ( ( ptr = Strchr(options, opch = *++str) ) != (char *) 0 ) {
	    ++Optind;
            Optarg = ++str; /* point to rest of argument if any  */
            if ( ( *++ptr == ':' ) && ( *Optarg == '\0' ) ) {
               if (--Argc <= 0) return '?';
               Optarg = *++Argv; ++Optind;
            }
	    return opch;
         }
	 else if ( opch == '-' ) { /* end of options */
	    ++Optind;
	    return -1;
	 }
         else return '?';
      }
   }
   return 0; /* this will never be reached */

} /* EnGetopt */

char *Strchr(s,c)
char *s; char c;
{
   while ( *s != '\0' ) {
      if ( *s == c ) return s;
      else ++s;
   }
   return ( (char *) 0 );
} /* EnStrchr */
@//E*O*F mkproto.c//
chmod u=rw,g=,o= mkproto.c
 
echo x - mkproto.h
sed 's/^@//' > "mkproto.h" <<'@//E*O*F mkproto.h//'
#ifdef __STDC__
# define	PROTO(s) s
#else
# define PROTO(s) ()
#endif


/* mkproto.c */
Word *word_alloc PROTO((char *s ));
void word_free PROTO((Word *w ));
int List_len PROTO((Word *w ));
Word *word_append PROTO((Word *w1 , Word *w2 ));
int foundin PROTO((Word *w1 , Word *w2 ));
void addword PROTO((Word *w , char *s ));
Word *typelist PROTO((Word *p ));
void typefixhack PROTO((Word *w ));
int ngetc PROTO((FILE *f ));
int fnextch PROTO((FILE *f ));
int nextch PROTO((FILE *f ));
int getsym PROTO((char *buf , FILE *f ));
int skipit PROTO((char *buf , FILE *f ));
Word *getparamlist PROTO((FILE *f ));
void emit PROTO((Word *wlist , Word *plist , long startline ));
void getdecl PROTO((FILE *f ));
void main PROTO((int argc , char **argv ));
void Usage PROTO((void ));

#undef PROTO
@//E*O*F mkproto.h//
chmod u=rw,g=,o= mkproto.h
 
exit 0
______________________ "And miles to go before I sleep." ______________________
 Brad Appleton           brad@ssd.csd.harris.com       Harris Computer Systems
                             uunet!hcx1!brad           Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~

bright@nazgul.UUCP (Walter Bright) (11/09/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
/	static void auxiliary_func(a) /* private helper-function */
/	int a;
/	{
/	}
/	void visible_fun(b)
/	int b;
/	{
/		...
/		auxiliary_func(b+1);   /* should be checked */
/		...
/	}
/
/The point is, why should I have to tell the compiler twice the type of
/auxiliary_func? The prototype should be necessary only if I make a
/forward reference to make it easier for the compiler to check types in a
/single pass. I think the function definition tells unambiguously the
/types, so I should not have to watch warnings such as "function
/definition used as a prototype" (Microsoft C 5.1).

In Zortech, the first function declaration does result in a prototype
being declared (internal to the compiler) and all future uses of
auxiliary_func are checked against that prototype.

In fact, the first use of a function, in the absence of a prototype,
causes a prototype to be generated for that function based on the
default argument promotion rules. This is not ANSI behavior, and can
be disabled with the -P switch, but is very useful in checking for
errors in old K&R code.

The -r switch, strict prototyping, is also available, which enforces a
rule that all uses of a function must have a prototype in scope for it.
I use this switch for all my code, and recommend its use, as it results
in the bugs being caught at the earliest possible time (when they are
the cheapest to fix!).

stever@Octopus.COM (Steve Resnick ) (11/11/90)

In article <151@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:


[Stuff Deleted]

>/The point is, why should I have to tell the compiler twice the type of
>/auxiliary_func? The prototype should be necessary only if I make a
>/forward reference to make it easier for the compiler to check types in a
>/single pass. I think the function definition tells unambiguously the
>/types, so I should not have to watch warnings such as "function
>/definition used as a prototype" (Microsoft C 5.1).
>
>In Zortech, the first function declaration does result in a prototype
>being declared (internal to the compiler) and all future uses of
>auxiliary_func are checked against that prototype.
>
>In fact, the first use of a function, in the absence of a prototype,
>causes a prototype to be generated for that function based on the
>default argument promotion rules. This is not ANSI behavior, and can
>be disabled with the -P switch, but is very useful in checking for
>errors in old K&R code.
>
>The -r switch, strict prototyping, is also available, which enforces a
>rule that all uses of a function must have a prototype in scope for it.
>I use this switch for all my code, and recommend its use, as it results
>in the bugs being caught at the earliest possible time (when they are
>the cheapest to fix!).

Both C compilers I use (Turbo C on DOS and Microsoft C on OS/2) do the same 
thing. (At least from my perspective). Turbo C doesn't generate warnings or
anything at all, but takes a function definition as a prototype (it WILL warn
about mis-matched types in parameters in calls to functions with previous
definitions but no prototype.) Microsoft C generates a warning.

	foo.c(6) : warning C4103: 'main' : function definition used as prototype

The warning is generated, however, at the first call to the function versus 
the function definition. I guess the would be correct behaviour.

Just as a matter of course, however, I usually use prototypes on all functions,
since this helps with conflicts on the PC in regards to pointer sizes, etc
and it helps document the code, too.


Adding my $.02 (unless it has devaluated already)
Steve

-- 
----------------------------------------------------------------------------
steve.resnick@f105.n143.z1.FIDONET.ORG - or - apple!camphq!105!steve.resnick
Flames, grammar errors, spelling errrors >/dev/nul
The Asylum OS/2 BBS - (408)263-8017 IFNA 1:143/105.0

utility@quiche.cs.mcgill.ca (Ronald BODKIN) (11/24/90)

In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>Right on!  The function prototypes are just stupid.  I shouldn't have to go:

>    extern gore	*good(const char *vomit);

>to get the functionality of type checking of function arguments.
>The compiler and loader should do it.
	However, one prototype will allow you to automatically coerce
things like ints to longs/floats/doubles or "near" pointers to "far"
pointers, etc.  Also, it is much faster to catch a prototype problem
before linking a large application.
	Ron

jrv@sdimax2.mitre.org (VanZandt) (11/28/90)

In article <75@homer.cs.mcgill.ca> utility@quiche.cs.mcgill.ca (Ronald BODKIN) writes:
>In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>>Right on!  The function prototypes are just stupid.  I shouldn't have to go:
>
>>    extern gore	*good(const char *vomit);

Speaking of which, qsort() requires a comparison routine declared as

	int sort_function( const void *a , const void *b )

Is there a convenient way to write such functions without using a cast for
every reference to a or b?  Copying their values into local pointers of the
appropriate type seems a kludge.  I suppose I could avoid #including stdlib.h,
and declare the arguments to be whatever type I liked.

                             - Jim Van Zandt

gwyn@smoke.brl.mil (Doug Gwyn) (11/29/90)

In article <126872@linus.mitre.org> jrv@sdimax2.mitre.org (VanZandt) writes:
>Is there a convenient way to write such functions without using a cast for
>every reference to a or b?  Copying their values into local pointers of the
>appropriate type seems a kludge.  I suppose I could avoid #including stdlib.h,
>and declare the arguments to be whatever type I liked.

If you don't declare the argument types properly, your code will not be
strictly conforming.  The only "cheat" that is allowed is to use char*
instead of void*, because the standard required these to have the same
representation.  This is NOT required for other pointer types, just for
these two.

The three obvious solutions are:
	explicit cast on each usage of the arguments
	use a macro to encapsulate the above casting
	use auto variables of the appropriate type

dswartz@bigbootay.sw.stratus.com (01/15/91)

Here's my problem:

I have a program with a couple of procedures which use the varargs
package (for tracing and debugging.)  I have an include file which
defines prototypes for all of the procedures in the source file.
The problem is that to make the calling references happy, I seem to
need to use the syntax "void foobar (char *, ...);"  Unfortunately,
the varargs.h on my system defines the dummy argument as an int and
there is only one such argument, so this disagrees with the prototype.
I have gotten around this by placing all of the varargs routines in
a separate file which doesn't include the prototype include file.
Am I out to lunch, or is there an "official" way to do this?

--

Dan S.