Sunday, October 24, 2010

Conditional Compilation

The third useful facility provided by the preprocessor is conditional compilation; i.e. the selection of lines of source code to be compiled and those to be ignored. While conditional compilation can be used for many purposes, we will illustrate its use with debug statements. In our previous programming examples, we have discussed the usefulness of printf() statements inserted in the code for the purpose of displaying debug information during program testing. Once the program is debugged and accepted as ``working'', it is desirable to remove these debug statements to use the program. Of course, if later an undetected bug appears during program use, we would like to put some or all debug statements back in the code to pinpoint and fix the bug. One approach to this is to simply ``comment out'' the debug statements; i.e. surround them with comment markers, so that if they are needed again, they can be ``uncommented''. This is a vast improvement over removing them and later having to type them back. However, this approach does require going through the entire source file(s) to find all of the debug statements and comment or uncomment them.

The C preprocessor provides a better alternative, namely conditional compilation. Lines of source code that may be sometimes desired in the program and other times not, are surrounded by #ifdef, #endif directive pairs as follows:

#ifdef DEBUG
printf("debug:x = %d, y = %f\n", x, y);
...
#endif

The #ifdef directive specifies that if DEBUG exists as a defined macro, i.e. is defined by means of a #define directive, then the statements between the #ifdef directive and the #endif directive are retained in the source file passed to the compiler. If DEBUG does not exist as a macro, then these statements are not passed on to the compiler.

Thus to ``turn on'' debugging statements, we simply include a definition:

#define DEBUG 1

in the source file; and to ``turn off'' debug we remove (or comment) the definition. In fact, the replacement string of the macro, DEBUG is not important; all that matters is the fact that its definition exists. Therefore,

#define DEBUG

is a sufficient definition for conditional compilation purposes. During the debug phase, we define DEBUG at the head of a source file, and compile the program. All statements appearing anywhere between #ifdef and matching #endif directives will be compiled as part of the program. When the program has been debugged, we take out the DEBUG definition, and recompile the program. The program will be compiled excluding the debug statements. The advantage is that debug statements do not have to be physically tracked down and removed. Also, if a program needs modification, the debug statements are in place and can simply be reactivated.

In general, conditional compilation directives begin with an if-part and end with an endif-part. Optionally, an else-part or an elseif-part may be present before the endif-part. The keywords for the different parts are:

* <711>> if-part: if, ifdef, ifndef
* else-part: else
* elseif-part: elif
* endif-part: endif

The syntax is:

* #
*
* [ #
* ]
* [ #
* ]
* #

If the if-part is True, then all the statements until the next , or are compiled; otherwise, if the is present, the statements between the and the are compiled.

We have already discussed the keyword ifdef. The keyword ifndef means ``if not defined''. If the identifier following it is NOT defined, then the statements until the next , or are compiled.

The keyword if must be followed by a constant expression, i.e. an expression made up of constants and operators. If the constant expression is True, the statements until the next else-part, elseif-part or endif-part are compiled. In fact, the keyword ifdef is just a special case of the if form. The directive:

#ifdef ident

is equivalent to:

#if defined ident

We can also use #if to test for the presence of a device, for example, so that if it is present, we can include an appropriate header file.

#if DEVICE == MOUSE
#include mouse.h
#endif

Here, both DEVICE and MOUSE are assumed to be constant identifiers.

The #elif provides a multiway branching in conditional compilation analogous to else ... if in C. Suppose, we wish to write a program that must work with any one of a variety of printers. We need to include in the program a header file to support the use of a specific printer. Let us assume that the specific printer used in an installation is defined by a macro DEVICE. We can then write conditional compilation directives to include the appropriate header file.

#if DEVICE == IBM
#include ibmdrv.h
#elif DEVICE == HP
#include hpdrv.h
#else
#include gendrv.h
#endif

Only constant expressions are allowed in conditional compilation directives. Therefore, in the above code, DEVICE, IBM, and HP must be be defined constants.

Exaples:
EX1:
#define VAL 10
int main () {

#if ( (VAL%2) == 0)
printf("Okay , I am executing the printf statement \n ");
#endif
}

No comments:

Post a Comment