Какой смысл в макросе 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)
должен иметь полупереносимый код, поскольку дается только один "аргумент", заключенный в скобки.