Monday, March 26, 2012

Conditional Compilation in C

The preprocessor allows you conditionally to compile sections o through the use of #ifdef , #else , and #endif directives.
For example:
#ifdef DOS
#define NAME "C:\ETC\DATA"
#else /* DOS */
#define NAME "/etc/data"
#endif /* DOS */
Actually, the #else and #endif directives take no arguments. The following them is entirely a comment, but a necessary one. It serves to match #else and #endif directive with the initial #ifdef .
Note: Some strict ANSI compilers don't allow symbols after #else or #endif directives. In these cases, the comment DOS must be formally written as /* DOS */ .
Comment #else and #endif directives with the symbol used in the initial #ifdef or #endif directive.
Use conditional compilation sparingly. It easily confuse the code.
#ifdef SPECIAL
float sum(float a[])
#else /* SPECIAL */
int sum(int bits)
#endif SPECIAL
{
#ifdef SPECIAL
    float total;    /* Total so far */
#else /* SPECIAL */
    int total;      /* Total number of bits */
#endif /* SPECIAL */
    int i;          /* General index */
#ifdef SPECIAL
    total = 0.0;
#else /* SPECIAL */
     total = 0;
#endif /* SPECIAL */
#ifdef SPECIAL
    for (i = 0; a[i] ! = 0.0; ++i)
        total += ( ( b its & i ) ! = 0);
#else /* SPECIAL */
    for (i = Ox8O; i ! = 0: i >> 1)
        total += a[i];
#endif /* SPECIAL */
    return (total);
}
/*
 * A comment explaining that this
 * is bad code would be redundant
 */
The structure of this function is nearly impossible to pick out. Actually, it consists of two completely different functions merged together. There are a few lines of common code, but not many.
float sum(float a[])
{
    float total;    /* Total so far */
    int i;          /* General index */
    total = 0.0;
    for (i = 0; a[i ] ! = 0.0; ++i)
        total += ( ( b its & i ) ! = 0);
    return (total);
}
int sum(int bits)
{
    int total;      /* Total number of bits */
    int i;          /* General index */
     total = 0;
    for (i = Ox8O; i ! = 0: i >> 1)
        total += a[i];
    return (total);
}
Avoid complex conditional sections. C is difficult enough to understand without confusing the issue. Usually it is better to write two entirely separate but clearer functions.
Use conditional compilation sparingly. Don't let the conditionals obscure the code.

Where to define the control symbols

The control symbols for conditional compilation can be defined through #define statements in the code or the -D compiler option.
If the compiler option is used, the programmer must know how the program was compiled in order to understand its function. If the control symbol is defined in the code, the programmer needs no outside help. Therefore, avoid the compiler option as much as possible.
Define (or undefine) conditional compilation control symbols in the code rather than using the -D option to the compiler.
Put the #define statements for control symbols at the very front of the file. After all, they control how the rest of the program is produced.
Use the #undef statement for symbols that are not defined. This serves several functions. It tells the program that this symbol is used for conditional compilation. Also, #undef contains a comment that describes the symbol Finally, to put the symbol in, all the programmer needs to do is change the #undef to #define.
#define CHECK /* Internal data checking enabled */
#undef DEBUG /* Not the debug version of the program */
Put #define and #undef statements for compilation control symbols at the beginning of the program.

Commenting out code

Sometimes a programmer wants to get rid of a section of code. This may be because of an unimplemented feature, or some other reason. One trick is to comment it out, but this can lead to problems:
/*-------Begin commented out section ------
open_database();
update_symbol_table(); /* Add our new symbols */
close_database();
/*-------End commented out section------*/
Unless your compiler has been extended for nested comments, this code will not compile. The commented-out section ends at the line /* Add our new symbols */, not at the bottom of the example.
Conditional compilation can accomplish the same thing, with much less hassle.
#ifdef UNDEF
open_database();
update_symbol_table(); /* Add our new symbols */
close_database();
#endif /* UNDEF */
Note: This will not work if the programmer defines the symbol (However, any programmer who defines this symbol should be shot.)
Do not comment out code. Use conditional compilation ( #ifdef UNDEF ) to get rid of unwanted code.
Sometimes the programmer wants to take out a section of code for a minutes for debugging. This can be done in a similar way:
#ifdef QQQ
    erase_backups();
#endif QQQ
The symbol QQQ was chosen because it is probably not defined and is easy spot with an editor. This allows all QQQ lines to be quickly removed when the is found.
Use #ifdef QQQ to temporarily eliminate code during debugging.

No comments:

Post a Comment