search    
Hewlett-Packard
Preprocessing Directives
Preprocessing directives function as compiler control lines. They allow you to direct the compiler to perform certain actions on the source file.

HP aC++ has its own, internal, preprocessor which is similar to the standalone preprocessor cpp.ansi. When you issue the aCC command, your source files are automatically preprocessed.

This section discusses the following topics:

Preprocessor directives provide the following functionality:
Overview 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.


Syntax

The general syntax for a preprocessor directives is:

preprocessor-directive ::=
   include-directive newline
   macro-directive newline
   conditional-directive newline
   line-directive newline
   pragma-directive newline
   error-directive newline
   trigraph-directive newline
   warning-directive newline


Usage Guidelines

Following are rules and guidelines for using preprocessor directives:

  • A preprocessor directive must be preceeded by a pound sign (#). White-space characters may precede the # character.)
  • The # character is followed by any number of spaces and horizontal tab characters and a preprocessor directive.
  • A preprocessor directive is terminated by a newline character.
  • Preprocessor directives, as well as normal source lines, can be continued over several lines. End the lines that are to be continued with a backslash (\).
  • Some directives can take actual arguments or values.
  • Comments in the source file that are not passed through the preprocessor are replaced with a single white space character (ASCII character number decimal 32).

Examples

Following are some examples of preprocessor directives:

include-directive:
#include <iostream.h>

macro-directive:
#define MAC x+y

conditional-directive:
#ifdef MAC
#define x 25
#endif

line-directive:
#line 5 "myfile"

pragma-directive:
#pragma OPTIMIZE ON

error-directive:
#error "FLAG not defined!"

trigraph-directive:
??=line 5 "myfile"

warning-directive:
#warning "FLAG not defined!"

Source File Inclusion (#include)
You can include the contents of other files within the source file using the #include directive.

Syntax:

include-directive ::=
     #include <filename>
     #include "filename"
     #include identifier
                                
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.

Files that are included may contain #include directives themselves. HP aC++ supports a nesting level of at least 35 #include files.

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.

Examples:

#include <iostream.h>
#include "myheader.h"
#ifdef   MINE
#   define  filename  "file1.h"
#else
#   define  filename  "file2.h"
#endif
#include filename
Macro Replacement (#define, #undef)
You can define C++ macros to substitute text in your source file. This section is organized into the following topics:
Syntax

The general syntax for a macro replacement directive is:

macro-directive ::=
#define identifier [replacement-list]
#define identifier( [identifier-list] )  [replacement-list]
#undef identifier

replacement-list ::=
     token
     replacement-list token
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 backslash character \ 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 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:

#define foo x + y
#define foo x   +   y
The replacement-list may be empty. If the token list is not provided, the macro name is replaced with no characters.


Macros with Parameters

You can create macros that have parameters. The syntax of the #define directive that includes formal parameters is as follows:

#define identifier([identifier-list]) [replacement-list]

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 open parenthesis "(", the macro is defined as if it were the first form and the replacement-list begins with the opwn prenthesis "(" 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.


Specifying String Literals with the # Operator

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:

#include <iostream.h>
#define display(arg) cout << #arg << "\n"  //define the macro
int main()
{
     display(any string you want to use);    //use the macro
}
After HP aC++ expands the macro definition in the preceding program, the following code results:
  ...
main ()
{
     cout << "any string you want to use" << "\n";
}

Concatenating Tokens with the ## Operator

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.

Example 1:

     // define the macro; the ## operator
     // concatenates arg1 with arg2
#define concat(arg1,arg2) arg1 ## arg2

int main()
{
     int concat(fire,fly);
     concat(fire,fly) = 1;
     printf("%d \n",concat(fire,fly));
}
Preprocessing the preceding program yields the following:
int main()
{
     int firefly ;
     firefly = 1;
     printf("%d \n",firefly );
}
Example 2:

You can use the # and ## operators together:

#include <iostream.h>
#define show_me(arg) int var##arg=arg;\
    cout << "var" #arg " is " << var##arg << "\n";
int main()
{
    show_me(1);
}
Preprocessing this example yields the following code for the main procedure:
int main()
{
    int var1=1; cout << "var" "1" " is " << var1 << "\n";
}
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.

Note: The result of the preprocessor concatenation operator ## must be a _single_ token. In particular, the use of ## to concatenate strings is redundant and not legal C or C++. For example:

#include <stdio.h>

#define concat_token(a, b) a##b
#define concat_string(a, b) a b

int main() {
    // Wrong:
    printf("%s\n", concat_token("Hello,", " World!"));
    // Correct:
    printf("%s\n", concat_string("Hello,", " World!"));
    // Best: (macro not needed at all!):
    printf("%s\n", "Hello," " World!");
}


Using Macros to Define Constants

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.

In the following example, the array x is dimensioned using the macro ARRAY_SIZE rather than the constant 1000.

#define ARRAY_SIZE 1000
float x[ARRAY_SIZE];
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.


Other Macros

Some other common macros include:

  • #define FALSE 0
  • #define TRUE 1
  • #define MAX(x,y)
The #define MAX(x,y) 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))

Note: 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.

Because 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 might not work as expected because the argument a is incremented two times when a is the maximum:

i = MAX(a++, b);

This is expanded to:

i = ((a) > (b) ? (a) : (b))

Given the above macro definition, the statement

i = MAX(a, b+2);

is expanded to:

i = ((a) > (b+2) ? (a) : (b+2));

More Examples:

// This macro tests a number and returns TRUE if
// the number is odd.  It returns FALSE otherwise.
#define isodd(n)  ( ((n % 2) == 1) ? (TRUE) : (FALSE))

// This macro skips white spaces.
#define eatspace()while((c=getc(input))==c=='\n'c\
 = '\t' )

Using Constants and Inline Functions Instead of Macros

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.

Example:

The following program illustrates the replacement of a macro with an inline function:

#include <stream.h>
#define distance1(rate,time) (rate * time)
// replaced by :
inline int distance2 ( int rate, int time )
{
     return ( rate * time );
}
int main()
{
     int i1 = 3, i2 = 3;

     printf("Distance from macro : %d\n",
             distance1(i1,i2) );
     printf("Distance from inline function : %d\n",
             distance2(i1,i2) );
}

Predefined Macros

The following list describes the complete set of predefined macros that produce special information. They cannot be undefined nor changed.

  • __cplusplus produces the decimal constant 199711L, indicating that the implementation supports ANSI/ISO C++ International Standard features.
    #if (__cplusplus >= 199711L)
    #include
    #else
    #include
  • __DATE__ produces the date of compilation in the form Mmm dd yyyy.

  • __FILE__ produces the name of the file being compiled.

  • __HP_aCC identifies the HP aC++ compiler driver version. It is represented up to six digits in the format mmnnxx, where mm is the major version number, nn is the minor version number, and xx is any extension. For example, for version A.06.00, __HP_aCC=60000.

  • __HP_cc identifies the HP C compiler driver version. It is represented up to six digits in the format mmnnxx, where mm is the major version number, nn is the minor version number, and xx is any extension. For example, for version A.06.00, __HP_cc=60000.

  • __LINE__ produces the current source line number.

  • __LP64__ is defined for +DD64.

  • __ia64 is defined.

  • _ILP32 is defined for +DD32.

  • _LP64 is defined for +DD64.

  • __STDCPP__ produces the decimal constant 1, indicating that the preprocessor is in ANSI C/C++ mode.

  • __TIME__ produces the time of compilation in the form hh:mm:ss.

Assertions (#assert, #unassert)
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.

Syntax:

#assert predicate-name[token-name]
#unassert predicate-name[token-name]

The #assert directive sets the predicate-name [token-name] to true. The #unassert directive 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 for PA-RISC 2.0 architecture:

  • #assert machine(parisc)
  • #assert cpu(parisc)
  • #assert system(unix)
  • #assert model(lp64) // when +DD64 is used
  • #assert model(ilp32) // default
  • #assert endian(big)
Example:
int main()
{
#assert dimensions(three)  // Set predicate and token to true.

#if #dimensions(two)
#error "May not compile in 2 dimensions"
#endif

#if #dimensions(three)
int x, y, z;
#endif

#unassert dimensions       // Set predicate and all tokens to false.
}
Example:

When compiling, you can test the compiler's predefined assertions. For example:

int main()
{

#if #model(lp64)
  // code for 64-bit processor (+DD64)
#else
#if #model(ilp32)
  // code for 32-bit processor
#endif
}
Conditional Compilation (#if, #ifdef, .. #endif)
Conditional compilation directives allow you to delimit portions of code that are compiled only if a condition is true. This section is organized into the following topics:
Conditional Compilation Syntax

conditional-directive ::=
#if                  constant-expression newline
#ifdef               identifier newline [group]
#ifndef              identifier newline [group]
#else                newline [group]
#elif                constant-expression newline [group]
#endif
Here, constant-expression may also contain the defined operator:
defined identifier
defined (identifier)
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:

#if constant-expression
     ...

(Code that compiles if the 
expression evaluates to a 
non-zero value.) 

     ...
#else
     ...

(Code that compiles if the 
expression evaluates to 
zero.) 

     ...
#endif
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, a const object.


Using the defined Operator

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:

#if defined (MAX) && ! defined (MIN)
     ...
Without using the defined operator, you would have to include the following two directives to perform the above example:
#ifdef max
#ifndef min

Using the #if Directive

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 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.


The #endif Directive

Whichever directive you use to begin the condition (#if, #ifdef, or #ifndef), you must use #endif to end the if section.


Using the #ifdef and #ifndef Directives

The following preprocessing directives test for a definition:

#ifdef identifier
#ifndef identifier
They 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 identifier is not yet defined.


Nesting Conditional Compilation Directives

You can nest conditional compilation constructs. Delimit portions of the source program using conditional directives at the same level of nesting, or with a -D option on the command line.


Using the #else Directive

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.


Using the #elif Directive

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.


Examples

The following examples show valid combinations of conditional compilation directives:

#ifdef SWITCH        // compiled if SWITCH is defined
#else                // compiled if SWITCH is undefined
#endif               // end of if

#if defined(THING)   // compiled if THING is defined
#endif               // end of if

#if A > 47           // compiled if A is greater than 47
#else
#if A < 20           // compiled if A is less than 20
#else                // compiled if A is greater than or equal
                     // to 20 and less than or equal to 47
#endif               // end of if, A is less than 20
#endif               // end of if, A is greater than 47
Following are some more examples showing conditional compilation directives:
#if (LARGE_MODEL)
#define INT_SIZE 32     // Defined to be 32 bits.
#elif defined (PC) && defined (SMALL_MODEL)
#define INT_SIZE 16     // Otherwise, if PC and SMALL_MODEL
                        // are defined, INT_SIZE is defined
                        // to be 16 bits.
#endif

#ifdef DEBUG                            // If DEBUG is defined, display
cout << "table element : \n";           // the table elements.
for (i=0; i << MAX_TABLE_SIZE; ++i)
     cout << i << " " << table[i] << '\n';
#endif

Line Control (#line)
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.)

Syntax:

line-directive ::=
     #line digit-sequence [filename]
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.

  1. __LINE__: An integer constant equal to the value of the current line number.
  2. __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.

Example:

#line 5 "myfile"

IOSTREAM Perfomance Improvement
The -AA -D_HP_NONSTD_FAST_IOSTREAM Performance Improvement Macro can be used to improve the -AA iostream performance.

Syntax:

#define _HP_NONSTD_FAST_IOSTREAM 1        (OR)
aCC options -D_HP_NONSTD_FAST_IOSTREAM

This macro enables the following non-standard features:
  • Sets std::ios_base::sync_with_stdio(false), which disables the default synchronization with stdio.
  • Sets std::cin.tie(0), which unties the cin from other streams.
  • Replaces all occurrences of "std::endl" with "\n".

Enabling this macro can provide noticeable performance improvement if the application uses iostreams often.

Note: Do not enable the HP_NONSTD_FAST_IOSTREAM macro in any of the following cases:
  • If the application assumes a C++ stream to be in sync with a C stream
  • If the application depends on stream flushing behavior with endl
  • If the user uses "std::cout.unsetf(ios::unitbuf)" to unit buffer the output stream.

Pragma Directive (#pragma)
A #pragma directive is an instruction to the compiler. You typically use a pragma to control the actions of the compiler in a particular portion of a program without affecting the program as a whole.

Syntax:

pragma-directive ::=
     #pragma [token-list]
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.

Example:

#pragma OPTIMIZE ON

Refer to Pragma Directives for a complete list of pragmas.

Error Directive (#error)
The #error directive causes a diagnostic message, along with any included token arguments, to be produced by HP aC++.

Syntax:

error-directive ::=
     #error [preprocessor tokens]
Examples:
     // This directive will produce the diagnostic
     // message "FLAG not defined!".
#ifndef FLAG
#error "FLAG not defined!"
#endif

     // This directive will produce the diagnostic
     // message "TABLE_SIZE must be a multiple of 256!".
#if TABLE_SIZE % 256 != 0
#error "TABLE_SIZE must be a multiple of 256!"
#endif
Warning Directive
The #warning directive causes a diagnostic message, along with any included token arguments, to be produced by HP aC++.

Syntax:

warning-directive ::=
     #warning [preprocessor tokens]
Trigraph Sequences

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:

  • ??= is replaced by #
  • ??/ is replaced by \
  • ??' is replaced by ^
  • ??( is replaced by [
  • ??) is replaced by ]
  • ??! is replaced by |
  • ??< is replaced by {
  • ??> is replaced by }
  • ??- is replaced by ~
Example:

The line below contains the trigraph sequence ??=:

??=line 5 "myfile"

When this line is compiled it becomes:

#line 5 "myfile"