Какой смысл в макросе PROTOTYPE, который просто расширяет свои аргументы?

У меня есть заголовочный файл, который содержит

#define PROTOTYPE(s) s

Какой смысл в этом? Похоже, он просто заменит вход сам по себе.

Есть множество других директив вокруг него, но единственная, которая, кажется, имеет какой-либо подшипник, только что проверенный, если он определил: #ifndef PROTOTYPE. Я нашел несколько мест в заголовочных файлах HDF4, которые делают это: #define PROTOTYPE. Так что ничего из этого не прояснило мой вопрос. Все еще кажется довольно бесполезным.

Вот как это используется:

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

Это часть проекта, в котором используется Sybase Open Client. clientmsg_callback позже используется здесь:

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

Я ухожу отсюда с примера программы:

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

clientmsg_callback будет реализован позже. Я думаю, что образец был изначально написан с учетом C, а не C++. Возможно, это как-то связано с этим?

Ответ 1

В древние времена действительно, очень раннего C не было такого понятия, как прототип. Списки аргументов функций следуют после скобок функций, вот так:

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

В наши дни, конечно, аргументы заключаются в круглые скобки:

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

Обратите внимание на "отсутствующий" тип возвращаемого значения; Функции C раньше неявно возвращали int, и только если вам нужен другой тип возвращаемого значения, вы должны были сказать, что это было.

Функция объявлений имела еще один набор правил. Объявление функции в K & R C (древняя версия) не имеет аргументов:

int square();

И функции прототипов в ANSI C имеют список аргументов:

int square(int x);

Во время перехода люди использовали дурацкие макросы, чтобы они могли скомпилировать оба пути:

int square(PROTOTYPE(int x));

С

#define PROTOTYPE(s)

это расширится до первой версии.

С

#define PROTOTYPE(s) s

это расширится до второго.

Что касается "лишних" скобок в коде вопроса, они нужны, когда в списке аргументов больше одного аргумента. Без них вызов макроса имеет более одного аргумента, поэтому он не будет соответствовать макросу, определенному только одним аргументом:

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

Ответ 2

Такие макросы будут использоваться в прототипах в заголовочном файле, чтобы разрешить что-то вроде этого:

int foo PROTOTYPE((int bar));

Если был обнаружен ANSI C (__STDC__ определен как 1), это расширилось бы до:

int foo(int bar);

Если ANSI C не был обнаружен, это расширилось бы до:

int foo();

что было распространено до стандартизации С.

Некоторые библиотеки все еще делают это; если вы посмотрите в tcpd.h (если он у вас есть), вы увидите:

/* 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

Это хорошо объясняет.

Что касается двойных скобок, __P(arg1, arg2) выдаст синтаксическую ошибку (передача слишком большого количества аргументов в макрос), тогда как __P((arg1, arg2)) подойдет (только один, заключенный в скобки).

Это похоже на __extension__((...)) в GNU C. В компиляторах, не относящихся к GNU, просто #define __extension__(unused) должен иметь полупереносимый код, поскольку дается только один "аргумент", заключенный в скобки.