82

I have a header file which contains

#define PROTOTYPE(s) s

What is the point of that? Seems like it would just replace the input with itself.

There are TONS of other directives around it, but the only one that appears to have any bearing just checked if it's defined: #ifndef PROTOTYPE. I found some places in HDF4 header files that do this: #define PROTOTYPE. So, none of that really clear up my question. Still seems pretty useless.

Here's how it's used:

CS_RETCODE clientmsg_callback PROTOTYPE((
CS_CONTEXT * context,
CS_CONNECTION *connection,
CS_CLIENTMSG *clientmsg));

This is part of a project which uses Sybase Open Client. clientmsg_callback is later used here:

ct_callback(context, NULL, CS_SET, CS_CLIENTMSG_CB,
                  (CS_VOID *)clientmsg_callback);

I'm going off of a sample program from here:

http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc35570.1570/html/clcprgde/clcprgde10.htm

clientmsg_callback is implemented later. I think the sample was originally written with C in mind, instead of C++. Perhaps that has something to do with it?

2
  • 6
    Are there nearby #if / #ifdef / #ifndef / #else directives where it might have a different definition instead? It could make a difference when used in other macros, especially near # or ##. It could be just for a commenting style. Not enough context to really answer.
    – aschepler
    Sep 3, 2019 at 21:53
  • As a general answer: because someone might have a reason to want to change PROTOTYPE. If you see weird defines in code that seem useless, think about potential flexibility if someone wanted to change something conveniently. Sep 5, 2019 at 0:42

2 Answers 2

133

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)
6
  • 10
    Wow. Blast from the past. One of my first jobs in software was stripping this stuff out out of an existing code base. Built up a healthy respect for Unix's array of one-liner text-mutating programs. Sep 4, 2019 at 0:32
  • 15
    I'm not understanding how those defines work, how does #define PROTOTYPE(s) with the input int x; gets turned into x? It looks like it wraps to an empty string to me
    – Ferrybig
    Sep 4, 2019 at 8:17
  • 3
    @Ferrybig -- sorry, I confused things. It's the prototype that gets defined this way. In K&R C, a prototype had no arguments, and in ANSI C it has the style of argument list that we're used to seeing. Sep 4, 2019 at 13:15
  • 1
    This practice can also be seen in the zlib.h header from the zlib library with the OF() macro: github.com/madler/zlib/blob/master/zlib.h Sep 4, 2019 at 15:46
  • @PaulBelanger The actual definition is in zconf.h.
    – S.S. Anne
    Sep 4, 2019 at 16:00
16

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.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.