¿Cómo muestro el valor de un #define en tiempo de compilación?

123

Estoy tratando de averiguar qué versión de Boost cree que está usando mi código. Quiero hacer algo como esto:

#error BOOST_VERSION

pero el preprocesador no expande BOOST_VERSION.

Sé que podría imprimirlo en tiempo de ejecución desde el programa y sé que podría mirar la salida del preprocesador para encontrar la respuesta. Siento que tener una forma de hacer esto durante la compilación podría ser útil.

Jim Hunziker
fuente
7
Para futuros visitantes ... Chris Barry proporciona la solución generalizada al final (sin cosas específicas de Boost).
jww

Respuestas:

117

Sé que esto es mucho después de la consulta original, pero aún puede ser útil.

Esto se puede hacer en GCC usando el operador de cadena "#", pero requiere dos etapas.

#define XSTR(x) STR(x)
#define STR(x) #x

El valor de una macro se puede mostrar con:

#pragma message "The value of ABC: " XSTR(ABC)

Consulte: 3.4 Cadena de caracteres en la documentación en línea de gcc.

Cómo funciona:

El preprocesador entiende las cadenas entre comillas y las maneja de manera diferente al texto normal. La concatenación de cadenas es un ejemplo de este tratamiento especial. El pragma del mensaje requiere un argumento que sea una cadena entre comillas. Cuando hay más de un componente en el argumento, todos deben ser cadenas para que se pueda aplicar la concatenación de cadenas. El preprocesador nunca puede asumir que una cadena sin comillas debe tratarse como si estuviera entre comillas. Si lo hizo, entonces:

#define ABC 123
int n = ABC;

no compilaría.

Ahora considere:

#define ABC abc
#pragma message "The value of ABC is: " ABC

que es equivalente a

#pragma message "The value of ABC is: " abc

Esto provoca una advertencia del preprocesador porque abc (sin comillas) no se puede concatenar con la cadena anterior.

Ahora considere el preprocesador stringize (que una vez se llamó stringificación, los enlaces en la documentación se han cambiado para reflejar la terminología revisada. (Ambos términos, por cierto, son igualmente detestables. El término correcto es, por supuesto, stringifaction. Esté listo para actualizar sus enlaces.)) operador. Esto actúa solo sobre los argumentos de una macro y reemplaza el argumento no expandido con el argumento entre comillas dobles. Así:

#define STR(x) #x
char *s1 = "abc";
char *s2 = STR(abc);

asignará valores idénticos a s1 y s2. Si ejecuta gcc -E, puede ver esto en la salida. Quizás STR se llamaría mejor algo como ENQUOTE.

Esto resuelve el problema de poner comillas alrededor de un elemento sin comillas, el problema ahora es que, si el argumento es una macro, la macro no se expandirá. Por eso se necesita la segunda macro. XSTR expande su argumento, luego llama a STR para poner el valor expandido entre comillas.

Chris Barry
fuente
3
Tengo curiosidad por saber por qué requiere dos etapas
Vincent Fourmond
4
@VincentFourmond Sin la etapa XSTR, la macro no se expande. Entonces, si #define ABC 42 \ n STR (ABC) obtendría "ABC". Ver gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c
Esto también funciona muy bien con Xcode 8, por ejemplo, reemplazando ABC con __IPHONE_9_3.
funroll
La terminología de GCC parece haber cambiado, y con ella la URL, que ahora es https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry
119

BOOST_PP_STRINGIZE parece una excelente solución para C ++, pero no para C.

Aquí está mi solución para GNU CPP:

/* Some test definition here */
#define DEFINED_BUT_NO_VALUE
#define DEFINED_INT 3
#define DEFINED_STR "ABC"

/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "="  VALUE(var)

/* Some example here */
#pragma message(VAR_NAME_VALUE(NOT_DEFINED))
#pragma message(VAR_NAME_VALUE(DEFINED_BUT_NO_VALUE))
#pragma message(VAR_NAME_VALUE(DEFINED_INT))
#pragma message(VAR_NAME_VALUE(DEFINED_STR))

Las definiciones anteriores dan como resultado:

test.c:10:9: note: #pragma message: NOT_DEFINED=NOT_DEFINED
test.c:11:9: note: #pragma message: DEFINED_BUT_NO_VALUE=
test.c:12:9: note: #pragma message: DEFINED_INT=3
test.c:13:9: note: #pragma message: DEFINED_STR="ABC"

