Quiero crear una macro en C que cree una función con un nombre basado en el número de línea. Pensé que podría hacer algo como (la función real tendría declaraciones entre llaves):
#define UNIQUE static void Unique_##__LINE__(void) {}
Que esperaba que se expandiera a algo como:
static void Unique_23(void) {}
Eso no funciona. Con la concatenación de tokens, las macros de posicionamiento se tratan literalmente y terminan expandiéndose a:
static void Unique___LINE__(void) {}
¿Es posible hacerlo?
(Sí, hay una razón real por la que quiero hacer esto, sin importar cuán inútil parezca).
c
macros
concatenation
token
DD.
fuente
fuente
__LINE__
(aunque ese es un caso de uso común.Respuestas:
El problema es que cuando tiene un reemplazo de macro, el preprocesador solo expandirá las macros de forma recursiva si no se le aplica el operador de encadenamiento
#
ni el de pegar token##
. Por lo tanto, debe usar algunas capas adicionales de indirección, puede usar el operador de pegado de tokens con un argumento expandido de forma recursiva:A continuación,
__LINE__
obtiene expandió al número de línea durante la expansiónUNIQUE
(ya que no está involucrado, ya sea con#
o##
), y luego el símbolo pegar sucede durante la expansión deTOKENPASTE
.También debe tenerse en cuenta que también existe la
__COUNTER__
macro, que se expande a un nuevo entero cada vez que se evalúa, en caso de que necesite tener múltiples instancias de laUNIQUE
macro en la misma línea. Nota:__COUNTER__
es compatible con MS Visual Studio, GCC (desde V4.3) y Clang, pero no es estándar C.fuente
__COUNTER__
macro no me funcionó en gcc; aunque el__LINE__
uno funcionó como se anuncia.GCC no requiere "envolver" (o darse cuenta) a menos que el resultado necesite ser "encadenado". Gcc tiene características, pero TODAS se pueden hacer con la versión 1 de C simple (y algunos argumentan que Berkeley 4.3 C es mucho más rápido que vale la pena aprender a usarlo).
** Clang (llvm) NO HACE EL ESPACIO EN BLANCO CORRECTAMENTE para la expansión macro; agrega espacios en blanco (que ciertamente destruye el resultado como un identificador C para un procesamiento previo adicional) **, clang simplemente no hace # o * expansión macro como un preprocesador de C durante décadas. El ejemplo principal es compilar X11, la macro "Concat3" está rota, su resultado ahora es Identificador C MISNAMED, que por supuesto no se puede construir. y estoy empezando a pensar que los fallos de construcción son su profesión.
Creo que la respuesta aquí es "la nueva C que rompe los estándares es mala C", estos hacks siempre eligen (aplastar los espacios de nombres), cambian los valores predeterminados sin ninguna razón, pero en realidad no "mejoran C" (excepto por lo que ellos dicen: que yo digamos que es un artilugio hecho para explicar por qué se salen con la suya con todas las roturas de las que nadie los ha hecho responsables).
No es un problema que los preprocesadores de C anteriores no admitieran UNIq_ () __ porque admitían #pragma, lo que permite que "la piratería de la marca del compilador en el código se marque como piratería" y también funcione igual de bien SIN afectar los estándares: simplemente como cambiar los valores predeterminados es una rotura inútil de wonton, y al igual que cambiar lo que hace una función mientras se usa el mismo nombre (espacio de nombres) es ... malware en mi opinión
fuente