2011-11-22 10 views
23

: C++ FAQ - Miscellaneous technical issues - [39.6] What should be done with macros that need to paste two tokens together?¿Por qué necesito doble capa de indirección para macros? En

es posible que alguien me explique por qué ? Todo lo que leo es créanme, pero simplemente no puedo confiar en algo porque alguien lo dijo.

He probado el enfoque y no puedo encontrar cualquier error que aparece:

#define mymacro(a) int a ## __LINE__ 
mymacro(prefix) = 5; 
mymacro(__LINE__) = 5; 
int test = prefix__LINE__*__LINE____LINE__; // fine 

Así qué necesito para hacerlo de esta manera en su lugar (cita de la página web):

Sin embargo, necesita una doble capa de direccionamiento indirecto cuando usa ##. Básicamente es necesario crear una macro especial para "pegar token" tales como :

#define NAME2(a,b)   NAME2_HIDDEN(a,b) 
#define NAME2_HIDDEN(a,b) a ## b 

confiar en mí - que realmente necesita hacer esto! (Y por favor, nadie me escribe diciendo que a veces funciona sin la segunda capa de indirección Trate concatenación de un símbolo con __ LINE__ y ver lo que sucede a continuación..)

Editar: Podría alguien explicar también por qué se utiliza NAME2_HIDDEN antes de su declaración a continuación? Parece más lógico definir macro NAME2_HIDDEN antes de usarlo. ¿Es algún tipo de truco aquí?

+0

No estoy seguro de entender lo que está pidiendo ... –

+0

veo es un poco claro, voy a editar. – Rookie

+0

@tenfour, hecho. ¿podría saber la respuesta a mi parte de edición? – Rookie

Respuesta

29

La parte pertinente de la especificación C:

6.10.3.1 sustitución Argumento

Después de los argumentos de la invocación de un macro-función como han sido identificados, sustitución argumento tiene lugar. Un parámetro en la lista de reemplazo, a menos que haya precedido con un token de preprocesamiento # o ## o seguido de un token de preprocesamiento ## (ver a continuación), es reemplazado por el argumento correspondiente después de que todas las macros contenidas en él hayan sido expandidas. Antes de ser sustituidos, los tokens de preprocesamiento de cada argumento son completamente macro reemplazados como si formaran el resto del archivo de preprocesamiento; no hay otros tokens de preprocesamiento disponibles.

La parte clave que determina si desea que el indirecto doble o no es la segunda frase y la excepción en él - si el parámetro está involucrado en una operación de # o ## (como los parametros en mymacro y NAME2_HIDDEN), entonces cualquier otra macro en el argumento NO se expande antes de hacer # o ##.Si, por otro lado, no hay # o ## INMEDIATAMENTE en el cuerpo de la macro (como con NAME2), entonces se amplían otras macros en los parámetros.

Así que todo se reduce a lo que usted quiere - a veces desea que todas las macros de una primera ampliación y, a continuación, hacer lo # o ## (en cuyo caso se desea que el direccionamiento indirecto doble capa) y en algún momento usted no quiere que las macros expandieron primero (en cuyo caso NO PUEDE TENER macros de doble capa, debe hacerlo directamente).

+1

de hecho. Me pareció muy inquietante el sitio web me dijo que SIEMPRE lo use ... mientras que en mi caso, nunca quiero usarlo de esa manera. – Rookie

4

__LINE__ es una macro especial que se supone que debe resolverse con el número de línea actual. Sin embargo, al hacer un token pegado con __LINE__ directamente, no tiene posibilidad de resolverse, por lo que termina con el token prefix__LINE__ en lugar de, digamos, prefix23, como probablemente estaría esperando si escribiera este código en lo salvaje.

+0

edit: oh espera, ¿quiere decir que alguien esperaría dar un número de línea en el nombre de la variable? hmm bien ¿Es eso todos los problemas que causará? – Rookie

+0

¿podría explicar también por qué ha utilizado las macros en un orden "incorrecto"? p.ej. se refiere a la macro 'NAME2_HIDDEN' antes de que se declare siquiera. ¿Es eso una buena práctica, o es algún tipo de truco? Probé de ambas maneras y parezco obtener los mismos resultados. – Rookie

+0

Puede definir macros en cualquier orden, ninguno de los dos es incorrecto. –

3

Chris Dodd tiene una excelente explicación para la primera parte de su pregunta. En cuanto a la segunda parte, sobre la secuencia de definición, la versión corta es que las directivas #define por sí mismas no se evalúan en absoluto; solo se evalúan y expanden cuando el símbolo se encuentra en otra parte del archivo. Por ejemplo:

#define A a //adds A->a to the symbol table 
#define B b //adds B->b to the symbol table 

int A; 

#undef A  //removes A->a from the symbol table 
#define A B //adds A->B to the symbol table 

int A; 

La primera int A; convierte int a; porque así es como se define A en ese punto en el archivo. El segundo int A; se convierte en int b; después de dos expansiones. Primero se expande a int B; porque A se define como B en ese punto del archivo. El preprocesador luego reconoce que B es una macro cuando verifica la tabla de símbolos. B luego se expande a b.

Lo único que importa es la definición del símbolo en el punto de expansión, independientemente de dónde esté la definición.

+1

oh sí, pensé que sería más lógico ponerlos en ese orden donde los usa, de alguna manera tiene más sentido para mí. gracias por la explicación. – Rookie

1

El orden en que se declaran las macros no es importante, el orden en el que se utilizan es. Si tuvieras que usar esa macro antes de que se declarara (en el código actual, es decir, no en una macro que permanece latente hasta que se invoque), entonces obtendrías un error, pero como la mayoría de las personas sanas no van por ahí este tipo de cosas, escribir una macro y luego escribir una función que utiliza una macro aún no definida más abajo, etc., etc ... Parece que su pregunta no es solo una pregunta sino que responderé esa parte. Creo que deberías haber roto esto un poco más.

0

La respuesta más no técnica, que reuní de todos los enlaces aquí, y el enlace de enlaces;) es que, una indirección de una sola capa macro(x) #x stringifica el nombre de la macro ingresada, pero al usar capas dobles, valor de macro.

#define valueOfPi 3 
#define macroHlp(x) #x 
#define macro(x) macroHlp(x) 
#define myVarOneLayer "Apprx. value of pi = " macroHlp(valueOfPi) 
#define myVarTwoLayers "Apprx. value of pi = " macro(valueOfPi) 

printf(myVarOneLayer); // out: Apprx. value of pi = valueOfPi 
printf(myVarOTwoLayers); // out: Apprx. value of pi = 3 

¿Qué ocurre en printf(myVarOneLayer)

printf(myVarOneLayer) se expande a printf("Apprx. value of pi = " macroHlp(valueOfPi))

macroHlp(valueOfPi) intentos para stringify la entrada, la entrada en sí no se evalúa. Su único propósito en la vida es tomar una entrada y stringify. Por lo tanto, se expande para "valueOfPi"

Por lo tanto, lo que sucede en printf(myVarTwoLayers)

printf(myVarTwoLayers) se expande a printf("Apprx. value of pi = " macro(valueOfPi)

macro(valueOfPi) tiene ninguna operación stringification, es decir, no hay #x en su expansión, pero hay una x, por lo tiene que evaluar x e ingresar el valor a macroHlp para la stringificación.Se expande a macroHlp(3) que a su vez stringify el número 3, ya que está utilizando #x

Cuestiones relacionadas