c++ The Preprocessor and Comments

The Preprocessor and Comments 

Overview

C and C++ include several preprocessor directives, which are used to give instructions to the compiler. The preprocessor directives are listed here:
#define
#elif
#else
#endif
#error
#if
#ifdef
#ifndef
#include
#line
#pragma
#undef



#define

#define is used to perform macro substitutions of one piece of text for another. The general form of the directive is
#define macro-name character-sequence
Here, each time macro-name is encountered, the specified character-sequence is substituted. Notice that there is no semicolon in this statement. Further, once the character sequence has started, it is terminated only by the end of the line.
For example, if you want to substitute the value 1 for the word “TRUE” and the value 0 for the word “FALSE,” you would declare these two #define statements:
#define TRUE 1
#define FALSE 0
This will cause the compiler to substitute a 1 or a 0 each time the word TRUE or FALSE is encountered.
The #define directive has another feature: the macro can have arguments. A macro that takes arguments acts much like a function. In fact, this type of macro is often referred to as a function-like macro. Each time the macro is encountered, the arguments associated with it are replaced by the actual arguments found in the program. For example,
#include <iostream>
using namespace std;

#define ABS(a) ((a)<0 ? -(a) : (a))

int main()
{
  cout << "abs of -1 and 1: " << ABS(-1) 
       << ' ' << ABS(1);

  return 0;
}
 
When this program is compiled, a in the macro definition will be substituted with the values –1 and 1.
In C++ and C99, inline functions have reduced the need for function-like macros.


Programming Tip 
You must be sure to completely parenthesize function-like macros that you create. If you don’t, they may not work in all situations. For example, the parentheses surrounding a in the preceding example are necessary to ensure proper substitution in all cases. If the parentheses around a were removed, the expression
ABS (10-20)
Would be converted to
10-20<0 ? –10-20 : 10-20
thus yielding the wrong result. If you have a function-like macro that misbehaves, check your parentheses.

#error

The #error directive forces the compiler to stop compilation when it is encountered. It is used primarily for debugging. Its general form is
#error message
When #error is encountered, the message and the line number are displayed.

#if, #ifdef, #ifndef, #else, #elif, and #endif

The #if, #ifdef, #ifndef, #else, #elif, and #endif preprocessor directives selectively compile various portions of a program. The general idea is that if the expression after an #if, #ifdef, or #ifndef is true, the code that is between one of the preceding and an #endif will be compiled; otherwise, it will be skipped over. The #endif is used to mark the end of an #if block. The #else can be used with any of the above to provide an alternative.
The general form of #if is
#if constant-expression
If the constant expression is true, the code sequence that immediately follows will be compiled.
The general form of #ifdef is
#ifdef macro-name
If the macro-name has been defined in a #define statement, the following code sequence will be compiled.
The general form of #ifndef is
#ifndef macro-name
If macro-name is currently undefined by a #define statement, the code sequence is compiled.
For example, here is the way some of these preprocessor directives work together. The code
#define ted 10

// ...

#ifdef ted
  cout << "Hi Ted\n";
#endif
  cout << "Hi Jon\n";
#if 10<9
  cout << "Hi George\n";
#endif
 
will print “Hi Ted” and “Hi Jon” on the screen, but not “Hi George”.
The #elif directive is used to create an if-else-if statement. Its general form is
#elif constant-expression
You can string together a series of #elifs to handle several alternatives.
You can also use #if or #elif to determine whether a macro name is defined using the defined preprocessing operator. It takes this general form:
#if defined macro-name
statement sequence
#endif
If the macro-name is defined, the statement sequence will be compiled. Otherwise, it will be skipped. For example, the following fragment compiles the conditional code because DEBUG is defined by the program:
#define DEBUG
// ...
int i=100;
// ... 
#if defined DEBUG
cout << "value of i is: " << i << endl;
#endif
 
You can also precede defined with the ! operator to cause conditional compilation when the macro is not defined.

#include


The #include directive causes the compiler to read and compile another source file. It takes these general forms:
#include "filename"
#include <filename>
The source file to be read in must be enclosed between double quotes or angle brackets. For example,
#include "MyFuncs.h"
will instruct the compiler to read and compile the file MyFuncs.h.
If the filename is enclosed by angle brackets, the file is searched for in a manner defined by the creator of the compiler. Often, this means searching some special directory set aside for header files. If the filename is enclosed in quotes, the file is looked for in another implementation-defined manner. For many implementations, this means searching the current working directory. If the file is not found, the search is repeated as if the filename had been enclosed in angle brackets. You must check your compiler’s user manual for details on the differences between angle brackets and double quotes. #include statements can be nested within other included files.
In addition to files, a C/C++ program uses the #include directive to include a header. C and C++ define a set of standard headers that provide the information necessary for the various libraries. A header might refer to a file, but need not. Thus, a header is simply an abstraction that guarantees that the appropriate information is included. As a practical matter, however, C headers are nearly always files and the names of the headers are valid filenames. However, for C++, the situation is different. All C++ header names are standard identifiers that the compiler may map to a filename, or handle in another manner. Since the C++ headers are not filenames, they do not have .h extensions. For example, to include the header information for the C++ I/O system, use
#include <iostream>
 
Here, <iostream> is the standard header for the I/O classes

#line


