¿Cuál es el punto de una macro PROTOTIPO que simplemente se expande a sus argumentos?

82

Tengo un archivo de encabezado que contiene

#define PROTOTYPE(s) s

¿Cuál es el punto de esto? Parece que simplemente reemplazaría la entrada por sí mismo.

Hay un montón de otras directivas a su alrededor, pero el único que parece tener alguna relación acaba de comprobar si se define: #ifndef PROTOTYPE. He encontrado algunos lugares en los archivos de cabecera HDF4 que hacen esto: #define PROTOTYPE. Entonces, nada de eso realmente aclara mi pregunta. Todavía parece bastante inútil.

Así es como se usa:

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

Esto es parte de un proyecto que utiliza Sybase Open Client. clientmsg_callback se usa más tarde aquí:

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

Salgo de un programa de muestra de aquí:

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

clientmsg_callback se implementa más tarde. Creo que la muestra se escribió originalmente con C en mente, en lugar de C ++. ¿Quizás eso tenga algo que ver con eso?

Charlie Elverson
fuente
6
¿Hay directivas #if/ #ifdef/ #ifndef/ cercanas #elseen las que podría tener una definición diferente? Podría marcar la diferencia cuando se usa en otras macros, especialmente cerca #o ##. Podría ser solo para un estilo de comentario. No hay suficiente contexto para responder realmente.
aschepler
Como respuesta general: porque alguien podría tener una razón para querer cambiar PROTOTYPE. Si ve definiciones extrañas en el código que parecen inútiles, piense en la flexibilidad potencial si alguien quisiera cambiar algo convenientemente.
Apollys apoya a Monica

Respuestas:

130

En los viejos tiempos de los primeros C realmente, no existían los prototipos. Las listas de argumentos de la función venían después de los paréntesis de la función, así :

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

En estos días, por supuesto, los argumentos van entre paréntesis:

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

Tenga en cuenta el tipo de retorno "faltante"; Las funciones de C solían retornar implícitamente int, y solo si necesitaba un tipo de retorno diferente tenía que decir cuál era.

Las declaraciones de funciones tenían otro conjunto de reglas. Una declaración de función en K&R C (la versión antigua) no tenía argumentos:

int square();

Y los prototipos de funciones en ANSI C tienen una lista de argumentos:

int square(int x);

Durante la transición, las personas usaron macros extravagantes para poder compilar en ambos sentidos:

int square(PROTOTYPE(int x));

Con

#define PROTOTYPE(s)

que se expandiría a la primera versión.

Con

#define PROTOTYPE(s) s

se expandiría al segundo.

Con respecto a los paréntesis "extra" en el código de la pregunta, son necesarios cuando hay más de un argumento en la lista de argumentos. Sin ellos, la invocación de la macro tiene más de un argumento, por lo que no coincidirá con una macro definida con un solo argumento:

PROTOTYPE(int x, int y)   // error: too many arguments
PROTOTYPE((int x, int y)) // ok: only one argument (enclosed in parentheses)
Pete Becker
fuente
10
Guau. Explosión del pasado. Uno de mis primeros trabajos en software fue eliminar estas cosas de una base de código existente. Construyó un respeto saludable por la variedad de programas de mutación de texto de una sola línea de Unix.
user4581301
15
No entiendo cómo funcionan esas definiciones, ¿cómo se convierte #define PROTOTYPE(s)la entrada ? Me parece que se envuelve en una cuerda vacíaint x;x
Ferrybig
3
@Ferrybig - lo siento, confundí las cosas. Es el prototipo el que se define de esta manera. En K&R C, un prototipo no tenía argumentos, y en ANSI C tiene el estilo de lista de argumentos que estamos acostumbrados a ver.
Pete Becker
1
Esta práctica también se puede ver en el zlib.hencabezado de la biblioteca zlib con la OF()macro: github.com/madler/zlib/blob/master/zlib.h
Paul Belanger
@PaulBelanger La definición real está en zconf.h.
SS Anne
16

Macros como esta se usarían en los prototipos en el archivo de encabezado para permitir algo como esto:

int foo PROTOTYPE((int bar));

Si se detectara ANSI C ( __STDC__definido como 1), esto se expandiría a:

int foo(int bar);

Si no se detecta ANSI C, esto se expandiría a:

int foo();

que era común antes de que C se estandarizara.

Algunas bibliotecas todavía hacen esto; si miras tcpd.h(si lo tienes disponible), verás:

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

Esto lo explica bien.

En cuanto al doble paréntesis, __P(arg1, arg2)daría un error de sintaxis (pasando demasiados argumentos a la macro), mientras __P((arg1, arg2))que estaría bien (solo uno entre paréntesis).

Esto es similar a __extension__((...))GNU C. En compiladores que no son GNU, simplemente #define __extension__(unused)para tener un código semiportátil, ya que solo se da un "argumento", entre paréntesis.

SS Anne
fuente