Textmakros ersetzen
Der Präprozessor unterstützt das Ersetzen von Textmakros. Funktionsähnliche Textmakros werden ebenfalls unterstützt.
Inhalt |
[bearbeiten] Syntax
#define identifikator ersetzungsliste (optional) |
(1) | ||||||||
#define identifikator (parameter ) ersetzungsliste (optional) |
(2) | ||||||||
#define identifikator (parameter , ...) ersetzungsliste (optional) |
(3) | (seit C++11) | |||||||
#define identifikator (...) ersetzungsliste (optional) |
(4) | (seit C++11) | |||||||
#undef identifikator |
(5) | ||||||||
[bearbeiten] Erklärung
[bearbeiten] #define-Direktiven
Die #define-Direktiven definieren den identifikator als Makro, d. h. sie weisen den Compiler an, die meisten aufeinanderfolgenden Vorkommen von identifikator durch ersetzungsliste zu ersetzen, die zusätzlich verarbeitet wird. Ausnahmen ergeben sich aus den Regeln des Scannens und Ersetzens. Wenn der Bezeichner bereits als irgendeine Art von Makro definiert ist, ist das Programm schlecht formuliert, es sei denn, die Definitionen sind identisch.
[bearbeiten] Objektähnliche Makros
Objektähnliche Makros ersetzen jedes Vorkommen eines definierten identifikator durch eine ersetzungsliste. Version (1) der #define-Direktive verhält sich genau so.
[bearbeiten] Funktionsähnliche Makros
Funktionsähnliche Makros ersetzen jedes Vorkommen eines definierten identifikator durch eine ersetzungsliste, wobei zusätzlich eine Anzahl von Argumenten angenommen wird, die dann entsprechende Vorkommen von Parametern in der ersetzungsliste ersetzen.
Die Syntax eines funktionsähnlichen Makroaufrufs ähnelt der Syntax eines Funktionsaufrufs: Jede Instanz des Makronamens, gefolgt von einer ( als nächstes Präprozessor-Token, leitet die zu ersetzende Token-Sequenz durch die ersetzungsliste ein. Die Sequenz wird durch das passende )-Token beendet, wobei dazwischenliegende übereinstimmende Klammerpaare übersprungen werden.
Bei Version (2) muss die Anzahl der Argumente mit der Anzahl der Parameter in der Makrodefinition übereinstimmen. Bei den Versionen (3, 4) darf die Anzahl der Argumente nicht kleiner sein als die Anzahl der Parameter (wobei ... nicht mitgezählt wird nicht(seit C++20)). Andernfalls ist das Programm schlecht formuliert. Wenn der Bezeichner nicht in funktioneller Notation steht, d. h. keine Klammern hinter sich hat, wird er überhaupt nicht ersetzt.
Version (2) der #define-Direktive definiert ein einfaches funktionsähnliches Makro.
Version (3) der #define-Direktive definiert ein funktionsähnliches Makro mit variabler Argumentanzahl. Die zusätzlichen Argumente (sogenannte _variable Argumente_) können über den Bezeichner __VA_ARGS__ abgerufen werden, der dann mit den Argumenten ersetzt wird, die dem zu ersetzenden Bezeichner zugeordnet sind.
Version (4) der #define-Direktive definiert ein funktionsähnliches Makro mit variabler Argumentanzahl, aber ohne reguläre Argumente. Die Argumente (sogenannte _variable Argumente_) können nur über den Bezeichner __VA_ARGS__ abgerufen werden, der dann mit den Argumenten ersetzt wird, die dem zu ersetzenden Bezeichner zugeordnet sind.
|
Bei den Versionen (3, 4) kann die ersetzungsliste die Token-Sequenz #define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) F(a, b, c) // replaced by f(0, a, b, c) F() // replaced by f(0) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) G(a, b, c) // replaced by f(0, a, b, c) G(a, ) // replaced by f(0, a) G(a) // replaced by f(0, a) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) SDEF(foo); // replaced by S foo; SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 }; |
(seit C++20) |
Hinweis: Wenn ein Argument eines funktionsähnlichen Makros Kommas enthält, die nicht von übereinstimmenden Klammerpaaren geschützt sind (am häufigsten in Vorlagen-Argumentlisten, wie in assert(std::is_same_v<int, int>); oder BOOST_FOREACH(std::pair<int, int> p, m) gefunden), wird das Komma als Trennzeichen für Makroargumente interpretiert, was zu einem Kompilierungsfehler aufgrund eines falschen Argumentzählers führt.
[bearbeiten] Scannen und Ersetzen
- Beim Scannen wird verfolgt, welche Makros sie ersetzt haben. Wenn beim Scannen Text gefunden wird, der einem solchen Makro entspricht, wird er als "zu ignorieren" markiert (alle Scans werden ihn ignorieren). Dies verhindert Rekursion.
- Wenn beim Scannen ein funktionsähnliches Makro gefunden wird, werden die Argumente gescannt, bevor sie in die ersetzungsliste eingefügt werden. Ausgenommen sind die #- und ##-Operatoren, die ein Argument ohne Scan übernehmen.
- Nachdem das Makro ersetzt wurde, wird der Ergebnis-Text gescannt.
Hinweis: Es ist möglich, ein pseudo-rekursives Makro zu definieren
#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)
[bearbeiten] Reservierte Makronamen
Eine Übersetzungseinheit, die eine Standardbibliotheks-Headerdatei einbindet, darf keine Namen definieren oder undefinieren, die in irgendeiner Standardbibliotheks-Headerdatei deklariert sind.
Einer Übersetzungseinheit, die Teile der Standardbibliothek verwendet, ist es nicht gestattet, Namen zu definieren oder zu undefinieren, die lexikalisch identisch sind mit:
|
(seit C++11) |
Andernfalls ist das Verhalten undefiniert.
[bearbeiten] #- und ##-Operatoren
In funktionsähnlichen Makros wandelt ein #-Operator vor einem Bezeichner in der ersetzungsliste den Bezeichner durch Parameterersetzung um und schließt das Ergebnis in Anführungszeichen ein, wodurch effektiv ein Zeichenkettenliteral entsteht. Darüber hinaus fügt der Präprozessor Backslashes hinzu, um die Anführungszeichen um eingebettete Zeichenkettenliterale zu maskieren, falls vorhanden, und verdoppelt die Backslashes innerhalb der Zeichenkette nach Bedarf. Alle führenden und nachfolgenden Leerzeichen werden entfernt, und jede Leerzeichensequenz in der Mitte des Textes (aber nicht innerhalb eingebetteter Zeichenkettenliterale) wird zu einem einzelnen Leerzeichen zusammengefasst. Diese Operation wird als "Stringifizierung" bezeichnet. Wenn das Ergebnis der Stringifizierung kein gültiges Zeichenkettenliteral ist, ist das Verhalten undefiniert.
|
Wenn # vor #define showlist(...) puts(#__VA_ARGS__) showlist(); // expands to puts("") showlist(1, "x", int); // expands to puts("1, \"x\", int") |
(seit C++11) |
Ein ##-Operator zwischen zwei aufeinanderfolgenden Bezeichnern in der ersetzungsliste führt eine Parameterersetzung für die beiden Bezeichner durch (die nicht zuerst als Makros expandiert werden) und verkettet dann das Ergebnis. Diese Operation wird als "Verkettung" oder "Token-Pasten" bezeichnet. Nur Token, die zusammen ein gültiges Token bilden, können zusammengefügt werden: Bezeichner, die einen längeren Bezeichner bilden, Ziffern, die eine Zahl bilden, oder Operatoren + und =, die ein += bilden. Ein Kommentar kann nicht durch das Zusammenfügen von / und * erstellt werden, da Kommentare vor der Makrosubstitution aus dem Text entfernt werden. Wenn das Ergebnis der Verkettung kein gültiges Token ist, ist das Verhalten undefiniert.
Hinweis: Einige Compiler bieten eine Erweiterung, die es ermöglicht, dass ## nach einem Komma und vor __VA_ARGS__ erscheint. In diesem Fall bewirkt der ## nichts, wenn die variablen Argumente vorhanden sind, entfernt aber das Komma, wenn die variablen Argumente nicht vorhanden sind: Dies ermöglicht die Definition von Makros wie fprintf (stderr, format, ##__VA_ARGS__). Dies kann auch auf standardmäßige Weise mit __VA_OPT__ erreicht werden, z. B. fprintf (stderr, format __VA_OPT__(, ) __VA_ARGS__).(seit C++20)
[bearbeiten] #undef-Direktive
Die #undef-Direktive hebt die Definition des identifikator auf, d. h. sie macht eine vorherige Definition des identifikator durch eine #define-Direktive rückgängig. Wenn der Bezeichner kein zugeordnetes Makro hat, wird die Direktive ignoriert.
[bearbeiten] Vordefinierte Makros
Die folgenden Makronamen sind in jeder Übersetzungseinheit vordefiniert:
| __cplusplus |
bezeichnet die Version des verwendeten C++-Standards und expandiert zum Wert
|
| __STDC_HOSTED__ (C++11) |
expandiert zur Ganzzahlkonstante 1, wenn die Implementierung gehostet wird (unter einem Betriebssystem läuft), 0 wenn sie eigenständig ist (ohne Betriebssystem läuft) (Makro-Konstante) |
| __FILE__ |
expandiert zum Namen der aktuellen Datei als Zeichenkettenliteral, kann durch die #line-Direktive geändert werden(Makro-Konstante) |
| __LINE__ |
expandiert zur Zeilennummer der aktuellen physischen Quellzeile, einer Ganzzahlkonstante, kann durch die #line-Direktive geändert werden(Makro-Konstante) |
| __DATE__ |
expandiert zum Übersetzungsdatum, einem Zeichenkettenliteral der Form "Mmm dd yyyy". Das erste Zeichen von "dd" ist ein Leerzeichen, wenn der Tag des Monats kleiner als 10 ist. Der Monatsname ist derselbe wie von std::asctime() generiert. (Makro-Konstante) |
| __TIME__ |
expandiert zur Übersetzungszeit, einem Zeichenkettenliteral der Form "hh:mm:ss" (Makro-Konstante) |
| __STDCPP_DEFAULT_NEW_ALIGNMENT__ (C++17) |
expandiert zu einem std::size_t-Literal, dessen Wert die Ausrichtung ist, die von einem Aufruf eines Alignment-unaware operator new garantiert wird (größere Ausrichtungen werden an die Alignment-aware Überladung übergeben, z. B. operator new(std::size_t, std::align_val_t))(Makro-Konstante) |
| __STDCPP_BFLOAT16_T____STDCPP_FLOAT16_T____STDCPP_FLOAT32_T____STDCPP_FLOAT64_T____STDCPP_FLOAT128_T__ (C++23) |
expandiert zu 1 genau dann, wenn die Implementierung die entsprechenden erweiterten Gleitkommatypen unterstützt (Makro-Konstante) |
Die folgenden zusätzlichen Makronamen können von den Implementierungen vordefiniert werden:
| __STDC__ |
implementierungsdefinierter Wert, falls vorhanden, normalerweise zur Anzeige der C-Konformität verwendet (Makro-Konstante) | ||||
| __STDC_VERSION__ (C++11) |
implementierungsdefinierter Wert, falls vorhanden (Makro-Konstante) | ||||
| __STDC_ISO_10646__ (C++11) |
(Makro-Konstante) | ||||
| __STDC_MB_MIGHT_NEQ_WC__ (C++11) |
expandiert zu 1, wenn 'x' == L'x' für ein Mitglied der Basistextmenge falsch sein könnte, z. B. auf EBCDIC-basierten Systemen, die Unicode für wchar_t verwenden (Makro-Konstante) | ||||
| __STDCPP_THREADS__ (C++11) |
expandiert zu 1, wenn das Programm mehr als einen Ausführungsfaden haben kann (Makro-Konstante) |
| __STDCPP_STRICT_POINTER_SAFETY__ (C++11)(entfernt in C++23) |
expandiert zu 1, wenn die Implementierung strenge std::pointer_safety aufweist (Makro-Konstante) |
Die Werte dieser Makros (mit Ausnahme von __FILE__ und __LINE__) bleiben während der gesamten Übersetzungseinheit konstant. Versuche, diese Makros neu zu definieren oder zu undefinieren, führen zu undefiniertem Verhalten.
Sprachmerkmal-TestmakrosDer Standard definiert eine Reihe von Präprozessor-Makros, die den in C++11 oder später eingeführten C++-Sprachmerkmalen entsprechen. Sie sind als einfache und portable Methode gedacht, um die Anwesenheit dieser Merkmale zu erkennen. Siehe Merkmalstests für Details. |
(seit C++20) |
AnmerkungenDie funktionslokale vordefinierte Variable __func__ ist kein vordefiniertes Makro, wird aber normalerweise zusammen mit |
(seit C++11) |
[bearbeiten] Beispiel
#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
[bearbeiten] Defect Reports
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 2908 | C++98 | war unklar, ob __LINE__ zur aktuellenphysischen Zeilennummer oder zur aktuellen logischen Zeilennummer expandiert |
expandiert zur aktuellen physischen Zeilennummer |
| LWG 294 | C++98 | eine Übersetzungseinheit, die eine Standardbibliotheks-Headerdatei einbindet, könnte Makros enthalten, die Namen definieren, die in anderen Standardbibliotheks-Headerdateien deklariert sind |
verboten |
| P2621R2 | C++23 | universelle Zeichencodes konnten nicht durch Token-Verkettung gebildet werden |
erlaubt |
[bearbeiten] Siehe auch
| C++ Dokumentation für Makro-Symbolindex
| |
| C-Dokumentation für Textmakros ersetzen
|