Spec-Zone.ru › C++

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)
1) Includes the code block it controls if constant-expression evaluates to a nonzero value.
2) Equivalent to #if defined identifier .
3) Equivalent to #if !defined identifier .
4) Includes the code block it controls if the previous #if block and all intervening #elif blocks are skipped, and constant-expression evaluates to a nonzero value.
5) Equivalent to #elif defined identifier .
6) Equivalent to #elif !defined identifier .
7) Includes the code block it controls if the previous #if block and all intervening #elif blocks are skipped.
8) Terminates the current #if, #elif or #else block.

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:

  • defined expressions (see below), which detects whether an identifier is defined as a macro name.
  • __has_include expressions, which detects whether a header or source file exists.
(since C++17)
  • __has_cpp_attribute expressions, which detects whether a given attribute token is supported and its supported version.
(since C++20)
  • __has_embed expressions, which detects whether a resource is available to be embedded.
(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.
  • It is __has_include.
(since C++17)
  • It is __has_cpp_attribute.
(since C++20)
  • It is __has_embed.
(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 limit, prefix, suffix or if_empty is encountered during the macro expansion between the opening and closing parentheses of a __has_embed expression, and the identifier is defined as a macro, the program is ill-formed.

#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):
  • int acts as it has the same representation as long.
  • unsignedint acts as it has the same representation as unsignedlong.
(until C++11)
  • All signed integer types act as they have the same representation as std::intmax_t.
  • All unsigned integer types act as they have the same representation as std::uintmax_t.
(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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API