Replacing text macros
Der Präprozessor unterstützt Text-Makroersetzung. Funktionsähnliche Text-Makroersetzung wird ebenfalls unterstützt.
InhaltSyntax
Erklärung
#define DirektivenDie #define Direktiven definieren den identifier als Makro, das heißt, sie weisen den Compiler an, die meisten aufeinanderfolgenden Vorkommen von identifier durch replacement-list zu ersetzen, die zusätzlich verarbeitet wird. Ausnahmen ergeben sich aus den Regeln für scanning and replacement . Wenn der identifier bereits als irgendeine Art von Makro definiert ist, ist das Programm fehlerhaft, es sei denn, die Definitionen sind identisch. Objektartige MakrosObjektartige Makros ersetzen jedes Vorkommen des definierten identifier durch die replacement-list . Version (1) der #define -Direktive verhält sich genau so. Funktionsartige MakrosFunktionsartige Makros ersetzen jedes Vorkommen des definierten identifier durch replacement-list , wobei zusätzlich eine Anzahl von Argumenten übernommen wird, die dann entsprechende Vorkommen der parameters in der replacement-list ersetzen. Die Syntax eines funktionsähnlichen Makroaufrufs ähnelt der Syntax eines Funktionsaufrufs: Jedes Vorkommen des Makronamens, gefolgt von einer ( als nächstes Präprozessor-Token, leitet die Sequenz von Tokens ein, die durch die replacement-list ersetzt wird. Die Sequenz wird durch das entsprechende ) Token beendet, wobei übereinstimmende Paare von linken und rechten Klammern übersprungen werden.
Für Version (2) muss die Anzahl der Argumente gleich der Anzahl der Parameter in der Makrodefinition sein. Für Versionen (3,4) darf die Anzahl der Argumente nicht kleiner sein als die Anzahl der Parameter (
nicht
(seit C++20)
zählend
Version (2) der #define Direktive definiert ein einfaches funktionsähnliches Makro.
Version (3) der
#define
-Direktive definiert ein funktionsähnliches Makro mit variabler Anzahl von Argumenten. Die zusätzlichen Argumente (genannt
variable Argumente
) können mit dem Bezeichner
Version (4) der
#define
-Direktive definiert ein funktionsähnliches Makro mit variabler Anzahl von Argumenten, jedoch ohne reguläre Argumente. Die Argumente (genannt
variable Argumente
) können nur mit dem
Hinweis: Wenn ein Argument eines funktionsähnlichen Makros Kommas enthält, die nicht durch übereinstimmende Paare von linken und rechten Klammern geschützt sind (am häufigsten in Template-Argumentlisten zu finden, wie in assert ( std:: is_same_v < int , int > ) ; oder BOOST_FOREACH ( std:: pair < int , int > p, m ) ), wird das Komma als Makro-Argumenttrenner interpretiert, was zu einem Kompilierungsfehler aufgrund von nicht übereinstimmender Argumentanzahl führt. Scannen und Ersetzen
Beachten Sie, dass es möglich ist, pseudo-rekursive Makros zu definieren:
Diesen Code ausführen
#define EMPTY #define SCAN(x) x #define EXAMPLE_() EXAMPLE #define EXAMPLE(n) EXAMPLE_ EMPTY()(n-1) (n) EXAMPLE(5) SCAN(EXAMPLE(5)) Ausgabe: EXAMPLE_ ()(5 -1) (5) EXAMPLE_ ()(5 -1 -1) (5 -1) (5) Reservierte MakronamenEine Übersetzungseinheit, die eine Standardbibliothek-Header-Datei einbindet , darf keine #define - oder #undef -Anweisungen für Namen verwenden, die in irgendeiner Standardbibliothek-Header-Datei deklariert sind. Eine Übersetzungseinheit, die irgendeinen Teil der Standardbibliothek verwendet, darf keine Namen lexikalisch identisch zu folgenden #define oder #undef definieren:
Andernfalls ist das Verhalten undefiniert.
# und ## OperatorenIn funktionsähnlichen Makros führt ein # -Operator vor einem Bezeichner in der replacement-list dazu, dass der Bezeichner durch Parameterersetzung verarbeitet und das Ergebnis in Anführungszeichen gesetzt wird, wodurch effektiv ein String-Literal erzeugt wird. Zusätzlich fügt der Präprozessor Backslashes hinzu, um die Anführungszeichen um eingebettete String-Literale zu escapen, falls vorhanden, und verdoppelt die Backslashes innerhalb des Strings nach Bedarf. Alle führenden und nachfolgenden Leerzeichen werden entfernt, und jede Abfolge von Leerzeichen in der Mitte des Textes (aber nicht innerhalb eingebetteter String-Literale) wird auf ein einzelnes Leerzeichen reduziert. Dieser Vorgang wird "Stringifizierung" genannt. Wenn das Ergebnis der Stringifizierung kein gültiges String-Literal ist, ist das Verhalten undefiniert.
Ein
##
Operator zwischen zwei aufeinanderfolgenden Bezeichnern in der
Ersetzungsliste
führt Parameterersetzung für die beiden Bezeichner durch (die nicht zuerst makroexpandiert werden) und verkettet dann das Ergebnis. Dieser Vorgang wird "Konkatenation" oder "Token-Zusammenfügung" genannt. Nur Token, die zusammen ein gültiges Token bilden, dürfen zusammengefügt werden: Bezeichner, die einen längeren Bezeichner bilden, Ziffern, die eine Zahl bilden, oder Operatoren
Hinweis: Einige Compiler bieten eine Erweiterung, die es erlaubt, dass
##
nach einem Komma und vor
#undef DirektiveDie #undef -Direktive hebt die Definition des Identifiers auf, das heißt, sie macht eine vorherige Definition des Identifiers durch die #define -Direktive rückgängig. Wenn der Identifier kein assoziiertes Makro besitzt, wird die Direktive ignoriert. Vordefinierte MakrosDie folgenden Makronamen sind in jeder Übersetzungseinheit vordefiniert:
Die folgenden zusätzlichen Makronamen können von den Implementierungen vordefiniert werden:
Die Werte dieser Makros (außer
Beispiel
Diesen Code ausführen
#include <iostream> // Make function factory and use it #define FUNCTION(name, a) int fun_##name() { return a; } FUNCTION(abcd, 12) FUNCTION(fff, 2) FUNCTION(qqq, 23) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) std::cout << "output: " #a << '\n' // Using a macro in the definition of a later macro #define WORD "Hello " #define OUTER(...) WORD #__VA_ARGS__ int main() { std::cout << "abcd: " << fun_abcd() << '\n'; std::cout << "fff: " << fun_fff() << '\n'; std::cout << "qqq: " << fun_qqq() << '\n'; std::cout << FUNCTION << '\n'; OUTPUT(million); //note the lack of quotes std::cout << OUTER(World) << '\n'; std::cout << OUTER(WORD World) << '\n'; } Ausgabe: abcd: 12 fff: 2 qqq: 23 34 output: million Hello World Hello WORD World FehlerberichteDie folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
Siehe auch
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||