HPlogo HP C++ Programmer's Guide: HP 9000 Series Workstations and Servers > Chapter 2 The HP C++ Preprocessor

Macro Replacement

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

You can define C++ macros to substitute text in your source file.

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 C++ concatenates adjacent strings.

For example,

#include <iostream.h>
#define display(arg) cout << #arg << "\n"  //define the macro
main()
{
     display(any string you want to use);    //use the macro
}

After HP C++ 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 special ## operator to form other tokens by concatenating tokens used as actual arguments. Each instance of the ## operator is deleted and the tokens preceding and following the ## are concatenated into a single token. If either of these names is a formal parameter of the macro, the corresponding argument at invocation is used. This is useful in forming unique variable names within macros.

Example 1

The following illustrates the ## operator:

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

main()
{
     int concat(fire,fly);
     concat(fire,fly) = 1;
     printf("%d \n",concat(fire,fly));
}

Preprocessing the preceding program yields the following:

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";
main()
{
    show_me(1);
}

Preprocessing this example yields the following code for the main procedure:

main()
{
    int var1=1; cout << "var" << "1" << " is " << var1 << "\n";
}

After compiling the code with CC and running the resulting executable file, you get the following results:

var1 is 1

Spaces around the # and ## are optional.

NOTE: The # and ## operators are only valid when using the ANSI C mode preprocessor, which is the default preprocessor. They are not supported when using the compatibility mode preprocessor.

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 re-scanned for further macro expansions.

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. See "Constants" in Chapter 1 “Overview of HP C++” for more information. 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));

Examples

Following are additional macro 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. Refer to "Constant Data Types" in Chapter 1 “Overview of HP C++”, "Overview of HP C++," for details.

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" below), HP C++ provides the __DATE__, __TIME__, __STDCPP__, __cplusplus and c_plusplus| predefined macros. Table 2-1 “Predefined Macros” describes the complete set of macros that are predefined to produce special information. They may not be undefined.

Table 2-1 Predefined Macros

Macro NameDescription

__cplusplus c_plusplus

Produces the decimal constant 1, indicating that the implementation supports C++ features. You should use __cplusplus because c_plusplus will be phased out in a future release.

__DATE__

Produces the date of compilation in the form Mmm dd yyyy.

__FILE__

Produces the name of the file being compiled.

|__LINE__

Produces the current source line number.

__STDCPP__

Produces the decimal constant 1, indicating that the preprocessor is in the ANSI C mode.

__TIME__

Produces the time of compilation in the form hh:mm:ss.

 

NOTE: __DATE__, __TIME__, and __STDCPP__ are not defined in the compatibility mode preprocessor.
© Hewlett-Packard Development Company, L.P.