web-dev-qa-db-fra.com

Quel est l'intérêt d'une macro PROTOTYPE qui se développe simplement à ses arguments?

J'ai un fichier d'en-tête qui contient

#define PROTOTYPE(s) s

Quel est le but de ça? On dirait que cela remplacerait simplement l'entrée par elle-même.

Il y a des tonnes d'autres directives autour d'elle, mais la seule qui semble avoir un roulement vient de vérifier si elle est définie: #ifndef PROTOTYPE. J'ai trouvé des endroits dans les fichiers d'en-tête HDF4 qui font cela: #define PROTOTYPE. Donc, rien de tout cela ne clarifie vraiment ma question. Semble encore assez inutile.

Voici comment il est utilisé:

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

Cela fait partie d'un projet qui utilise Sybase Open Client. clientmsg_callback est utilisé plus tard ici:

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

Je vais partir d'un exemple de programme d'ici:

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

clientmsg_callback est implémenté ultérieurement. Je pense que l'exemple a été écrit à l'origine avec C à l'esprit, au lieu de C++. Peut-être que cela a quelque chose à voir avec ça?

81
Charlie Elverson

Dans les temps anciens du très, très tôt C, il n'existait pas de prototype. Les listes d'arguments de fonction sont apparues après les parenthèses de la fonction, comme ceci :

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

Ces jours-ci, bien sûr, les arguments vont entre parenthèses:

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

Notez le type de retour "manquant"; Les fonctions C renvoyaient implicitement int, et ce n'était que si vous aviez besoin d'un type de retour différent que vous deviez dire de quoi il s'agissait.

Les déclarations de fonction avaient encore un autre ensemble de règles. Une déclaration de fonction dans K&R C (la version ancienne) n'avait aucun argument:

int square();

Et les prototypes de fonction en ANSI C ont une liste d'arguments:

int square(int x);

Pendant la transition, les gens ont utilisé des macros farfelues pour pouvoir compiler dans les deux sens:

int square(PROTOTYPE(int x));

Avec

#define PROTOTYPE(s)

qui s'étendrait à la première version.

Avec

#define PROTOTYPE(s) s

il s'étendrait au second.

En ce qui concerne les parenthèses "supplémentaires" dans le code de la question, elles sont nécessaires lorsqu'il y a plus d'un argument dans la liste d'arguments. Sans eux, l'invocation de macro a plus d'un argument, donc ne correspondra pas à une macro définie avec un seul argument:

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

Des macros comme celle-ci seraient utilisées dans les prototypes du fichier d'en-tête pour permettre quelque chose comme ceci:

int foo PROTOTYPE((int bar));

Si ANSI C a été détecté (__STDC__ Défini comme 1), cela s'étendrait à:

int foo(int bar);

Si ANSI C n'a pas été détecté, cela s'étendrait à:

int foo();

ce qui était courant avant que C ne soit normalisé.

Certaines bibliothèques le font encore; si vous regardez dans tcpd.h (si vous l'avez disponible), vous verrez:

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

Cela l'explique bien.

Quant aux doubles parenthèses, __P(arg1, arg2) donnerait une erreur de syntaxe (en passant trop d'arguments à la macro), tandis que __P((arg1, arg2)) serait bien (une seule entre parenthèses).

Ceci est similaire à __extension__((...)) dans GNU C. Dans les compilateurs non GNU, simplement #define __extension__(unused) pour avoir du code semi-portable, comme un seul argument " "est donné, entouré de parenthèses.

15
S.S. Anne