| United States-English |
|
|
|
![]() |
HP Itanium-based Systems: HP aC++/HP C Programmer's Guide > Chapter 4 Preprocessing DirectivesOverview of the Preprocessor |
|
A preprocessor is a text-processing program that manipulates the text within your source file. You enter preprocessing directives into your source file to direct the preprocessor to perform certain actions on the source file. For example, the preprocessor can replace tokens in the text, insert the contents of other files into the source file, or suppress the compilation of part of the file by conditionally removing sections of the text. It also expands preprocessor macros and conditionally strips out comments. The general syntax for a preprocessor directive is:
Following are rules and guidelines for using preprocessor directives:
Preprocessor directives provide the following functionality:
You can include the contents of other files within the source file using the #include directive. The #include preprocessing directive causes HP aC++ to read source input from the file named in the directive. Usually, include files are named filename.h. If the file name is enclosed in angle brackets (< >), the default system directories are searched to find the named file. If the file name is enclosed in double quotation marks (“ “), by default, the directory of the file containing the #include line is searched first, then directories named in -I options in left-to-right order, and last directories on a standard list. The arguments to the #include directive are subject to macro replacement before being processed. Thus, if you use a #include directive of the form #include identifier, identifier must be a previously defined macro that when expanded produces one of the above defined forms of the #include directive. Refer to Macro Replacement (#define, #undef) for more information on macros. Error messages produced by HP aC++ indicate the name of the #include file where the error occurred, as well as the line number within the file. You can define C++ macros to substitute text in your source file.
A #define preprocessing directive defines the identifier as a macro name that represents the replacement-list. This is of the form: #define identifier [replacement-list] The macro name is then replaced by the list of tokens wherever it appears in the source file (except inside of a string, character constant, or comment). A macro definition remains in force until it is undefined through the use of the #undef directive or until the end of the compilation unit. The replacement-list must fit on one line. If the line becomes too long, it can be broken up into several lines provided that all lines but the last are terminated by a backslash (\) character. The following is an example: #define mac very very long\ replacement string The \ must be the last character on the line. You cannot add any spaces or comments after it. Macros can be redefined without an intervening #undef directive. Any parameter used must agree in number and spelling with the original definition, and the replacement lists must be identical. All white space within the replacement-list is treated as a single blank space regardless of the number of white-space characters you use. For example, the following #define directives are equivalent:
The replacement-list may be empty. If the token list is not provided, the macro name is replaced with no characters. You can create macros that have parameters. The syntax of the #define directive that includes formal parameters is as follows:
The macro name is identifier. The formal parameters are provided by the identifier-list enclosed in parentheses. The open parenthesis ( ‘(’ ) must immediately follow the identifier with no intervening white space. If there is a space between the identifier and the parenthesis, the macro is defined as if it were the first form and the replacement-list begins with the ( character. The formal parameters to the macro are separated with commas. They may or may not appear in the replacement-list. When the macro is invoked, the actual arguments are placed in a parenthesized list following the macro name. Commas enclosed in additional matching pairs of parentheses do not separate arguments but are themselves components of arguments. The actual arguments replace the formal parameters in the token string when the macro is invoked. If a formal parameter in the macro definition directive’s replacement string is preceded by a # operator, it is replaced by the corresponding argument from the macro invocation, preceded and followed by a double-quote character (") to create a string literal. This feature, available only with the ANSI C preprocessor, may be used to turn macro arguments into strings. This feature is often used with the fact that HP aC++ concatenates adjacent strings. Example:
After HP aC++ expands the macro definition in the preceding program, the following code results:
Use the ## operator within macros to create a single token out of two other tokens. Usually, one of these two tokens is the actual argument for a macro-parameter. Upon expansion of the macro, each instance of the ## operator is deleted and the tokens preceding and following the ## are concatenated into a single token. The following illustrates the ## operator:
Preprocessing this program yields the following:
You can use the # and ## operators together:
Preprocessing this example yields the following code for the main procedure:
After compiling the code with aCC and running the resulting executable file, you get the following results: var1 is 1 Spaces around the # and ## are optional. In both the # and ## operations, the arguments are substituted as is, without any intermediate expansion. After these operations are completed, the entire replacement text is rescanned for further macro expansions.
The most common use of the macro replacement is in defining a constant. In C++ you can also declare constants using the keyword const. Rather than explicitly putting constant values in a program, you can name the constants using macros, then use the names in place of the constants. By changing the definition of the macro, you can more easily change the program: #define ARRAY_SIZE 1000float x[ARRAY_SIZE]; In this example, the array x is dimensioned using the macro ARRAY_SIZE rather than the constant 1000. Note that expressions that may use the array can also use the macro instead of the actual constant: for (i=0; i<<ARRAY_SIZE; ++i) f+=x[i]; Changing the dimension of x means only changing the macro for ARRAY_SIZE. The dimension changes and so do all of the expressions that make use of the dimension. Some other common macros used by C programmers include: #define FALSE 0 #define TRUE 1 The following macro is more complex. It has two parameters and produces an inline expression which is equal to the maximum of its two parameters: #define MAX(x,y) ((x) > (y) ? (x) : (y)) Parentheses surrounding each argument and the resulting expression ensure that the precedences of the arguments and the result interact properly with any other operators that might be used with the MAX macro. Using a macro definition for MAX has some advantages over a function definition. First, it executes faster because the macro generates in-line code, avoiding the overhead of a function call. Second, the MAX macro accepts any argument types. A functional implementation of MAX would be restricted to the types defined for the function. Note that because each argument each argument to the MAX macro appears in the token string more than once, the actual arguments to the MAX macro may have undesirable side effects. The following example may not work as expected because the argument a is incremented two times when a is the maximum: i = MAX(a++, b); This expression is expanded to: i = ((a) > (b) ? (a) : (b)) Given this macro definition, the statement i = MAX(a, b+2); is expanded to: i = ((a) > (b+2) ? (a) : (b+2));
This macro tests a number and returns TRUE if the number is odd. It returns FALSE otherwise. In C++ you can use named constants and inline functions to achieve results similar to using macros. You can use const variables in place of macros. You can also use inline functions in many C++ programs where you would have used a function-like macro in a C program. Using inline functions reduces the likelihood of unintended side effects, since they have return types and generate their own temporary variables where necessary. The following program illustrates the replacement of a macro with an inline function:
In addition to __LINE__ and __FILE__, HP aC++ provides the following predefined macros. The list describes the complete set of predefined macros that produce special information. They cannot be undefined nor changed. To use some HP-UX system functions you may need to define the symbol __HPUX_SOURCE. See stdsyms(5) manpage or the HP-UX Reference Manual for more information. Use #assert and #unassert to set a predicate name or predicate name and token to be tested with a #if directive. Note that you must also specify the -ext option at compile and link time. #assert sets the predicate-name [token-name] to true. #unassert sets the predicate-name [token-name] to false. Note that when testing a predicate, it must be preceded by the # character. HP aC++ predefines the following predicates:
Example:
Conditional compilation directives allow you to delimit portions of code that are compiled only if a condition is true.
Here, constant-expression may also contain the defined operator: defined identifier defined (identifier) The constant-expression is like other C++ integral constant expressions except that all arithmetic is carried out in long int precision. Also, the expressions cannot use the sizeof operator, a cast, an enumeration constant, or a const object. You can use #if, #ifdef, or #ifndef to mark the beginning of the block of code that will only be compiled conditionally. An #else directive optionally sets aside an alternative group of statements. You mark the end of the block using an #endif directive. The following #if directive illustrates the structure of conditional compilation:
(Code that compiles if the expression evaluates to a nonzero value.)
(Code that compiles if the expression evaluates to zero.)
You can use the defined operator in the #if directive to use expressions that evaluate to 0 or 1 within a preprocessor line. This saves you from using nested preprocessing directives. The parentheses around the identifier are optional. Below is an example:
Without using the defined operator, you would have to include the following two directives to perform the above example:
The #if preprocessing directive has the form: #if constant-expression Use #if to test an expression. HP aC++ evaluates the expression in the directive. If the expression evaluates to a non-zero value (TRUE), the code following the directive is included. Otherwise, the expression evaluates to FALSE and HP aC++ ignores the code up to the next #else, #endif, or #elif directive. All macro identifiers that appear in the constant-expression are replaced by their current replacement lists before the expression is evaluated. All defined expressions are replaced with either 1 or 0 depending on their operands. Whichever directive you use to begin the condition (#if, #ifdef, or #ifndef), you must use #endif to end the if section. The following preprocessing directives test for a definition: #ifdef identifier #ifndef identifier These preprocessing directives behave like the #if directive, but #ifdef is considered true if the identifier was previously defined using a #define directive or the -D option. #ifndef is considered TRUE if the identifier is not defined yet. Use the #else directive to specify an alternative section of code to be compiled if the #if, #ifdef, or #ifndef conditions fail. The code after the #else directive is included if the code following any of the #if directives is not included. Use the #else directive to specify an alternative section of code to be compiled if the #if, #ifdef, or #ifndef conditions fail. The code after the #else directive is included if the code following any of the #if directives is not included. The #elif constant-expression directive tests whether a condition of the previous #if, #ifdef, or #ifndef was false. #elif has the same syntax as the #if directive and can be used in place of an #else directive to specify an alternative set of conditions. The following examples show valid combinations of conditional compilation directives:
Following are more examples showing conditional compilation directives:
You can cause HP aC++ to set line numbers during compilation from a number specified in a line control directive. The resulting line numbers appear in error message references, but do not alter the line numbers of the actual source code. The #line preprocessing directive causes HP aC++ to treat lines following it in the program as if the name of the source file were filename and the current line number were digit-sequence. This serves to control the file name and line number that are given in diagnostic messages. This feature is used primarily by preprocessor programs that generate C++ code. It enables them to force HP aC++ to produce diagnostic messages with respect to the source code that is input to the preprocessor rather than the C++ source code that is output. HP aC++ defines two macros that you can use for error diagnostics. The first is __LINE__, an integer constant equal to the value of the current line number. The second is __FILE__, a quoted string literal equal to the name of the input source file. You can change __FILE__ and __LINE__ using #include or #line directives. The -AA -D_HP_NONSTD_FAST_IOSTREAM Performance Improvement macro can be used to improve the -AA iostream performance. #define _HP_NONSTD_FAST_IOSTREAM 1 (or) aCC options -D_HP_NONSTD_FAST_IOSTREAM This macro enables the following non-standard features:
Enabling this macro can provide noticeable performance improvement if the application uses iostreams often.
A #pragma directive is an instruction to the compiler. Use a pragma to control the actions of the compiler in a particular portion of a program without affecting the program as a whole. The #pragma directive is ignored by the preprocessor, and instead is passed on to the HP aC++ compiler. It provides implementation-dependent information to HP aC++. Any pragma that is not recognized by HP aC++ will generate a warning from the compiler. The #error directive causes a diagnostic message, along with any included token arguments, to be produced by HP aC++.
The #warning directive causes a diagnostic message, along with any included token arguments, to be produced by HP aC++. The C++ source code character set is a superset of the ISO 646-1983 Invariant Code Set. To enable you to use only the reduced set, you can use trigraph sequences to represent those characters not in the reduced set. A trigraph sequence is a set of three characters that is replaced by a corresponding single character. The preprocessor replaces all trigraph sequences with the corresponding character. The list below gives the complete list of trigraph sequences and their replacement characters. The following are all the trigraph sequences and their respective replacement characters:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||