Estoy tratando de escribir un programa donde los nombres de algunas funciones dependen del valor de una determinada variable macro con una macro como esta:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
Desafortunadamente, la macro NAME()
convierte eso en
int some_function_VARIABLE(int a);
más bien que
int some_function_3(int a);
así que esta es claramente la forma incorrecta de hacerlo. Afortunadamente, el número de diferentes valores posibles para VARIABLE es pequeño, así que simplemente puedo hacer #if VARIABLE == n
y enumerar todos los casos por separado, pero me preguntaba si hay una manera inteligente de hacerlo.
#define A 0 \n #define M a ## A
: tener dos##
no es la clave.Respuestas:
Preprocesador estándar C
Dos niveles de indirección.
En un comentario a otra respuesta, Cade Roux preguntó por qué esto necesita dos niveles de indirección. La respuesta impertinente es porque así es como el estándar requiere que funcione; tiende a encontrar que necesita el truco equivalente con el operador de encordado también.
La Sección 6.10.3 de la norma C99 cubre el 'reemplazo de macros' y 6.10.3.1 cubre la 'sustitución de argumentos'.
En la invocación
NAME(mine)
, el argumento es 'mío'; está completamente expandido a 'mío'; luego se sustituye en la cadena de reemplazo:Ahora se descubre el macro EVALUATOR, y los argumentos se aíslan como 'mío' y 'VARIABLE'; este último se expande completamente a '3' y se sustituye en la cadena de reemplazo:
El funcionamiento de esto está cubierto por otras reglas (6.10.3.3 'El operador ##'):
Entonces, la lista de reemplazo contiene
x
seguido por##
y también##
seguido pory
; entonces tenemos:y eliminar las
##
fichas y concatenar las fichas en ambos lados combina 'mina' con '_' y '3' para obtener:Este es el resultado deseado.
Si miramos la pregunta original, el código fue (adaptado para usar 'mine' en lugar de 'some_function'):
El argumento para NAME es claramente 'mío' y eso se expande completamente.
Siguiendo las reglas de 6.10.3.3, encontramos:
que, cuando
##
se eliminan los operadores, se asigna a:exactamente como se informa en la pregunta.
Preprocesador C tradicional
Robert Rüger pregunta :
Tal vez, y tal vez no, depende del preprocesador. Una de las ventajas del preprocesador estándar es que tiene esta instalación que funciona de manera confiable, mientras que hubo diferentes implementaciones para preprocesadores pre-estándar. Un requisito es que cuando el preprocesador reemplaza un comentario, no genera un espacio como debe hacer el preprocesador ANSI. El preprocesador GCC (6.3.0) C cumple con este requisito; el preprocesador Clang de XCode 8.2.1 no lo hace.
Cuando funciona, esto hace el trabajo (
x-paste.c
):Tenga en cuenta que no hay un espacio entre
fun,
yVARIABLE
, eso es importante porque, si está presente, se copia en la salida, y termina conmine_ 3
el nombre, que no es sintácticamente válido, por supuesto. (Ahora, por favor, ¿puedo tener mi cabello hacia atrás?)Con GCC 6.3.0 (en ejecución
cpp -traditional x-paste.c
), obtengo:Con Clang de XCode 8.2.1, obtengo:
Esos espacios lo estropean todo. Observo que ambos preprocesadores son correctos; diferentes preprocesadores pre-estándar exhibieron ambos comportamientos, lo que hizo que el pegado de tokens fuera un proceso extremadamente molesto y poco confiable al intentar portar código. El estándar con la
##
notación simplifica radicalmente eso.Puede haber otras formas de hacer esto. Sin embargo, esto no funciona:
GCC genera:
Cerca, pero sin dados. YMMV, por supuesto, dependiendo del preprocesador pre-estándar que esté usando. Francamente, si está atascado con un preprocesador que no está cooperando, probablemente sería más sencillo organizar el uso de un preprocesador C estándar en lugar del preprocesador (generalmente hay una manera de configurar el compilador de manera adecuada) que Pasar mucho tiempo tratando de encontrar una manera de hacer el trabajo.
fuente
cpp -traditional
. Tenga en cuenta que no hay una respuesta definitiva, depende del preprocesador que tenga.Honestamente, no quieres saber por qué funciona esto. Si sabes por qué funciona, te convertirás en ese tipo en el trabajo que sabe este tipo de cosas, y todos vendrán a hacerte preguntas. =)
Editar: si realmente quieres saber por qué funciona, felizmente publicaré una explicación, suponiendo que nadie me gane.
fuente