selectively enabling prototypes

Richard Minner rtm at christmas.UUCP
Fri Aug 17 10:10:53 AEST 1990


In article <493 at mtndew.Tustin.CA.US>, Steve Friedl writes:
>
>     I like to use function prototypes when possible, so...
>	#ifdef	USE_PROTO
>	#  define	PROTO(name, args)	name args
>	#else
>	#  define	PROTO(name, args)	name ( )
>	#endif
>...
> However, I have seen it done "the hard way":
>	#ifdef	USE_PROTO
>	  extern int printf(const char *, ... );
>...
> This looks like a real maintenance nightmare, but some of the
> people who do it are people I respect, so I gotta wonder if they
> know something that I don't know on this one.  Are there any
> gotchas on doing it with the flavor of the way I've done it?

First, one caution that some may still not be aware of (even
though it's covered in the FAQ, question 22 :-), which is, briefly,
that with Classic style function declarations and definitions,
args of type char, short and float are (silently) promoted to int
and double, whereas with ANSI style they are not, e.g:

	extern int foo(char c);
	...
	int foo(c)
	char c;

doesn't work.  Those who don't understand should read FAQ, Q #22
(or thereabouts).

So you can't use your PROTO() macro with functions that take
char's, short's or float's as arguments.  Other than that, it
*works* fine (see end of article).  In fact, the "updated version
of the `Indian Hill C Style and Coding Standards'" (which was
posted here a while back, and by the way, thanks!), includes a
`PROTO()' macro similar to yours, like this:

   #ifdef HAVE_ANSI_PROTOTYPES
   #   define PROTO(args)	args
   #else
   #   define PROTO(args)	()
   #endif

I find this much cleaner, because the results look (to me) more
like normal declarations, and because it supports things like
signal() (and I use a fair number of `functions taking functions
as arguments').  It takes advantage of the fact that the only
difference between ANSI prototypes and Classic decls is the
presence of the argument list.

I actually use `P_()', because it's much more cryptic and hard
to read ;-), so signal() would look like:

   extern void ( *signal P_((int sig, void (*func)(int))) ) P_((int));

which is admittedly a bit ugly with all the ()'s, but:

   #ifdef HAVE_ANSI_PROTOTYPES
   extern void (*signal (int sig, void (*func)(int))) (int);
   #else
   extern void (*signal())();
   #endif

isn't much of an improvement.

By the way, don't forget typedefs, e.g:

    typedef int (*func_type) P((int, double, const char *));

As to why some experienced programmers would use the `#if'
approach instead, it's probably because they don't like macros
that alter the syntax of C, which is *not* an unreasonable
position, mind you.  It's your code, you choose.  Just don't be
surprised if someone someday is irritated by it.  I can't imagine
any other reasons to prefer `#if'.

-- 
Richard Minner  || {uunet,sun,well}!island!rtm     (916) 736-1323 ||
                || Island Graphics Corporation     Sacramento, CA ||



More information about the Comp.lang.c mailing list