una todas salida que procesador preprocesamiento preprocesador menciona lenguaje las informatica ifdef funciones funcion forma entrada encuentran directivas directiva define declarar cual constante c++ visual-c++ preprocessor c-preprocessor compiler-bug

c++ - todas - menciona una forma de declarar una constante



Comportamiento estándar del preprocesador C++ (3)

Estoy estudiando el estándar C ++ sobre el comportamiento exacto del preprocesador (necesito implementar algún tipo de preprocesador C ++). Por lo que entiendo, el ejemplo que inventé (para ayudar a mi comprensión) a continuación debería ser válido:

#define dds(x) f(x, #define f(a,b) a+b dds(eoe) su)

Espero que la primera función, como dds(eoe) invocación de macro dds(eoe) se reemplace por f(eoe, (tenga en cuenta la coma dentro de la cadena de reemplazo) que luego se considera como f(eoe,su) cuando se f(eoe,su) la entrada.

Pero una prueba con VC ++ 2010 me dio esto (le dije a VC ++ que sacara el archivo preprocesado):

eoe+et_leoe+et_l su)

Esto es contrario a la intuición y obviamente es incorrecto. ¿Es un error con VC ++ 2010 o mi incomprensión del estándar de C ++? En particular, ¿es incorrecto poner una coma al final de la cadena de reemplazo como lo hice? Mi comprensión de la gramática estándar de C ++ es que cualquier preprocessing-token está permitido allí.

EDITAR:

No tengo GCC u otras versiones de VC ++. ¿Alguien podría ayudarme a verificar con estos compiladores?


A mi entender, no hay nada en las [cpp.subst/rescan] del estándar que haga que lo que haces sea ilegal, y clang y gcc tienen razón al expandirlo como eoe+su , y el MSC (Visual C ++) el comportamiento debe ser reportado como un error.

No pude hacerlo funcionar, pero logré encontrar una fea solución de MSC para usted, utilizando variadics, puede que le resulte útil, o puede que no, pero en cualquier caso es:

#define f(a,b) (a+b #define dds(...) f(__VA_ARGS__)

Se expande como:

(eoe+ su)

Por supuesto, esto no funcionará con gcc y clang .


Bueno, el problema que veo es que el preprocesador hace lo siguiente

ddx (x) se convierte en f (x,

Sin embargo, f (x, también se define (incluso si se define como f (a, b)), entonces f (x, se expande a x + basura.

Entonces ddx (x) finalmente se transforma en x + basura (porque definiste f (smthing,).

Su dds (eoe) en realidad se expande en a + b donde a es eoe yb es et_l. Y lo hace dos veces por cualquier razón :).

Este escenario que usted hizo es específico del compilador, depende de cómo el preprocesador elija manejar la expansión definida.


Mi respuesta es válida para el preprocesador C, pero de acuerdo con ¿Es un preprocesador C ++ idéntico a un preprocesador C? , las diferencias no son relevantes para este caso.

De C, A Manual de referencia, quinta edición :

Cuando se busca una macro llamada funcional, toda la macro llamada es reemplazada, luego del procesamiento del parámetro, por una copia del cuerpo. El procesamiento de parámetros se desarrolla de la siguiente manera. Las cadenas de tokens de argumento real están asociadas con los nombres de parámetros formales correspondientes. Luego se realiza una copia del cuerpo en la que cada aparición de un nombre de parámetro formal se reemplaza por una copia de la secuencia de token de parámetro real asociada a él. Esta copia del cuerpo reemplaza la macro llamada. [...] Una vez que se ha expandido una llamada de macro, el escaneo de llamadas de macro se reanuda al comienzo de la expansión para que los nombres de las macros puedan ser reconocidos dentro de la expansión con el fin de seguir reemplazando las macros.

Tenga en cuenta las palabras dentro de la expansión . Eso es lo que hace que tu ejemplo sea inválido. Ahora, combínelo con esto: ACTUALIZACIÓN : lea los comentarios a continuación.

[...] La macro se invoca escribiendo su nombre, un paréntesis izquierdo, luego una secuencia de token de argumento real para cada parámetro formal, luego un paréntesis de la derecha. Las secuencias del token del argumento real están separadas por comas.

Básicamente, todo se reduce a si el preprocesador volverá a escanear para nuevas invocaciones de macro solo dentro de la expansión anterior, o si seguirá leyendo los tokens que aparecen incluso después de la expansión.

Puede ser difícil pensar en esto, pero creo que lo que debería suceder con su ejemplo es que el nombre de macro f se reconoce durante la nueva exploración, y como el procesamiento de token posterior revela una invocación de macro para f() , su ejemplo es correcto y debería mostrarse que esperas. GCC y clang dan la salida correcta, y de acuerdo con este razonamiento, esto también sería válido (y producir salidas equivalentes):

#define dds f #define f(a,b) a+b dds(eoe,su)

Y, de hecho, el resultado del preproceso es el mismo en ambos ejemplos. En cuanto a la salida que obtienes con VC ++, diría que encontraste un error.

Esto es coherente con la sección 6.10.3.4 de C99, así como con la sección estándar 16.3.4 de C ++, nueva exploración y reemplazo adicional :

Después de que se hayan sustituido todos los parámetros en la lista de reemplazo y se haya realizado el procesamiento # y ##, se eliminarán todos los tokens de preprocesamiento de placemarker. A continuación, se vuelve a analizar la secuencia de token de preprocesamiento resultante, junto con todos los tokens de preprocesamiento posteriores del archivo de origen, para reemplazar más nombres de macro.