Preprocessing in HP aC++

HP aC++ has its own, internal, preprocessor which is similar to the HP C preprocessor described in the HP C/HP-UX Reference Manual. When you issue the aCC command, your source files are automatically preprocessed.

Directives

By coding preprocessing directives in a source file, you request that the file be processed in a particular way.

Command Line Options

Migration

For More Information


Summary of Preprocessor Directives

Preprocessor directives provide the following functionality:


Preprocessor Directives: Syntax, Guidelines, and Examples

Syntax

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

Guidelines

Following are rules and guidelines for using preprocessor directives:

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 a given source file by using the #include directive in the source file.

Syntax:

include-directive ::=
     #include <filename>
     #include "filename"
     #include identifier

Description:

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 (&dquote; &dquote;), 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.

For More Information:

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.

Macro Syntax and Description:

Syntax:

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

replacement-list ::=
     token
     replacement-list token

Description:

A #define preprocessing directive of the form:

#define identifier [replacement-list]

defines the identifier as a macro name that represents the 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.

NOTE: 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 "\" 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:

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

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.

For 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

The following illustrates the ## operator:

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

#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:

#define ARRAY_SIZE 1000
float 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.

Other Macros

Two other macros 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))

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);
which 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

In addition to __LINE__ and __FILE__ (refer to Line Control (#line)), HP aC++ provides the predefined macros listed below. The list describes the complete set of predefined macros that produce special information. They cannot be undefined nor changed.

For More Information

To use some HP-UX system functions you may need to define the symbol __HPUX_SOURCE. See the stdsyms(5) man page if it is installed on your system, or in the HP-UX Reference Manual. (If you see the message "Man page could not be formatted," ensure the man page is installed.)

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]

Description:

#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 for PA-RISC 2.0 architecture:

#assert machine(parisc)
#assert cpu(parisc)
#assert system(unix)
#assert model(lp64)     // when +DA2.0W is used
#assert model(ilp32)    // default
#assert endian(big)

Example

int void 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 on PA-RISC 2.0 or later, you can test the compiler's predefined assertions. For example:
int void main()
{

#if #model(lp64)
  // code for 64-bit processor (+DA2.0W)
#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.

Conditional Compilation Syntax and Description

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)

Description:

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 nonzero 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, or 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 nonzero 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.

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 the 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 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]

Description:

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.

Example:

#line 5 "myfile"


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]

Description:

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

For More Information:


Error Directive (#error)

Syntax:

error-directive ::=
     #error [preprocessor tokens]

Description:

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

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 (#warning)

Syntax:

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

Description:

The #warning directive causes a diagnostic message, along with any included token arguments, to be produced by HP aC++

Examples:


Trigraph Sequences

Description:

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:

Examples:

The line below contains the trigraph sequence ??=:
     ??=line 5 "myfile"

When this line is compiled it becomes:

     #line 5 "myfile"