Para las variables "definido como entero" , "definido como cadena" y "definido pero sin valor" , funcionan bien. Solo para la variable "no definida" , se muestran exactamente igual que el nombre de la variable original. Tienes que acostumbrarte, o tal vez alguien pueda proporcionar una mejor solución.

Jackie Yeh
fuente
¡excelente! ¿Alguna experiencia en ARM RVCT? parece que no tiene función de "Stringification" como GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
xdan
2
Gran solucion Sin embargo, si deseo mostrar el tamaño de un valor calculado en tiempo de compilación, por ejemplo, el tamaño de una estructura compleja, ¿se puede hacer? El método sugerido en esta respuesta parece generar DEFINED_INT=(sizeof(MY_STRUCT)), sin sizeofque se evalúe al operador.
Carl
(Comentario Además: no es inesperado, ya que es el compilador en lugar de la pre-procesador que va a evaluar sizeof., Sin embargo, deja de ser curioso si hay una forma inteligente de conseguir esto)
Carl
@xdan Buena solución, desafortunadamente no se adapta a cosas como#define masks {0xff, 0xaf, 0x0f}
Simon Bagley
59

Si está utilizando Visual C ++, puede utilizar #pragma message:

#include <boost/preprocessor/stringize.hpp>
#pragma message("BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION))

Editar: Gracias a LB por el enlace

Aparentemente, el equivalente de GCC es (no probado):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)
Bojan Resnik
fuente
5
Eso se llama pragmas de diagnóstico, gcc.gnu.org/onlinedocs/gcc/…
LB40
4
Sería bueno si incluyese la definición deBOOST_PP_STRINGIZE cuál es agradable y breve y se puede copiar / pegar.
Timmmm
Funciona bien bajo gcc :)
Thomas Legris
14

Hasta donde yo sé, '#error' solo imprimirá cadenas, de hecho , ni siquiera necesita usar comillas .

¿Ha intentado escribir varios códigos incorrectos a propósito con "BOOST_VERSION"? Quizás algo como "blah [BOOST_VERSION] = foo;" le dirá algo como "el literal de cadena 1.2.1 no se puede utilizar como una dirección de matriz". No será un mensaje de error bonito, pero al menos te mostrará el valor relevante. Puede jugar hasta que encuentre un error de compilación que le indique el valor.

KeyserSoze
fuente
Eso no funcionó, ya que BOOST_VERSION es un número entero, pero pude verlo con esta declaración: std::vector<BOOST_VERSION>;en gcc 4.4.1. ¡Gracias!
Jim Hunziker
Tenga en cuenta que con Visual C ++, tendría que usar la respuesta de Bojan Resnik.
Raphaël Saint-Pierre
Traté de que esto funcionara, pero el mensaje de error que me dio GCC no era descriptivo. Pero +1 por mencionarlo.
Chris Lutz
14

Sin impulso:

  1. defina la misma macro nuevamente y el compilador HIMSELF le dará una advertencia.

  2. Desde la advertencia puede ver la ubicación de la definición anterior.

  3. Archivo vi de definición anterior.

ambarish@axiom:~/cpp$ g++ shiftOper.cpp
shiftOper.cpp:7:1: warning: "LINUX_VERSION_CODE" redefined
shiftOper.cpp:6:1: warning: this is the location of the previous definition

#define LINUX_VERSION_CODE 265216
#define LINUX_VERSION_CODE 666

int main ()
{

}
Ambarish Kumar Shivam
fuente
Este es más fácil y sencillo.
Tmx
1
sí mismo : los compiladores no tienen género
Sky
Esto no funciona con macros predefinidas, como __cplusplus.
ManuelAtWork
10

En Microsoft C / C ++, puede utilizar el integrado _CRT_STRINGIZE()para imprimir constantes. Muchos de mis stdafx.harchivos contienen alguna combinación de estos:

#pragma message("_MSC_VER      is " _CRT_STRINGIZE(_MSC_VER))
#pragma message("_MFC_VER      is " _CRT_STRINGIZE(_MFC_VER))
#pragma message("_ATL_VER      is " _CRT_STRINGIZE(_ATL_VER))
#pragma message("WINVER        is " _CRT_STRINGIZE(WINVER))
#pragma message("_WIN32_WINNT  is " _CRT_STRINGIZE(_WIN32_WINNT))
#pragma message("_WIN32_IE     is " _CRT_STRINGIZE(_WIN32_IE))
#pragma message("NTDDI_VERSION is " _CRT_STRINGIZE(NTDDI_VERSION)) 

