Preprocessor Directives

Directives are instructions we can provide for the C++ compiler for a variety of purposes. These are not actual C++ code. They can be recognized easily by the fact that they all start with '#'.

These were inherited by C++ from the C language.


Some commonly used directives

#include <filename>

Include the text of the indicated file at this point; search standard libraries for for the file.

#include "filename"

Include the text of the indicated file at this point; search the current directory as well as standard libraries for the file.

#define NAME value

Associate NAME with the (constant) value (as a string). Before compiling, a search-and-replace operation will replace each occurrence of NAME by the value.

If no value is provided, NAME is a defined symbol with an empty string as its value.

#undef NAME

Removes NAME from the list of defined symbols.

#if expression

If the expression (which must evaluate to a constant value) is not 0, start including lines until #else or #endif is found. If it is 0, skip text until #else or #endif is found.

#ifdef NAME

If NAME is a defined symbol (at this point), start including text until #else or #endif is found.

#ifndef NAME

If NAME is not a defined symbol (at this point), start including text until #else or #endif is found.

#else

See #if, #ifdef and #ifndef above; we have an if-else-endif structure. The body of either part of the structure might be empty.

#endif

Delimiter for the scope of a #if, #ifdef, #ifndef or #else.

defined(NAME)

Used in an #if, this returns 0 if NAME is not a defined symbol and 1 if it is a defined symbol. Thus "#ifdef NAME" is equivalent to "#if defined(NAME)".

There are a few other directives as well, such as #pragma and #elif, which are a bit more obscure.


Expressions

It is possible to use arithmetic expressions in a #define, such as:

         #define RECORDSIZE 120
         #define BLOCKSIZE (32*RECORDSIZE)

It is possible to use logical expressions in a #if, such as: #if defined(NAME1) && !defined(NAME2)

The expressions will be evaluated when the expanded code is evalauted. Thus, in the above example, BLOCKSIZE is replaced with "(32*120)" rather than with "(3840)".


Macros

It is also possible to use #define to create macros, which can have parameters and which will generate actual C++ code.

Writing macros is somewhat of an art, as it can be difficult to anticipate how the macro may be used.

Here is a (traditional) example:

         #define SQUARE(x) x * x

As long as the value supplied for x is just one term, this may work well. However, if someone uses it like this:

         SQUARE(a + b)

the result will be

         a + b * a + b

rather than

(a + b) * (a + b)

One way to fix this is:

         #define SQUARE(x) (x) * (x)

A similar problem occurs with SQUARE(N++), and the above fix will probably not help.

There are some predefined macros required by the ANSI standard, such as:

_DATE_

The value returned is the compilation date as a string of the form "Mmm dd yyyy", where Mmm is a standard 3-letter abbreviation for a month name.

_TIME_

The value returned is the time of compilation as a string of the form "hh:mm:ss" (using a 24-hour clock).

_FILE_

The value returned is as string containing the name of the source code file.

Here is an example of using these:

         cout << "File " << _FILE_ << " was compiled at "
              << _TIME_ << " on " << _DATE_;

There are a few others, and specific compilers frequently provide more predefined macros as well.