Preprocessor Directives

Pre-processor directives, such as #define and #include, are processed in the first stage of compiling a GXC program. All directives are replaced by the result of acting on the directive. For example, the pre-processor can replace tokens in the text, insert the contents of other files into the source file, or suppress compilation of part of the file by removing sections of text. Pre-processor lines are recognised and carried out before macro expansion. Therefore, if a macro expands into something that looks like a pre-processor command, that command is not recognised by the pre-processor.

The number sign (#) must be the first character on the line containing the directive; white-space characters can appear between the number sign and the first letter of the directive. Some directives include arguments or values. Any text that follows a directive (except an argument or value that is part of the directive) must be enclosed in comment delimiters ( /* */ ) (single-line comment delimiters (//) are not supported on directive lines). Lines containing pre-processor directives can be continued by immediately preceding the end-of-line marker with a backslash (\).

Pre-processor directives can appear anywhere in a source file, but they apply only to the remainder of the source file. 


 

#define

You can use the #define directive to give a meaningful name to a constant in your program. The two forms of the syntax are:

#define identifier token-string

#define identifier(parameter list) token-string

The #define directive substitutes token-string for all subsequent occurrences of an identifier in the source file. The identifier is replaced only when it forms a token. (See Tokens.) For instance, identifier is not replaced if it appears in a comment, within a string, or as part of a longer identifier.

A #define without a token-string removes occurrences of identifier from the source file. The identifier remains defined and can be tested using the #ifdef directive.

The token-string argument consists of a series of tokens, such as keywords, constants, or complete statements. One or more white-space characters must separate the token-string from the identifier. This white space is not considered part of the substituted text, nor is any white space following the last token of the text.

Formal parameter names appear in token-string to mark the places where actual values are substituted. Each parameter name can appear more than once in token-string, and the names can appear in any order. The number of arguments in the call must match the number of parameters in the macro definition. Liberal use of parentheses ensures that complicated actual arguments are interpreted correctly.

The second syntax form allows the creation of function-like macros. This form accepts an optional list of parameters that must appear in parentheses. References to the identifier after the original definition replace each occurrence of identifier ( parameter list ) with a version of the token-string argument that has actual arguments substituted for formal parameters. The formal parameters in the list are separated by commas. Each name in the list must be unique, and the list must be enclosed in parentheses. No spaces can separate identifier and the opening parenthesis. Use line concatenation — place a backslash (\) before the newline character — for long directives on multiple source lines. The scope of a formal parameter name extends to the new line that ends token-string.

When a macro has been defined in the second syntax form, subsequent textual instances followed by an argument list constitute a macro call. The actual arguments following an instance of identifier in the source file are matched to the corresponding formal parameters in the macro definition.

This example illustrates the #define directive:

 

#define WIDTH 80
#define LENGTH ( WIDTH + 10 )

 

The first statement defines the identifier WIDTH as the integer constant 80 and defines LENGTH in terms of WIDTH and the integer constant 10. Each occurrence of LENGTH is replaced by (WIDTH + 10). In turn, each occurrence of WIDTH + 10 is replaced by the expression (80 + 10). The parentheses around WIDTH + 10 are important because they control the interpretation in statements such as the following:

 

iVar = LENGTH * 20;

 

After the pre-processing stage the statement becomes:

 

iVar = ( 80 + 10 ) * 20;

 

which evaluates to 1800. Without parentheses, the result is:

 

iVar = 80 + 10 * 20;

which evaluates to 280.

 

The following example of macros with arguments illustrate the second form of the #define syntax:

 

// Macros to return minimum and maximum values
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

 

Arguments with side effects sometimes cause macros to produce unexpected results. A given formal parameter may appear more than once in token-string (as in the previous example). If that formal parameter is replaced by an expression with side effects, the expression, with its side effects, may be evaluated more than once.

The #undef directive causes an identifier’s pre-processor definition to be forgotten.

If the name of the macro being defined occurs in token-string (even as a result of another macro expansion), it is not expanded. 

#undef

As its name implies, the #undef directive removes (undefines) a name previously created with #define.

#undef identifier

The #undef directive removes the current definition of identifier. Consequently, the pre-processor ignores subsequent occurrences of identifier. To remove a macro definition using #undef, give only the macro identifier; do not give a parameter list.

You can also apply the #undef directive to an identifier that has no previous definition. This ensures that the identifier is undefined. Macro replacement is not performed within #undef statements. 

 

#if, #elif, #else, and #endif

The #if directive, with the #elif, #else, and #endif directives, controls compilation of portions of a source file. If the expression you write (after the #if) has a nonzero value, the line group immediately following the #if directive is retained in the translation unit. The following summarises the syntax of these directives:

#if constant-expression

#ifdefidentifier

#ifndef identifier

if parts…

#elif constant-expression

else-if parts …

#else

else parts :

#endif

Each #if directive in a source file must be matched by a closing #endif directive. Any number of #elif directives can appear between the #if and #endif directives, but at most one #else directive is allowed. The #else directive, if present, must be the last directive before #endif.

The #if, #elif, #else, and #endif directives can nest in the text portions of other #if directives. Each nested #else, #elif, or #endif directive belongs to the closest preceding #if directive.

All conditional compilation directives, such as #if and #ifdef, must be matched with closing #endif directives prior to the end of file; otherwise, an error message is generated. When conditional-compilation directives are contained in include files, they must satisfy the same conditions: There must be no unmatched conditional-compilation directives at the end of the include file. 

#include

The #include directive tells the pre-processor to treat the contents of a specified file as if those contents had appeared in the source program at the point where the directive appears. You can organise constant and macro definitions into include files and then use #include directives to add these definitions to any source file. Include files are also useful for incorporating declarations of external variables and complex data types. You only need to define and name the types once in an include file created for that purpose.

#include"path-spec"

The path-spec is a filename optionally preceded by a directory specification. The filename must name an existing file. The syntax of the path-spec depends on the operating system on which the program is compiled. 

#ifdef #ifndef

The #ifdef directive tells the pre-processor to compile the next section of code only if the symbol is defined, while the #ifndef directive does the opposite. They create a flow control block that will require and #endif directive to complete it.

A typical use might be to compile specific code depending on which version of the GXC compiler you are targeting.

 

#define GX6
#ifdef GX6
//do something
#elif
//do something else
#endif

 

Â