Wie zeige ich den Wert einer #define zur Kompilierungszeit an?

122

Ich versuche herauszufinden, welche Version von Boost mein Code verwendet. Ich möchte so etwas machen:

#error BOOST_VERSION

Der Präprozessor erweitert BOOST_VERSION jedoch nicht.

Ich weiß, dass ich es zur Laufzeit aus dem Programm ausdrucken kann, und ich weiß, dass ich die Ausgabe des Präprozessors überprüfen kann, um die Antwort zu finden. Ich denke, eine Möglichkeit, dies während der Kompilierung zu tun, könnte nützlich sein.

Jim Hunziker
quelle
7
Für zukünftige Besucher ... Chris Barry bietet am Ende die allgemeine Lösung (ohne Boost-spezifische Dinge).
JWW

Antworten:

116

Ich weiß, dass dies eine lange Zeit nach der ursprünglichen Abfrage ist, aber dies kann immer noch nützlich sein.

Dies kann in GCC mit dem Stringify-Operator "#" erfolgen, erfordert jedoch zwei Stufen.

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

Der Wert eines Makros kann dann angezeigt werden mit:

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

Siehe: 3.4 Stringifizierung in der gcc Online-Dokumentation.

Wie es funktioniert:

Der Präprozessor versteht Zeichenfolgen in Anführungszeichen und behandelt sie anders als normalen Text. Die Verkettung von Zeichenfolgen ist ein Beispiel für diese spezielle Behandlung. Das Nachrichtenpragma erfordert ein Argument, das eine Zeichenfolge in Anführungszeichen ist. Wenn das Argument mehr als eine Komponente enthält, müssen alle Zeichenfolgen sein, damit die Verkettung von Zeichenfolgen angewendet werden kann. Der Präprozessor kann niemals davon ausgehen, dass eine nicht in Anführungszeichen gesetzte Zeichenfolge so behandelt werden sollte, als ob sie in Anführungszeichen gesetzt wäre. Wenn ja, dann:

#define ABC 123
int n = ABC;

würde nicht kompilieren.

Betrachten Sie nun:

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

das ist äquivalent zu

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

Dies führt zu einer Präprozessorwarnung, da abc (nicht in Anführungszeichen) nicht mit der vorhergehenden Zeichenfolge verkettet werden kann.

Betrachten Sie nun die Präprozessor-Stringisierung (die früher als Stringifizierung bezeichnet wurde). Die Links in der Dokumentation wurden geändert, um die überarbeitete Terminologie widerzuspiegeln. (Beide Begriffe sind übrigens gleichermaßen verabscheuungswürdig. Der richtige Begriff ist natürlich Stringifaction. Seien Sie bereit für die Aktualisierung Ihre Links.)) Betreiber. Dies wirkt sich nur auf die Argumente eines Makros aus und ersetzt das nicht erweiterte Argument durch das in doppelte Anführungszeichen eingeschlossene Argument. So:

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

weist s1 und s2 identische Werte zu. Wenn Sie gcc -E ausführen, können Sie dies in der Ausgabe sehen. Vielleicht würde STR besser so etwas wie ENQUOTE heißen.

Dies löst das Problem, Anführungszeichen um ein nicht in Anführungszeichen gesetztes Element zu setzen. Das Problem besteht nun darin, dass das Makro nicht erweitert wird, wenn das Argument ein Makro ist. Aus diesem Grund wird das zweite Makro benötigt. XSTR erweitert sein Argument und ruft dann STR auf, um den erweiterten Wert in Anführungszeichen zu setzen.

Chris Barry
quelle
3
Ich bin gespannt, warum es zwei Stufen erfordert
Vincent Fourmond
4
@ VincentFourmond Ohne die XSTR-Stufe wird das Makro nicht erweitert. Wenn Sie also ABC 42 \ n STR (ABC) definieren, erhalten Sie "ABC". Siehe gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c
Dies funktioniert auch hervorragend mit Xcode 8, z __IPHONE_9_3. B. durch Ersetzen von ABC durch .
Funroll
Die GCC-Terminologie scheint sich geändert zu haben, und damit auch die URL, die jetzt https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry
119

BOOST_PP_STRINGIZE scheint eine ausgezeichnete Lösung für C ++ zu sein, aber nicht für reguläres C.

Hier ist meine Lösung für 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))

Die obigen Definitionen führen zu:

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"

Für die Variablen "definiert als Interger" , "definiert als Zeichenfolge" und "definiert, aber kein Wert" funktionieren sie einwandfrei . Nur für "nicht definierte" Variablen werden sie genauso angezeigt wie der ursprüngliche Variablenname. Man muss sich daran gewöhnen - oder vielleicht kann jemand eine bessere Lösung anbieten.

Jackie Yeh
quelle
Ausgezeichnet! Irgendwelche Erfahrungen in ARM RVCT? es scheint keine „Stringification“ -Funktion als GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/...
xdan
2
Tolle Lösung. Kann ich dies jedoch tun, wenn ich die Größe eines berechneten Werts zur Kompilierungszeit anzeigen möchte, z. B. die Größe einer komplexen Struktur? Die in dieser Antwort vorgeschlagene Methode scheint zu generieren DEFINED_INT=(sizeof(MY_STRUCT)), ohne dass der sizeofOperator ausgewertet wird.
Carl
(Kommentar hinzufügen: nicht unerwartet, da es eher der Compiler als der Vorprozessor ist, der auswertet sizeof, aber immer noch neugierig ist, ob es einen cleveren Weg gibt, dies zu erreichen.)
Carl
@xdan Gute Lösung, ist leider nicht für Dinge wie#define masks {0xff, 0xaf, 0x0f}
Simon Bagley
59

Wenn Sie Visual C ++ verwenden, können Sie Folgendes verwenden #pragma message:

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

Edit: Danke an LB für den Link

Anscheinend ist das GCC-Äquivalent (nicht getestet):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)
Bojan Resnik
quelle
5
Das nennt man diagnostische Pragmas, gcc.gnu.org/onlinedocs/gcc/…
LB40
4
Wäre schön, wenn Sie die DefinitionBOOST_PP_STRINGIZE einfügen würden, die nett und kurz und kopierbar / einfügbar ist.
Timmmm
Funktioniert gut unter gcc :)
Thomas Legris
14

Soweit ich weiß, druckt '#error' nur Zeichenfolgen, Sie müssen nicht einmal Anführungszeichen verwenden .

Haben Sie versucht, mit "BOOST_VERSION" verschiedene absichtlich falsche Codes zu schreiben? Vielleicht so etwas wie "bla [BOOST_VERSION] = foo;" wird Ihnen etwas sagen wie "String-Literal 1.2.1 kann nicht als Array-Adresse verwendet werden". Es wird keine hübsche Fehlermeldung sein, aber es zeigt Ihnen zumindest den relevanten Wert. Sie können herumspielen, bis Sie einen Kompilierungsfehler finden, der Ihnen den Wert angibt.

KeyserSoze
quelle
Das hat nicht funktioniert, da BOOST_VERSION eine ganze Zahl ist, aber ich habe es mit dieser Anweisung gesehen: std::vector<BOOST_VERSION>;in gcc 4.4.1. Vielen Dank!
Jim Hunziker
Beachten Sie, dass Sie mit Visual C ++ die Antwort von Bojan Resnik verwenden müssen.
Raphaël Saint-Pierre
Ich habe versucht, dies zum Laufen zu bringen, aber die Fehlermeldung, die GCC mir gab, war leider nicht beschreibend. Aber +1 für die Erwähnung.
Chris Lutz
14

Ohne Schub:

  1. Definieren Sie dasselbe Makro erneut und der Compiler HIMSELF gibt eine Warnung aus.

  2. Aus der Warnung können Sie den Ort der vorherigen Definition ersehen.

  3. vi-Datei der vorherigen Definition.

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
quelle
Dieser ist einfacher und unkomplizierter.
Tmx
1
selbst : Compiler haben kein Geschlecht
Sky
Dies funktioniert nicht mit vordefinierten Makros wie z __cplusplus.
ManuelAtWork
10

In Microsoft C / C ++ können Sie die integrierten Funktionen _CRT_STRINGIZE()zum Drucken von Konstanten verwenden. Viele meiner stdafx.hDateien enthalten eine Kombination davon:

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

und gibt so etwas aus:

_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
quelle
5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : Schwerwiegender Fehler C1083: Include-Datei kann nicht geöffnet werden: ':: 106200': Keine solche Datei oder kein solches Verzeichnis

Funktioniert auch, wenn preprocess to filees aktiviert ist, auch wenn ungültige Token vorhanden sind:

#define a <::'*/`#>
#include a
MSVC2015 : Schwerwiegender Fehler C1083: Include-Datei kann nicht geöffnet werden: '::' * / `# ': Keine solche Datei oder kein solches Verzeichnis
GCC4.x : Warnung: Fehlendes Abschlusszeichen' -Winvalid-pp-token]
#define a <:: '* / `#>
Andry
quelle
Meins sagt nur Build error: #include expects "FILENAME" or <FILENAME>. Seufzer.
Endolith
@endolith welcher Compiler und welche Version?
Andry
DP8051 Keil 9.51 :)
Endolith
@endolith Scheint, dass dieser Compiler in der Vorverarbeitung sehr eingeschränkt ist: keil.com/support/man/docs/c51/c51_pp_directives.htm Aber auf meiner Seite funktioniert es fast wie erwartet, ich habe gerade einige ungültige Zeichen entfernt wie ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry
Vielen Dank, das hat mich gerettet, weil das Pragma-Nachrichtenmaterial in dem von mir verwendeten Compiler nicht implementiert war.
CodeMonkey
3

Sie können auch die Quelldatei vorverarbeiten und sehen, wie der Präprozessorwert ausgewertet wird.

fbrereto
quelle
2

Suchen Sie

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

Nicht großartig, wenn BOOST_VERSION eine Zeichenfolge ist, wie ich angenommen habe, aber es können auch einzelne Ganzzahlen für die Haupt-, Neben- und Revisionsnummern definiert sein.

user47559
quelle
Ich denke, der Übermittler möchte einen bestimmten Wert nicht (nur) erzwingen, sondern sehen, wie hoch der aktuelle Wert ist.
KeyserSoze
Dies ist das einzige, was für mich funktioniert. Ich kann die #if VARIABLE == 123Anweisung im laufenden Betrieb ändern und die Syntaxhervorhebung sagt mir, ob es der Wert ist, von dem ich denke, dass er es ist oder nicht ...
Endolith
2

Ein Blick auf die Ausgabe des Präprozessors kommt der von Ihnen gewünschten Antwort am nächsten.

Ich weiß, dass Sie das (und andere Möglichkeiten) ausgeschlossen haben, aber ich bin mir nicht sicher, warum. Sie haben ein Problem, das spezifisch genug ist, um es zu lösen, aber Sie haben nicht erklärt, warum eine der "normalen" Methoden für Sie nicht gut funktioniert.

dwc
quelle
Dies ist wahrscheinlich die richtige Antwort auf das allgemeine Problem.
JWW
1

Sie können ein Programm schreiben, das es ausgibt, BOOST_VERSIONkompiliert und als Teil Ihres Build-Systems ausführt . Ansonsten denke ich, dass Sie kein Glück haben.

Chris Lutz
quelle
Für den Fall einer in einem Header definierten Softwareversion sind Sie wahrscheinlich sicher (und es ist eine gute Antwort). Als allgemeine Lösung besteht ein möglicher Nachteil darin, dass Ihre Test-App und Ihre reale App den gleichen Wert wie #define haben - abhängig von ihren Include-Pfaden andere #defines, die zum Festlegen des Werts dieser verwendet werden können , die CFLAGS an den Compiler übergeben, etc.
KeyserSoze
Drucken Sie es aus Ihrem realen Programm aus. Wenn grafisch, fügen Sie es in das Dialogfeld "Über" ein. Wenn es sich um eine Befehlszeile handelt, machen Sie sie zu einer Option (möglicherweise Teil von --version). Wenn es sich um einen Daemon handelt, schreiben Sie ihn in eine Protokolldatei. Wenn eingebettet, finden Sie einen anderen Weg.
Divegeek
@swillden - Das OP wollte es zur Kompilierungszeit, nicht zur Laufzeit.
Chris Lutz
Dies neigt auch dazu, Cross-Compiler-basierte Builds zu brechen
Craig Ringer
1

BOOST_VERSION ist in der Boost-Header-Datei version.hpp definiert.

David Harris
quelle
1

Schauen Sie sich auch die Boost-Dokumentation an, um zu erfahren, wie Sie das Makro verwenden:

In Bezug auf die BOOST_VERSIONvon 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 :

Beschreibt die Boost-Versionsnummer im XXYYZZ-Format wie folgt: (BOOST_VERSION % 100)ist die Sub-Minor-Version, ist die Minor-Version und ist die Major-Version.((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)

bn.
quelle
0

Versuchen Sie anstelle von #error, das Makro kurz vor seiner Verwendung neu zu definieren. Die Kompilierung schlägt fehl und der Compiler gibt den aktuellen Wert an, der seiner Meinung nach für das Makro gilt.

#define BOOST_VERSION bla

tecMav
quelle