Bedingte Einbindung
Der Präprozessor unterstützt die bedingte Kompilierung von Teilen einer Quelldatei. Dieses Verhalten wird durch die Direktiven #if, #else, #elif, #ifdef, #ifndef, #elifdef, #elifndef(seit C++23) und #endif gesteuert.
Inhalt |
[bearbeiten] Syntax
#if Ausdruck |
|||||||||
#ifdef Bezeichner |
|||||||||
#ifndef Bezeichner |
|||||||||
#elif Ausdruck |
|||||||||
#elifdef Bezeichner |
(seit C++23) | ||||||||
#elifndef Bezeichner |
(seit C++23) | ||||||||
#else
|
|||||||||
#endif
|
|||||||||
[bearbeiten] Erklärung
Der bedingte Präprozessorblock beginnt mit einer #if-, #ifdef- oder #ifndef-Direktive, gefolgt von optional einer beliebigen Anzahl von #elif-, #elifdef- oder #elifndef-Direktiven(seit C++23), gefolgt von optional höchstens einer #else-Direktive und wird mit einer #endif-Direktive abgeschlossen. Verschachtelte bedingte Präprozessorblöcke werden separat verarbeitet.
Jede der Direktiven #if, #ifdef, #ifndef, #elif, #elifdef, #elifndef(seit C++23) und #else steuert den Codeblock bis zur ersten #elif-, #elifdef-, #elifndef-(seit C++23), #else- oder #endif-Direktive, die nicht zu einem inneren bedingten Präprozessorblock gehört.
Die Direktiven #if, #ifdef und #ifndef prüfen die angegebene Bedingung (siehe unten) und kompilieren den gesteuerten Codeblock, wenn diese zu wahr ausgewertet wird. In diesem Fall werden nachfolgende #else-, #elifdef-, #elifndef-(seit C++23) und #elif-Direktiven ignoriert. Andernfalls, wenn die angegebene Bedingung zu falsch ausgewertet wird, wird der gesteuerte Codeblock übersprungen und die nachfolgende #else-, #elifdef-, #elifndef-(seit C++23) oder #elif-Direktive (falls vorhanden) verarbeitet. Wenn die nachfolgende Direktive #else ist, wird der von der #else-Direktive gesteuerte Codeblock bedingungslos kompiliert. Andernfalls verhält sich die #elif-, #elifdef- oder #elifndef-(seit C++23)-Direktive wie eine #if-Direktive: Sie prüft die Bedingung, kompiliert oder überspringt den gesteuerten Codeblock basierend auf dem Ergebnis und verarbeitet im letzteren Fall nachfolgende #elif-, #elifdef-, #elifndef-(seit C++23) und #else-Direktiven. Der bedingte Präprozessorblock wird mit der #endif-Direktive abgeschlossen.
[bearbeiten] Auswertung von Bedingungen
[bearbeiten] #if, #elif
Der Ausdruck kann Folgendes enthalten:
- unäre Operatoren in Form von
definedBezeichner oderdefined (Bezeichner). Das Ergebnis ist 1, wenn der Bezeichner als Makroname definiert wurde, andernfalls ist das Ergebnis 0.__has_includeund__has_cpp_attribute(seit C++20) werden in diesem Kontext so behandelt, als wären sie Namen von definierten Makros.(seit C++17) - (seit C++17) __has_include-Ausdrücke, die erkennen, ob eine Header- oder Quelldatei existiert.
- (seit C++20) __has_cpp_attribute-Ausdrücke, die erkennen, ob ein gegebener Attribut-Token unterstützt wird und welche unterstützte Version.
Nach der gesamten Makroerweiterung und Auswertung von defined-, __has_include(seit C++17)- und __has_cpp_attribute(seit C++20)-Ausdrücken wird jeder Bezeichner, der kein boolesches Literal ist, durch die Zahl 0 ersetzt (dies schließt Bezeichner ein, die lexikalische Schlüsselwörter sind, aber keine alternativen Token wie and).
Anschließend wird der Ausdruck als ganzzahliger konstanter Ausdruck ausgewertet.
Wenn der Ausdruck einen Wert ungleich Null ergibt, wird der gesteuerte Codeblock einbezogen, andernfalls wird er übersprungen.
Hinweis: Bis zur Klärung des CWG-Problems 1955 unterschied sich #if Bedingung1 ... #elif Bedingung2 von #if Bedingung1 ... #else gefolgt von #if Bedingung2, da im Fall, dass Bedingung1 wahr ist, der zweite #if übersprungen wird und Bedingung2 keine wohlgeformte Bedingung sein muss, während die Bedingung2 eines #elif ein gültiger Ausdruck sein muss. Seit CWG 1955 wird auch ein #elif übersprungen, dem ein übersprungener Codeblock vorausgeht.
[bearbeiten] Kombinierte Direktiven
Prüft, ob der Bezeichner als Makroname definiert wurde.
#ifdef Bezeichner ist im Wesentlichen äquivalent zu #if defined Bezeichner.
#ifndef Bezeichner ist im Wesentlichen äquivalent zu #if !defined Bezeichner.
|
|
(seit C++23) |
[bearbeiten] Hinweise
Obwohl die Direktiven #elifdef und #elifndef auf C++23 abzielen, werden Implementierungen ermutigt, sie als konforme Erweiterungen in ältere Sprachmodi zurückzuportieren.
[bearbeiten] Beispiel
#define ABCD 2 #include <iostream> int main() { #ifdef ABCD std::cout << "1: yes\n"; #else std::cout << "1: no\n"; #endif #ifndef ABCD std::cout << "2: no1\n"; #elif ABCD == 2 std::cout << "2: yes\n"; #else std::cout << "2: no2\n"; #endif #if !defined(DCBA) && (ABCD < 2*4-3) std::cout << "3: yes\n"; #endif // Note that if a compiler does not support C++23's #elifdef/#elifndef // directives then the "unexpected" block (see below) will be selected. #ifdef CPU std::cout << "4: no1\n"; #elifdef GPU std::cout << "4: no2\n"; #elifndef RAM std::cout << "4: yes\n"; // expected block #else std::cout << "4: no!\n"; // unexpectedly selects this block by skipping // unknown directives and "jumping" directly // from "#ifdef CPU" to this "#else" block #endif // To fix the problem above we may conditionally define the // macro ELIFDEF_SUPPORTED only if the C++23 directives // #elifdef/#elifndef are supported. #if 0 #elifndef UNDEFINED_MACRO #define ELIFDEF_SUPPORTED #else #endif #ifdef ELIFDEF_SUPPORTED #ifdef CPU std::cout << "4: no1\n"; #elifdef GPU std::cout << "4: no2\n"; #elifndef RAM std::cout << "4: yes\n"; // expected block #else std::cout << "4: no3\n"; #endif #else // when #elifdef unsupported use old verbose `#elif defined` #ifdef CPU std::cout << "4: no1\n"; #elif defined GPU std::cout << "4: no2\n"; #elif !defined RAM std::cout << "4: yes\n"; // expected block #else std::cout << "4: no3\n"; #endif #endif }
Mögliche Ausgabe
1: yes 2: yes 3: yes 4: no! 4: yes
[bearbeiten] Fehlerberichte
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 1955 | C++98 | Der Ausdruck von #elif war erforderlich, gültig zu sein. |
#elif wird übersprungen. |
[bearbeiten] Siehe auch
| C-Dokumentation für Bedingte Einbindung
|