What's the point of a PROTOTYPE macro that merely expands to its arguments?

Back in the olden days of really, really early C, there was no such thing as a prototype. Function argument lists came after the function's parentheses, like this:

square(x)
int x;
{
int y = x * x;
return y;
}

These days, of course, the arguments go inside the parentheses:

square(int x)
{
int y = x * x;
return y;
}

Note the "missing" return type; C functions used to implicitly return int, and it was only if you needed a different return type that you had to say what it was.

Function declarations had yet another set of rules. A function declaration in K&R C (the ancient version) had no arguments:

int square();

And function prototypes in ANSI C have a list of arguments:

int square(int x);

During the transition, people used wacky macros so they could compile both ways:

int square(PROTOTYPE(int x));

With

#define PROTOTYPE(s)

that would expand to the first version.

With

#define PROTOTYPE(s) s

it would expand to the second.

With regard to the "extra" parentheses in the code in the question, they're needed when there is more than one argument in the argument list. Without them, the macro invocation has more than one argument, so won't match a macro defined with just one argument:

PROTOTYPE(int x, int y)   // error: too many arguments
PROTOTYPE((int x, int y)) // ok: only one argument (enclosed in parentheses)

Macros like this would be used in the prototypes in the header file to allow something like this:

int foo PROTOTYPE((int bar));

If ANSI C was detected (__STDC__ defined as 1), this would expand to:

int foo(int bar);

If ANSI C was not detected, this would expand to:

int foo();

which was common before C was standardized.

Some libraries still do this; if you look in tcpd.h (if you have it available), you'll see:

/* someone else may have defined this */
#undef  __P

/* use prototypes if we have an ANSI C compiler or are using C++ */
#if defined(__STDC__) || defined(__cplusplus)
#define __P(args)       args
#else
#define __P(args)       ()
#endif

This explains it well.

As for the double parentheses, __P(arg1, arg2) would give a syntax error (passing too many arguments to the macro), while __P((arg1, arg2)) would be fine (just one enclosed in parentheses).

This is similar to __extension__((...)) in GNU C. In non-GNU compilers, simply #define __extension__(unused) to have semi-portable code, as just one "argument" is given, wrapped in parentheses.

Tags:

C++

Macros