Conditional inclusion
The preprocessor supports conditional inclusion of parts of source file. This behavior is controlled by the following directives.
Syntax
#if constant-expression | (1) | |
#ifdef identifier | (2) | |
#ifndef identifier | (3) | |
#elif constant-expression | (4) | |
#elifdef identifier | (5) | (since C++23) |
#elifndef identifier | (6) | (since C++23) |
#else | (7) | |
#endif | (8) |
Explanation
The conditional preprocessing block starts with an #if, #ifdef or #ifndef directive, then optionally includes any number of #elif, #elifdef, or #elifndef(since C++23) directives, then optionally includes at most one #else directive and is terminated with #endif directive. Any nested conditional preprocessing blocks are processed separately.
Except for #endif, each of these directives control the code block until the first #elif, #elifdef, #elifndef(since C++23), #else or #endif directive not belonging to any nested conditional preprocessing blocks.
#if, #ifdef and #ifndef directives test the specified condition (see below):
- If it evaluates to true (nonzero value), the controlled code block is included. In this case, all subsequent #else, #elifdef, #elifndef,(since C++23) and #elif blocks till the associated #endif directive are skipped.
- Otherwise, the controlled code block is skipped. In this case:
- If the subsequent non-nested directive is #endif, no code block is included.
- If the subsequent non-nested directive is #else, the code block it controls is unconditionally included.
- Otherwise, the subsequent non-nested (#elif, #elifdef, or #elifndef(since C++23)) directive acts as if it was an #if directive: checks for condition, includes or skips the controlled code block based on the result, and in the latter case processes its subsequent directive.
#define MY_MACRO 2 #if MY_MACRO > 0 // included #elif MY_MACRO > 1 // the condition of the #if-block is passed, no more tests // not included #endif
Testing conditions
Preprocessing-exclusive expressions
constant-expression may contain the following expressions:
-
definedexpressions (see below), which detects whether an identifier is defined as a macro name.
| (since C++17) |
| (since C++20) |
| (since C++26) |
defined expressions
defined identifier | (1) | |
defined( identifier ) | (2) |
The defined expression evaluates to 1 if identifier is currently defined as macro name, or 0 if it is not.
An identifier is defined as macro name in the context of condition inclusion directive if any of the following conditions is satisfied:
- It has one or more active macro definitions.
- It is predefined.
| (since C++17) |
| (since C++20) |
| (since C++26) |
Macro expansion
Except for the identifier s of defined expressions, macros in constant-expression s will be expanded prior to condition evaluation.
If the preprocessing token defined is generated as a result of this replacement process, or use of the defined unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined(until C++26)the program is ill-formed, no diagnostic required(since C++26).
#define MACRO defined // UB until C++26, IFNDR since C++26 #if MACRO MACRO // “defined” is generated by macro expansion #if defined (A + B) // “A + B” is not an identifier #if defined 1 // 1 is a literal, not an identifier #if defined // no argument provided
| If an identifier #define limit 0
#define HAS_EMBED_LIMIT_10(file) __has_embed(file limit(10))
// ill-formed
#if __has_embed("e.dat" limit(10)) // “limit” is defined as a macro
#if HAS_EMBED("e.dat") // macro expansion encounters the macro “limit”
| (since C++26) |
Condition evaluation
After all macro expansion and evaluations of the preprocessing-exclusive expressions described above, each remaining identifier other than true and false is replaced with the preprocessing number 0 (this includes identifiers that are lexically keywords, but not alternative tokens like and).
After that, each preprocessing token is converted into a token. The resulting tokens comprise the condition controlloing the associated code block, which is evaluated as an constant expression, with the following changes:
- During token conversion and condition evaluation (including determining the value of character literals):
| (until C++11) |
| (since C++11) |
- Each subexpression with type bool is subjected to integral promotion before processing continues.
If the condition evaluates to nonzero value, the controlled code block is included and skipped otherwise.
Notes
While #elifdef and #elifndef directives target C++23, implementations are encouraged to backport them to the older language modes as conforming extensions.
Example
#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
}
Possible output:
1: yes 2: yes 3: yes 4: no! 4: yes
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 1955 | C++98 |
#elif must have a valid expression even if it followed a #if/#elif block whose condition was true | the expression of #elif is skipped in this case |
See also
| C documentation for Conditional inclusion |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/preprocessor/conditional