Come mostro il valore di un #define in fase di compilazione?


123

Sto cercando di capire quale versione di Boost il mio codice pensa di utilizzare. Voglio fare qualcosa del genere:

#error BOOST_VERSION

ma il preprocessore non espande BOOST_VERSION.

So che potrei stamparlo in fase di esecuzione dal programma e so che potrei guardare l'output del preprocessore per trovare la risposta. Penso che avere un modo per farlo durante la compilazione potrebbe essere utile.


7
Per i futuri visitatori ... Chris Barry fornisce la soluzione generalizzata alla fine (priva di elementi specifici per Boost).
jww

Risposte:


117

So che è passato molto tempo dalla query originale, ma potrebbe comunque essere utile.

Questo può essere fatto in GCC utilizzando l'operatore stringify "#", ma richiede due fasi.

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

Il valore di una macro può quindi essere visualizzato con:

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

Vedere: 3.4 Stringificazione nella documentazione in linea di gcc.

Come funziona:

Il preprocessore comprende le stringhe quotate e le gestisce in modo diverso dal testo normale. La concatenazione di stringhe è un esempio di questo trattamento speciale. Il pragma del messaggio richiede un argomento che sia una stringa tra virgolette. Quando è presente più di un componente per l'argomento, devono essere tutti stringhe in modo che possa essere applicata la concatenazione di stringhe. Il preprocessore non può mai presumere che una stringa non quotata debba essere trattata come se fosse quotata. In tal caso:

#define ABC 123
int n = ABC;

non si compilerebbe.

Ora considera:

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

che è equivalente a

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

Ciò causa un avviso del preprocessore perché abc (non quotato) non può essere concatenato con la stringa precedente.

Ora considera il preprocessore stringize (che una volta era chiamato stringificazione, i collegamenti nella documentazione sono stati modificati per riflettere la terminologia rivista. (Entrambi i termini, per inciso, sono ugualmente detestabili. Il termine corretto è, ovviamente, stringifaction. Preparati ad aggiornare i tuoi collegamenti.)) operatore. Agisce solo sugli argomenti di una macro e sostituisce l'argomento non espanso con l'argomento racchiuso tra virgolette doppie. Così:

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

assegnerà valori identici a s1 e s2. Se esegui gcc -E puoi vederlo nell'output. Forse STR sarebbe meglio chiamare qualcosa come ENQUOTE.

Questo risolve il problema di inserire virgolette attorno a un elemento non quotato, il problema ora è che, se l'argomento è una macro, la macro non verrà espansa. Questo è il motivo per cui è necessaria la seconda macro. XSTR espande il suo argomento, quindi chiama STR per inserire il valore espanso tra virgolette.


3
Sono curioso di
sapere

4
@VincentFourmond Senza la fase XSTR, la macro non viene espansa. Quindi se hai #define ABC 42 \ n STR (ABC) otterrai "ABC". Vedi gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c

Funziona benissimo anche con Xcode 8, ad esempio sostituendo ABC con __IPHONE_9_3.
funroll

La terminologia di GCC sembra essere cambiata e con essa l'URL, che ora è https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry

119

BOOST_PP_STRINGIZE sembra un'ottima soluzione per C ++, ma non per il normale C.

Ecco la mia soluzione per 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))

Le definizioni di cui sopra danno come risultato:

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"

Per le variabili "definite come interger" , "definite come stringa" e "definite ma senza valore" , funzionano perfettamente. Solo per le variabili "non definite" , venivano visualizzate esattamente come il nome della variabile originale. Devi abituarti - o forse qualcuno può fornire una soluzione migliore.


eccellente! Eventuali esperienze in ARM RVCT? sembra non abbia la funzione "Stringification" in quanto GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
xdan

2
Ottima soluzione. Tuttavia, se desidero visualizzare la dimensione di un valore calcolato in fase di compilazione, ad esempio la dimensione di una struttura complessa, è possibile farlo? Il metodo suggerito in questa risposta sembra generare DEFINED_INT=(sizeof(MY_STRUCT)), senza che l' sizeofoperatore venga valutato.
Carl

(Aggiunta commento: non inaspettata, dato che è il compilatore piuttosto che il pre-processore che valuterà sizeof, comunque, ancora curioso se c'è un modo intelligente per raggiungere questo obiettivo.)
Carl

@xdan Buona soluzione, purtroppo non è adatta a cose come#define masks {0xff, 0xaf, 0x0f}
Simon Bagley,

59

Se stai usando Visual C ++, puoi usare #pragma message:

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

Modifica: grazie a LB per il collegamento

Apparentemente, l'equivalente GCC è (non testato):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)

5
Si chiamano pragmi diagnostici, gcc.gnu.org/onlinedocs/gcc/…
LB40

4
Sarebbe bello se includessi la definizioneBOOST_PP_STRINGIZE che è carina e breve e copia / incollabile.
Timmmm

Funziona bene con gcc :)
Thomas Legris

14

Per quanto ne so '#error' solo stamperà stringhe, infatti non hai nemmeno bisogno di usare le virgolette .

Hai provato a scrivere vari codici volutamente errati utilizzando "BOOST_VERSION"? Forse qualcosa come "blah [BOOST_VERSION] = foo;" ti dirà qualcosa come "la stringa letterale 1.2.1 non può essere usata come un indirizzo di array". Non sarà un bel messaggio di errore, ma almeno ti mostrerà il valore pertinente. Puoi giocare finché non trovi un errore di compilazione che ti dice il valore.


