83

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?

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
Charlie Elverson
  • 1,180
  • 8
  • 18
  • 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 03 '19 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. – Apollys supports Monica Sep 05 '19 at 00:42

2 Answers2

132

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)
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • 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. – user4581301 Sep 04 '19 at 00: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 04 '19 at 08: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. – Pete Becker Sep 04 '19 at 13:15
  • 1
    This practice can also be seen in the `zlib.h` header from the zlib library with the `OF()` macro: https://github.com/madler/zlib/blob/master/zlib.h – Paul Belanger Sep 04 '19 at 15:46
  • @PaulBelanger The actual definition is in `zconf.h`. – S.S. Anne Sep 04 '19 at 16:00
  • "Back in the olden days of really, really early C" yeah, and modern linux kernel ... – bolov Sep 27 '19 at 12:03
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.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76