conditional compilation

A means of controlling the generation of a computer
program based upon conditions specified at the time it is compiled (translated
from its source code into an intermediate form, or into a machine language
that the computer can directly process).

Preventing
the same code from being compiled more than once due to the code's source
file being accessed more than once by the compiler (the famous #include guard), and

Turning various facilities in the source code (such as debug statements)
on or off as the programmer wishes.

The most widely recognized form of conditional compilation arises from
the introduction of particular compiler directives into a program's source
code. This, of course, means that the compiler must provide facilities
to support it.

The best-known (and most widely copied) of these
facilities are the family of #if compiler directives that
are used in the C and C++ programming languages.

Conditional compilation in C/C++

Because the traditional C compiler, cc, implements conditional
compilation facilities in its preprocessing phase (and because all C
and C++ compilers must behave as if the preprocessing phase takes place
separately), these facilities take the form of preprocessor directives:
lines of code that begin with a # character and which are followed
by a word naming the directive.

Blocks of C and C++ code that might be compiled, and also might not,
are placed in between a #if directive and a #endif
directive:

If, at the time the program is compiled, the compile-time-expression
evaluates
to a true or nonzero value, the compiler evaluates all of the code between
the #if and the #endif. If not, the
lines between the #if and the #endif are
skipped.

Variations on the basic theme.

The directive #else is used when code also exists for those
occations when the compile-time-expression evaluates to
false.
so instead of

#if compile-time-expression//// some code//#endif#if negative-form-of-above-compile-time-expression//// some more code//#endif

we have

#if compile-time-expression//// some code//#else//// some more code//#endif

which is easier to understand, and less error-prone.

The directive #elif combines a #elsedirective
and the#ifdirective of a new condition.
It is not very useful, for reasons we will describe below.

#if blocks may be nested: That is, the lines between
a #if ... #endif pair may contain a block
of lines between another #if ... #endif
pair. Thus:

#if foo//// some code//# if bar // // some more code. // This will be compile only if both
foo and bar are true. //# endif // to balance #if bar//// yet more code in the #if foo block//#endif // to balance #if foo

The basic #if directive has two very important variations:

#ifdefmacro evaluates to a true value only if
the specified preprocessor macro is defined when the compiler
reaches the directive.

Similarly, #ifndefmacro evaluates to a true
value only if the specified macro is not defined.

Due to the nature of the primary uses of conditional compilation described
above, #ifdef and #ifndef are traditionally
used much more frequently than #if (the reason
#elif isn't very useful).

This has recently changed in the C++ world. Due to the complexity
of the compile-time expressions that programmers found they had to use
in C++, the construction defined(macro) was
added to the preprocessor language. This allows checking the definition
/ non-definition of more than one macro in the same expression.

Isolating platform dependencies

Platform dependencies are isolated by using #ifdef with
a macro named for the platform. These are either pre-defined in the
compiler, or set in the makefile used to control compiling the entire
project. Thus:

There are all sorts of constructs you can make, for the compiler version,
processor version, operating system, and things like that.

Include guards

Include guards arise from the traditional use of include files to specify
a C module's interface to the outside world.
Let's say you have a module foo whose interface you
have packaged neatly into the file foo.h. Let's
say you also have two other modules, foo_user1 and foo_user2
which both depend on the module foo. You've packaged their interfaces
into foo_user1.h and foo_user1.h:

// -- foo_user1.h --#include "foo.h"...... foo_user1 interface

// -- foo_usr2.h --#include "foo.h"...... foo_user2 interface

Finally, let's say you have a module bar which uses
both foo_user1 and foo_user2, but doesn't
need to know about foo's interface. It has
to include foo_user1.h and foo_user2.h:

// -- bar.c --#include "foo_user1.h"#include "foo_user2.h"

Since foo.h will be included more than once, the above
construction will be rejected by the compiler somewhere in the bowels of
foo.h the second time around, the moment something is defined
the second time. A solution might be to remove the references
to foo.h from foo_user1.h and foo_user2.h

The solution is to make up a macro name for each file that might be
#include'ed, and then to wrap the contents of the file
in a #if construction like this:

#ifndef THIS_HERE_INCLUDE_FILE_H#define THIS_HERE_INCLUDE_FILE_H//// All the rest of the code in the file//#endif

The trick is to #define the macro so that the
#ifndef directive can be true only once in a particular translation
unit.

In this way, the contents of the include file will be processed the
first time it is accessed because of a #include directive
in another file. If a later #include directive mentions
the same file, however, the contents won't be processed again.

It has become fashionable in some circles to place the #include guard
around the #include directive rather than inside the included
file. Don't do this. Although this technique reduces dependencies
in automatic makefile generators, it obfuscates your code.

Another phenomenon you will run into frequently is the construction
#pragma once, which is a facility provided by some compiler manufacturers
(most notably Microsoft) to do precisely the same thing. But it's
a platform dependency, which you have to isolate with... conditional compilation.
Plus, you have to provide alternative code for platforms that don't support
it, which means... an include guard.

Switching facilities on and off

During the programming cycle, you may have differing purposes for invoking
a compiler: At one point, you may wish to create a program you can
debug; at other times, you may be finished debugging (ha!) and may wish
to create a sleek, optimized program. To that end, you might create
the following construction: