[comp.lang.c] ANSI/Non-ANIS Function Declaration Macros

scs@itivax.iti.org (Steve Simmons) (12/08/89)

Currently I'm writing code that must compile with and without ANSI
C features.  Ideally this could be done with no slgging the code
with lots of #ifdef constructs like

#ifdef __STDC__
int	*foo( int const a )
#else
int	*foo( a )
int	a ;
#endif
{
	func body
}

While this works, it's got a weakness.  In having two separate declarations,
one can over time let them get out of sync.  It's also a pain to write.
Has anybody come up with a way of doing this so that I write the declarations
once and once only?  Bizarre constructs considered within limits.
-- 
Steve Simmons	       scs@iti.org         Industrial Technology Institute
	'"You're not a big name on Usenet until someone puts
	  you in their .sig file."		-- Anonymous'

gwc@root.co.uk (Geoff Clare) (12/12/89)

In article <4603@itivax.iti.org> scs@itivax.iti.org (Steve Simmons) writes:
>
>Currently I'm writing code that must compile with and without ANSI
>C features.  Ideally this could be done with no slgging the code
>with lots of #ifdef constructs like
>
>#ifdef __STDC__
>int	*foo( int const a )
>#else
>int	*foo( a )
>int	a ;
>#endif
>{
>	func body
>}
>
>While this works, it's got a weakness.  In having two separate declarations,
>one can over time let them get out of sync.  It's also a pain to write.
>Has anybody come up with a way of doing this so that I write the declarations
>once and once only?  Bizarre constructs considered within limits.

My recommendation is that you do the following:

	#ifndef __STDC__
	#define const
	#endif

	int *foo(a)
	const int a;

Old style declarations are accepted equally well by ANSI compilers
as 'common C' ones.  The only thing that needs to be dependent on
__STDC__ being defined is the 'const' keyword.

"But what do I do about prototypes?" I hear you ask.

My answer is the same: just stick to old-style 'extern' declarations.
The advantage of using prototypes is that you don't need to worry
about casting function arguments correctly all the time.  But if
your code must compile with non-ANSI compilers, you're going to have
to cast all the arguments anyway - so the prototypes are redundant.

-- 
Geoff Clare, UniSoft Limited, Saunderson House, Hayne Street, London EC1A 9HH
gwc@root.co.uk  (Dumb mailers: ...!uunet!root.co.uk!gwc)  Tel: +44-1-315-6600

martin@mwtech.UUCP (Martin Weitzel) (12/16/89)

In article <4603@itivax.iti.org> scs@itivax.iti.org (Steve Simmons) writes:
>
>Currently I'm writing code that must compile with and without ANSI
>C features.  Ideally this could be done with no slgging the code
>with lots of #ifdef constructs like
>
>#ifdef __STDC__
>int	*foo( int const a )
>#else
>int	*foo( a )
>int	a ;
>#endif
>{
>	func body
>}
>
>While this works, it's got a weakness.  In having two separate declarations,
>one can over time let them get out of sync.  It's also a pain to write.

It's a pain to write, true, but the chance of getting 'out-of-sync' is
small, because the two declarations are near by each other.

>Has anybody come up with a way of doing this so that I write the declarations
>once and once only?  Bizarre constructs considered within limits.

If your concern isn't to convert any arbitrary C-source from K&R-Style
to prototypes, but only two write "compatible" code yourself, it's
easy to stick to some formatting conventions and do the conversions with
a tool like 'awk' or 'lex' ('sed' might not be sufficient ...).

To show an appropiate formatting convention, I expand the example a
little:
	/* ... only for decoration ... */
	typedef (*FPI)();

	/* ... the real stuff ... */
	int *foo
	(
		const int a,	/* you may want to use */
		char **cp,	/* small comments */
		FPI ptr		/* after the params */
	)

These are ANSI-Prototypes and when converting to K&R-Style using
'awk' a general strategie could be to setup a special context between
lines containing '(' and ')' as first character. (IMHO, it's not
so hard, to avoid such lines in all other parts of the source.)
When in this context, you retard the lines read (easy with an
associative array) extract the parameter-names (more or less
elegant, depending on how familiar you are with 'awk' and how much
of formatting convention you want to put on your source(++).)
and finally output the collected parameters in the old style
before you output the retarded lines, deleting the first '(',
changing embedded kommas and the final ')' to semicolons.

(++) It's *very* easy, if you *allways* use defined types and
separate the kommas with white space, but I think the above
example wouldn't be too hard to do and is less in danger, to
write a declaration wrong by accident. Using defined types
for pointers to functions and the more complex types helps
much - for your tool and for the later reader ... :-)

Such a tool could also automatically produce 'interface files'
for inclusion in any compilation unit, which wants to use foo().
(This last idea of automatically generating interface files
was freely stolen from 'Portable UNIX and C Programming', 
J.E. Lapin, ISBN 0-13-686494-5.)

In article <1066@root44.co.uk> gwc@root.co.uk (Geoff Clare) writes:
>
>My recommendation is that you do the following:
>
>	#ifndef __STDC__
>	#define const
>	#endif
>
>	int *foo(a)
>	const int a;
>
>Old style declarations are accepted equally well by ANSI compilers
>as 'common C' ones.  The only thing that needs to be dependent on
>__STDC__ being defined is the 'const' keyword.

Yes, I once recommended this solution, but then I stumbled over
this compiler, who told :

	"const can not be redefined, because it will
	become a reserved word in future releases"	:-(
	
I think the guys who wrote that did not know the standard, because
during preprocessing 'const' would *not* be a reserved word.
(Question to all ANSI experts on the net: Am I right?) 
As a workaround for such "clever" compilers, you need to define
an 'intermediate' value, say CONST:

	#ifndef __STDC__
	#define CONST
	#else
	#define CONST const
 	#endif
 
 	int *foo(a)
 	CONST int a;
>
>"But what do I do about prototypes?" I hear you ask.
>
>My answer is the same: just stick to old-style 'extern' declarations.
>The advantage of using prototypes is that you don't need to worry
>about casting function arguments correctly all the time.  But if
>your code must compile with non-ANSI compilers, you're going to have
>to cast all the arguments anyway - so the prototypes are redundant.

IMHO wrong. Some compilers may not do any argument type checking with
'old-style'-declarations. Automatic prototyping in these cases is only
an option, not an obligation (Again question to all ANSI experts:
Am I right? I'm not quite sure.) 

As the author of the original posting allready mentioned, one of
his fears is to get 'out-of-sync' in case of changes. With explicit
casting, he's more in danger to run into that situation, but you
are right, with non-ANSI compilers, this danger allways exists.

-- 
<<< MW -- email: see header -- voice: 49-(0)6151-6 56 83 >>>