Team LiB   Previous Section   Next Section
#if directive Tests a condition

#if constant-expression

The #if directive begins a region of conditional compilation, that is, a region within a source file where preprocessor directives determine whether the code in the region is compiled. A conditional region starts with #ifdef, #ifndef, or #if and ends with #endif. Each region can have any number of #elif directives and an optional #else directive after all the #elif directives. The basic form to use is:

#if defined(_  _win32_  _)
  const char os[] = "Microsoft Windows";
#elif defined(__linux__) or defined(_  _unix_  _)
  const char os[] = "UNIX (or variant)";
#elif defined(_  _vms_  _)
  const char os[] = "VMS";
#else
  const char os[] = "(unknown)";
#endif

Macros in the directive argument are expanded, except for the operands of the defined operator. The constant expression is evaluated, and if the result is nonzero, the #if condition is true, and the code in the region that immediately follows is compiled. The region ends with #else, #elif, or #endif. If the #if expression is false, the condition for the next #elif is evaluated, and if that expression is true, its region is compiled, and so on. If all #elif expressions are false, and #else is present, its region is compiled. Conditional processing ends with the corresponding #endif directive.

Conditionals can be nested. Within an inner region, the preprocessor keeps track of conditional directives even if the region is not being compiled, so conditional directives can be properly matched.

The #if and #elif directives take a single parameter, a constant expression. The expression differs slightly from non-preprocessor constant expressions:

  • You can use the defined operator.

  • Integers are long, that is, int values (and values that are promoted to int) have the same representation as long int, and unsigned int values have the same representation as unsigned long. All bool values are promoted to integers, including the keywords true and false.

  • Character literals are converted to the execution character set. The numeric value of a character in a preprocessor expression is not necessarily the same as the value of the same character in a non-preprocessor expression. A character may have a negative value.

  • Keywords that are alternative operators for symbolic operators (i.e., and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, and xor_eq) have their usual meaning, although it is ineffective to try using assignment operators (and_eq, or_eq, and xor_eq) in an #if or #elif condition.

  • figs/acorn.gif

    Other identifiers and keywords that remain after macro expansion are replaced by 0. (It might seem strange to convert keywords, such as sizeof, to the integer 0, but that is the rule. Some compilers fail to follow this particular rule.) One consequence of this rule is that you cannot use type casts or new, delete, sizeof, throw, or typeid expressions in an #if or #elif condition.

Conditional directives are most often used to guard header files from multiple inclusion. All the standard headers are guarded, so including them more than once has no harmful effects. This is important because an implementation might include one header in another header. For example, <map> might include <utility> to get the declaration for the pair<> template. If you explicitly #include <map> and #include <utility>, you might end up including <utility> more than once.

Another common use is for system- or compiler-specific code. Every compiler predefines one or more macros to identify the compiler and possibly the host operating system (such as _ _linux_ _ or _ _GNUC_ _). Consult your compiler's documentation to learn which macro names are predefined.

Examples

Example 11-4 shows one way to nest conditional directives.

Example 11-4. Nesting conditional directives
#define zero zero  // Identifiers are converted to 0.
#define one  true  // Bool expressions are promoted to int.

#if one
// This region is compiled.
  #if zero
  This region can contain erroneous C++ code. The code is not
  compiled, so the errors do not matter.
  #else // This #else matches the inner #if.
    // This region is compiled.
    const int zero = 0;
  #endif // This #endif matches the inner #if.
  int x = zero;
#else
  This #else matches the outer #if. Because the #if
  condition was true, the #else region is not compiled.
#endif

You can guard your own headers by using conditional directives to define a guard macro and using the guard macro to ensure the file's contents are compiled only when the macro is not defined, as shown in Example 11-5.

Example 11-5. Guarding a header against multiple inclusion
// In the header file employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
// Thus, the entire contents of the file are compiled only when EMPLOYEE_H is not
// defined. The first time the file is #included, the macro is not defined, in
// which case it is immediately defined. The second and subsequent times the same
// header is included in the same source file; the macro and conditional
// directives ensure that the entire file is skipped.

class employee { ... };

#endif  // End of employee.h

See Also

#elif directive, #else directive, #endif directive, #ifdef directive, #ifndef directive

    Team LiB   Previous Section   Next Section