Non ha funzionato, poiché BOOST_VERSION è un numero intero, ma l'ho visto con questa dichiarazione: std::vector<BOOST_VERSION>;in gcc 4.4.1. Grazie!
Jim Hunziker

Nota che con Visual C ++, dovresti usare la risposta di Bojan Resnik.
Raphaël Saint-Pierre

Ho provato a farlo funzionare, ma il messaggio di errore che GCC mi ha fornito era purtroppo poco descrittivo. Ma +1 per averlo menzionato.
Chris Lutz

14

Senza boost:

  1. definire di nuovo la stessa macro e il compilatore HIMSELF darà un avviso.

  2. Dall'avviso puoi vedere la posizione della definizione precedente.

  3. vi file della definizione precedente.

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 ()
{

}

Questo è più facile e diretto.
Tmx

1
stesso : i compilatori non hanno sesso
Sky

Questo non funziona con le macro predefinite, come __cplusplus.
ManuelAtWork

10

In Microsoft C / C ++, puoi utilizzare il built-in _CRT_STRINGIZE()per stampare le costanti. Molti dei miei stdafx.hfile contengono una combinazione di questi:

#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)) 

e produce qualcosa del genere:

_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

5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : errore irreversibile C1083: impossibile aprire il file di inclusione: ':: 106200': nessun file o directory di questo tipo

Funziona anche se preprocess to fileè abilitato, anche se sono presenti token non validi:

#define a <::'*/`#>
#include a
MSVC2015 : errore irreversibile C1083: impossibile aprire il file di inclusione: '::' * / `# ': nessun file o directory
GCC4.x : avviso: carattere di terminazione mancante [-Winvalid-pp-token]
#define a <:: '* / `#>

Il mio dice solo Build error: #include expects "FILENAME" or <FILENAME>. Sospiro.
endolith

@endolith quale compilatore e versione?
Andry

DP8051 Keil 9.51 :)
endolith

@endolith Sembra che questo compilatore sia molto limitato nella preelaborazione: keil.com/support/man/docs/c51/c51_pp_directives.htm Ma, da parte mia, funziona quasi come previsto, ho appena rimosso alcuni caratteri non validi come ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry

Grazie, questo mi ha salvato perché il materiale del messaggio pragma non è stato implementato nel compilatore che stavo usando.
CodeMonkey

3

È anche possibile preelaborare il file di origine e vedere cosa restituisce il valore del preprocessore.


2

Stai cercando

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

Non eccezionale se BOOST_VERSION è una stringa, come ho ipotizzato, ma potrebbero essere definiti anche singoli interi per i numeri maggiore, minore e di revisione.


Penso che il mittente non voglia (solo) imporre un valore particolare, vuole vedere qual è il valore corrente.
KeyserSoze

Questa è l'unica cosa che funziona per me. Posso cambiare l' #if VARIABLE == 123affermazione al volo e l'evidenziazione della sintassi mi dice se è il valore che penso sia o no ...
endolith

2

Guardare l'output del preprocessore è la cosa più vicina alla risposta che chiedi.

So che l'hai escluso (e in altri modi), ma non sono sicuro del perché. Hai un problema abbastanza specifico da risolvere, ma non hai spiegato perché nessuno dei metodi "normali" non funziona bene per te.


Questa è probabilmente la risposta corretta al problema generale.
jww

1

Potresti scrivere un programma che stampa, BOOST_VERSIONcompilarlo ed eseguirlo come parte del tuo sistema di compilazione. Altrimenti, penso che tu sia sfortunato.


Per il caso di una versione software definita in un'intestazione probabilmente sei al sicuro (ed è una buona risposta). Ma come soluzione generale, un possibile svantaggio sarebbe quello di far sì che la tua app di test e la tua app reale abbiano lo stesso valore di #define - a seconda dei loro percorsi di inclusione, altri #define che possono essere utilizzati per impostare il valore di quella , le CFLAGS passate al compilatore, ecc.
KeyserSoze

Stampalo dal tuo programma reale. Se grafico, inserirlo nella finestra di dialogo "informazioni su". Se da riga di comando, rendilo un'opzione (parte di --version, forse). Se un demone, scrivilo in un file di registro. Se incorporato, trova un altro modo.
divegeek

@swillden - L'OP lo voleva in fase di compilazione, non in fase di runtime.
Chris Lutz,

Questo tende anche a rompere le build basate su più compilatori
Craig Ringer

1

BOOST_VERSION è definito nel file di intestazione boost version.hpp.


1

Dai un'occhiata anche alla documentazione di Boost, riguardo a come stai usando la macro:

In riferimento a BOOST_VERSION, da 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 :

Descrive il numero della versione boost in formato XXYYZZ in modo tale che: (BOOST_VERSION % 100)è la versione secondaria secondaria, è la versione secondaria ed è la versione principale.((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)


0

Invece di #error, prova a ridefinire la macro, appena prima che venga utilizzata. La compilazione avrà esito negativo e il compilatore fornirà il valore corrente che ritiene si applichi alla macro.

#define BOOST_VERSION blah

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.