The #line directive changes the contents of _ _LINE_ _ and _ _FILE_ _, which are predefined identifiers. The basic form of the command is
#line number "filename"
where number is any positive integer and the filename is any valid file identifier. The value of number becomes the number of the current source line and filename becomes the name of the source file. The name of the file is optional. #line is primarily used for debugging purposes and special applications.
The _ _LINE_ _ identifier is an integer, and _ _FILE_ _ is a null-terminated string.
For example, the following sets the current line counter to 10 and the filename to “test”:
#line 10 "test"
 

#pragma

The #pragma directive is an implementation-defined directive that allows various instructions to be given to the compiler. For example, a compiler may have an option to support the tracing of program execution. A trace option would then be specified by a #pragma statement. You must check your compiler’s documentation for the pragmas it supports.

The C99 _Pragma Operator

C99 includes another way to specify a pragma in a program: the _Pragma operator. It has this general form:
_Pragma ( "directive")
Here, directive is the pragma being invoked. The _Pragma operator was added to allow pragmas to participate in macro replacement.

The C99 Built-In Pragmas

C99 defines the following built-in pragmas:
Pragma
Meaning
STDC FP_CONTRACT
  ON/OFF/DEFAULT
When on, floating-point expressions are treated as indivisible units that are handled by hardware-based methods. The default state is implementation defined.
STDC FENV_ACCESS
  ON/OFF/DEFAULT
Tells the compiler that the floating-point environment might be accessed. The default state is implementation defined.
STDC CX_LIMITED_RANGE
  ON/OFF/DEFAULT
When on, tells the compiler that certain formulas involving complex values are safe. The default state is off.

#undef

The #undef directive removes a previously defined macro name. The general form is
#undef macro-name
For example, in the following code,
#define LEN 100
#define WIDTH 100

char array[LEN][WIDTH];

#undef LEN
#undef WIDTH
/* at this point both LEN and WIDTH are
   undefined */
 
both LEN and WIDTH are defined until the #undef statements are encountered

The # and ## Preprocessor Operators

C/C++ provides two preprocessor operators: # and ##. These operators are used in a #define macro.
The # operator causes the argument it precedes to be turned into a quoted string. For example, consider this program:
#include <iostream>
using namespace std;

#define mkstr(s)  # s

int main()
{
  cout << mkstr(I like C++);

  return 0;
}
 
The preprocessor turns the line
cout << mkstr(I like C++);
 
into
cout << "I like C++";
 
The ## operator is used to concatenate two tokens. For example, in the following program,
#include <iostream>
using namespace std;

#define concat(a, b)  a ## b

int main()
{
  int xy = 10;

  cout << concat(x, y);

  return 0;
}
 
the preprocessor transforms
 
cout << concat(x, y);
 
into
cout << xy;
 
If these operators seem strange to you, keep in mind that they are not needed or used in most programs. They exist primarily to allow some special cases to be handled by the preprocessor.

Predefined Macro Names

C/C++ specifies several built-in predefined macro names. They are
_ _LINE_ _
_ _FILE_ _
_ _DATE_ _
_ _STDC_ _
_ _TIME_ _
_ _cplusplus
The _ _LINE_ _ and _ _FILE_ _ macros are described in the #line discussion. The others will be examined here.
The _ _DATE_ _ macro is a string, in the form month/day/year, that is the date of the translation of the source file into object code.
The time of the translation of the source code into object code is contained as a string in _ _TIME_ _. The form of the string is hour:minute:second.
The macro _ _cplusplus is defined when compiling a C++ program. This macro will not be defined by a C compiler. The macro _ _STDC_ _ is defined by a C program and may be defined by a C++ compiler. In both cases, check your compiler’s documentation for details.
Most C/C++ compilers define several other built-in macros that relate to the specific environment and implementation.

Additional Built-In Macros Defined by C99

C99 adds the following macros to those just described. They are not supported by C++.
_ _STDC_HOSTED_ _
1 if an operating system is present
_ _STDC_VERSION_ _
199901L or greater; represents version of C
_ _STDC_IEC_559_ _
1 if IEC 60559 floating-point arithmetic is supported
_ _STDC_IEC_599_COMPLEX_ _
1 if IEC 60559 complex arithmetic is supported
_ _STDC_ISO_10646_ _
A value of the form yyyymmL that states the year and month of the ISO/IEC 10646 specification supported by the compiler

Comments

C++ and C99 define two styles of comments. The first is a multiline comment. It begins with a /* and is terminated with a */. Anything between the comment symbols is ignored by the compiler. A multiline comment can extend over several lines.
The second type of comment is the single-line comment. It begins with a // and ends at the end of the line.
Multiline comments are the only type of comment supported by C89. However, many C89 compilers will accept single-line comments even though they are nonstandard.

C99 Variable Argument Lists

C99 adds to the preprocessor the ability to create macros that take a variable number of arguments. This is indicated by an ellipsis (. . .) in the definition of the macro. The built-in preprocessing identifier _ _VA_ARGS_ _ determines where the arguments will be substituted. For example, given this definition,
#define MyMax(...) max(_ _VA_ARGS_ _)
this statement
MyMax(a, b);
 
is transformed into
max(a, b);
 
There can be other arguments prior to the variable ones. For example, given
#define compare(compfunc, ...) compfunc(_ _VA_ARGS_ _)
 
this statement
compare(strcmp, "one", "two");
 
is transformed into
strcmp("one", "two");
 
As the example shows, _ _VA_ARGS_ _ is replaced by all of the remaining arguments.






Followers