y genera algo como esto:

_MSC_VER      is 1915
_MFC_VER      is 0x0E00
_ATL_VER      is 0x0E00
WINVER        is 0x0600
_WIN32_WINNT  is 0x0600
_WIN32_IE     is 0x0700
NTDDI_VERSION is 0x06000000
UweBaemayr
fuente
5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : error fatal C1083: no se puede abrir el archivo de inclusión: ':: 106200': no ​​existe ese archivo o directorio

Funciona incluso si preprocess to fileestá habilitado, incluso si hay tokens no válidos:

#define a <::'*/`#>
#include a
MSVC2015 : error fatal C1083: No se puede abrir el archivo de inclusión: '::' * / `# ': No
existe tal archivo o directorio GCC4.x : advertencia: falta el carácter de terminación' [-Winvalid-pp-token]
#define a <:: '* / `#>
Andry
fuente
El mío solo dice Build error: #include expects "FILENAME" or <FILENAME>. Suspiro.
endolito
@endolith ¿Qué compilador y versión?
Andry
DP8051 Keil 9.51 :)
endolito
@endolith Parece que este compilador tiene un preprocesamiento muy limitado: keil.com/support/man/docs/c51/c51_pp_directives.htm Pero, por mi parte, casi funciona como se esperaba, acabo de eliminar algunos de los caracteres no válidos como ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry
Gracias, esto me salvó porque el mensaje de pragma no se implementó en el compilador que estaba usando.
CodeMonkey
3

También puede preprocesar el archivo fuente y ver a qué se evalúa el valor del preprocesador.

fbrereto
fuente
2

Estás buscando

#if BOOST_VERSION != "1.2"
#error "Bad version"
#endif

No es genial si BOOST_VERSION es una cadena, como he asumido, pero también puede haber enteros individuales definidos para los números mayor, menor y de revisión.

user47559
fuente
Creo que el remitente no quiere (solo) imponer un valor en particular, quiere ver cuál es el valor actual.
KeyserSoze
Esto es lo único que me funciona. Puedo cambiar la #if VARIABLE == 123declaración sobre la marcha y el resaltado de sintaxis me dice si es el valor que creo que es o no ...
endolith
2

Mirar la salida del preprocesador es lo más parecido a la respuesta que pide.

Sé que ha excluido eso (y otras formas), pero no estoy seguro de por qué. Tiene un problema suficientemente específico que resolver, pero no ha explicado por qué ninguno de los métodos "normales" no le funciona bien.

dwc
fuente
Esta es probablemente la respuesta correcta al problema general.
jww
1

Puede escribir un programa que imprima, BOOST_VERSIONcompile y ejecute como parte de su sistema de compilación. De lo contrario, creo que no tienes suerte.

Chris Lutz
fuente
Para el caso de una versión de software definida en un encabezado, probablemente esté seguro (y es una buena respuesta). Pero como solución general, una posible desventaja sería que su aplicación de prueba y su aplicación real tuvieran el mismo valor de #define, dependiendo de sus rutas de inclusión, otras #defines que pueden usarse para establecer el valor de esa. , el CFLAGS pasó al compilador, etc.
KeyserSoze
Imprímelo desde tu programa real. Si es gráfico, colóquelo en el cuadro de diálogo "Acerca de". Si es línea de comandos, conviértalo en una opción (parte de --version, tal vez). Si es un demonio, escríbalo en un archivo de registro. Si está integrado, busque otra forma.
divegeek
@swillden: el OP lo quería en tiempo de compilación, no en tiempo de ejecución.
Chris Lutz
Esto también tiende a romper las compilaciones basadas en compiladores cruzados
Craig Ringer
1

BOOST_VERSION se define en el archivo de encabezado de boost version.hpp.

David Harris
fuente
1

Eche un vistazo también a la documentación de Boost, con respecto a cómo está usando la macro:

En referencia a BOOST_VERSION, de http://www.boost.org/doc/libs/1_37_0/libs/config/doc/html/boost_config/boost_macro_reference.html#boost_config.boost_macro_reference.boost_helper_macros :

Describe el número de versión de refuerzo en formato XXYYZZ de manera que: (BOOST_VERSION % 100)es la versión sub-menor, es la versión menor y es la versión principal.((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)

bn.
fuente
0

En lugar de #error, intente redefinir la macro, justo antes de que se utilice. La compilación fallará y el compilador proporcionará el valor actual que cree que se aplica a la macro.

#define BOOST_VERSION bla

tecMav
fuente