Який сенс макросу PROTOTYPE, який просто розширюється до своїх аргументів?


82

У мене є файл заголовка, який містить

#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 ++. Можливо, це має щось спільне?


6
Чи є поряд директиви #if/ #ifdef/ #ifndef/, #elseде натомість воно може мати інше визначення? Це може мати значення при використанні в інших макросах, особливо поблизу #або ##. Це може бути просто для стилю коментування. Недостатньо контексту, щоб справді відповісти.
aschepler

Як загальна відповідь: тому що хтось може мати причину хотіти змінитися PROTOTYPE. Якщо ви бачите дивні дефініції в коді, які здаються марними, подумайте про потенційну гнучкість, якщо хтось хотів щось зручно змінити.
Apollys підтримує Моніку

Відповіді:


130

Ще в давнину справді, дуже раннього С, не було такого поняття, як прототип. Списки аргументів функції з’являються після дужок функції, наприклад :

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)

10
Ого. Вибух з минулого. Однією з моїх перших робіт у галузі програмного забезпечення було вилучення цих матеріалів із існуючої кодової бази. Виховували здорову повагу до масиву однолінійних програм, що мутують текст, в одній лінії.
user4581301 04

15
Я не зрозуміти , як працюють ці визначає, як же #define PROTOTYPE(s)з входом int x;перетворений x? Мені здається, що він обертається порожнім рядком
Феррібіг,

3
@Ferrybig - вибачте, я переплутав речі. Це прототип, який визначається таким чином. У K&R C прототип не мав аргументів, а в ANSI C він має стиль списку аргументів, який ми звикли бачити.
Піт Беккер,

1
Цю практику можна побачити також у zlib.hзаголовку з бібліотеки zlib з OF()макросом: github.com/madler/zlib/blob/master/zlib.h
Paul Belanger

@PaulBelanger Фактичне визначення в zconf.h.
SS Anne

16

Такі макроси використовуватимуться у прототипах у заголовковому файлі, щоб дозволити щось подібне:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.