C++ Tutorial

Pre-Processor Directives

1. Pre-Processor Directives
    a. if/elif/else/endif
    b. ifndef/ifdef
    c. define (Macros)
    d. undef
    e. include

    Pre-Processor directives are special symbols which are evaluated before compilation.  They can be used to control how something is compiled with the #pragma directives, or the #if constructions, and the #include directive can be used to make the compiler compile files other than the ones that the programmer explicitly compiles on the command line, effectively making several files one file.  These directives can be used in all manner of ways, and I will be going over each of them briefly, except for the pragma directive which is dependent upon the compiler that you use for what pragmas will be supported. None of the pre-processor directives need a ';' to end them.  They end with the line they are on, though a '\' can be used to extend them beyond one line.  Though if that is used, you must remember not to have anything else on that line.

#if/elif/else/endif
    Next (ifndef/ifdef)  Prev (Pre-Processor Directives TOC)   Top (Pre-Processor Directives)

    This directive has the same form as most programming languages if/else statements.  Though, this is a bit more archaic than C++ in that it uses an If statement, optionally an elif (else-if) statement, optionally an else statement, and finally it requires that you close the statement with an endif statement.  This is probably the most useful directive for conditional compilation that there is.  This directive has the effect of either making sure certain areas of the program are not compiled in certain situations, or making sure that certain areas of the program are only compiled in certain situations.
    The important thing to remember with regard to this directive is that only things which the pre-processor knows about can be used for the conditions.  So, you may have constant expressions, or macros as the conditions.  Basically this evaluates to the idea that you can only have things that resolve at compile time affect the compilation.
    A full description of each part of the #if directive is below.

#if (condition) -- if the condition is true, then allow the code in the <statements> section to be compiled, most logical operators are allowed here, if they are given incompatible arguments, then the pre-processor will either evaluate the condition as false, or will error.  A special keyword that has its own macro below is the defined() macro, which determines if something has been #define'd or not.

#elif (condition) -- if none of the #if or #elif conditions above this were true, then evaluate this condition, if it is true, allow the code in the <statements> section to be compiled.

#else -- if none of the #if or #elif conditions above this were true, then allow the code in the <statements> section to be compiled.

#endif -- end this if directive.

<statements> consist of any valid C++ code.

So, given that, the structure of these are:

#if (condition)
  <statements>
#elif (condition)     // Note:  This is an optional case...
  <statements>
#else                 // Note:  This is an optional case...
  <statements>
#endif

   The fact that this directive has optional companion directives is that you may see it in the form of:

#if (condition)
  <statements>
#elif (condition)
  <statements>
#endif

  or:

#if (condition)
  <statements>
#else
  <statements>
#endif

  or:

#if (condition)
  <statements>
#endif

ifndef/ifdef
Next ( define )    Prev (if/elif/else/endif)   Top (Pre-Processing Directives)

    The shortest story on these is that they are simply a macro for another directive.  #ifndef should be read, if not defined, and can also be written as:

#if !defined(symbol)

Similarly, #ifdef can also be written as:

#if defined(symbol)

These two constructions both follow the rules of an #if construction and may be used the same way, with the exception that they do not allow an elif clause.  They do in fact allow #else directives, but not #elif.  These must also be ended with an #endif directive.
    These two directives allow conditional compilation based on whether or not a symbol is defined. The catch here is that the symbol to be checked must be something that is #define'd.  This is actually because of the fact that at the time that the pre-processor is active, the program is not compiled and therefore program variables cannot be accessed, even as to whether they are defined or not.  Though anything that has been #define'd can be checked for definition with these directives.  To check a macro (a psuedo-function which has been defined using the #define directive) one need only supply the name of the macro (no parens are needed).  Further, in the #if directive the defined() macro is in fact a macro, and therefore requires parens around the symbol name to be checked, while the ifndef/ifdef construction does not.  So...the example is:

#define free(x) _free(x)

#if defined(free)
  <statements>
#endif

#ifndef free
  <statements>
#endif

Both of the above would be true expressions and would therefore allow the statements in their bodies to execute.
 

define

    This macro defines a given symbol to be something else.  The exact behavior of this is that where ever the symbol that is defined appears within the program, it will be EXACTLY replaced with the contents of the macro.  There are several special characters including '#' '@' and others that are not allowed in these directives, though they have a purpose for the directive, though I shall not go over those.  The '\' character is also special for the #define directive, though it is used to escape any character, and one may continue a #define directives substitution string beyond the end of the line using the '\' character, because it will escape the '\n' and will not end the directive (any pre-processor directive ends with a '\n').  Unfortunately though, for this behavior the '\' must be the last character on the line, so that it really does escape the newline ('\n').  The syntax for this directive is that the directive must be the first thing on the line (just as all pre-processor directives), and then the symbol name must be next.  The macro may take arguments, and in that case, the symbol should have an open paren, a naming for each argument, separated by commas, and then the closing paren.  After that, the next characters are taken as the body of the macro and will be substituted into wherever the symbol is found.  The body of the macro will be evaluated by the pre-processor and any named arguments to the macro will be substituted for their values, as the code specifies.  An example is below:

#define free(x) _free(x)
  -- what this says is that everywhere that the pre-processor finds the word free, it should replace that with _free, and further, free must have one argument to it, which is placed as the argument to _free.  So, the following piece of code:
 (intList is assumed to have a member named next which contains the next element in the list, and 'list' is assumed to be of the type intList).
for (intList * p = list; p; p = p->next) {
   free(p);
}
Given the #define above, would look like this after the substitution:
for (intList *p = list; p; p = p->next) {
  _free(p);
}
Further,
#define free(x) { char * p = "The blue moon is "; \
printf("%s %s", p, x);  }
for (intList *p = list; p; p = p->next) {
  free(p);
}
Would be expanded as:
for (intList *p = list; p; p = p->next) {
  { char * p = "The blue moon is ";
     printf("%s %s", p, p); }
}
Which, though syntactically correct, would undermine the program, as it would no longer perform the function of the free function.  Though notice that this would also not act on the p variable of the for loop, but would instead act on the p variable of the block within the for loop (a char *, not an intList*).  Further remember the fact that as the macros are expanded exactly as they are defined, they may throw error messages into strange places (as they can add lines to a program, and an error can be within the macro, and yet the error will show up on the line that the macro is expanded on).  Macros are a powerful tool, but they can be abused, as they may increase program size, remember they are substituted exactly, and if the macro is only five characters, but one hundred characters are substituted for it, and you use it fifty times, then you program has grown by 450 characters (5*50 = 250, 100*50 = 5000)...  Most of the time, functions are better to use, the only situation where it is not the case is where something cannot be done using a function that can be done using a macro, such as the following:

class ExceptionThing : public Exception {
   public:
      ExceptionThing();
      char * toString();
      int myCode();
   private:
      int codeNum;
      ...

};
 however, you have thirty of these classes, which though they all define the same functions (the front part of the class), they require different member variables, some of them have a state, some of them have a state stack (for history), and basically each one is different, but they all have the same functions....  Then, as you can see, there is no way to do a function that will produce that, however, you can use a macro, consider the following:

#define makeClass(classname) \
class classname : public Exception { \
  public:  \
     classname(); \
     char * toString(); \
     int myCode();

Now the above class definition can be simplified as:

makeClass(ExceptionThing)
  private:
    int codeNum;
    ...
};

The first thing to note is that this is a hack and though it may be more trouble to do the entire definition, it is probably a better idea just because of readability.  However, there are situations in which this can cut down development time, and leave the programmer to do more interesting stuff with his/her time.  Further, notice about the macro itself, there are '\'s on each line, because otherwise the macro would end with the first '\n' encountered (though recall, the '\' character escapes the '\n' and therefore the macro does not end with it), Further, notice that we do not have a ';' after the makeClass(...) line.  This prevents an error, as in a class definition, a ';' character sometimes behaves badly, and considering that we are inside a class definition (remember, the macro is substituted EXACTLY into the place where the macro is called), we don't want these.  Further notice that we no longer need the braces, as the braces are now partially in the macro.
    Now on to more interesting tricks...  We can also begin to make C++ just like pascal...  Observe:

#define begin {
#define end }
#define procedure void

We could continue, and though there is a stopping point, we already have the beginnings of a pascal system...
procedure main()
begin
   if (x > 5)
    begin
       y = 1;
    end
end

Isn't that icky....

undef

include
    The short story is that whatever is in the file #include'd is substituted exactly where the include directive is placed.  Further, the syntax of the #include command is the directive, and then the file in double quotes ('"', '"'), if it is in the same directory, and in angle brackets ('<', '>') if it is in the include path.  Typically, this is not really paid attention to by the compiler, though it is convention that all include files that you write should be included using the double quotes, and any include files that you get from somewhere (usually the system) should be included using angle brackets.  Further, the naming convention as of recently has been to include C++ include files without the .h extension and C files with the .h extension.

Example:
C++ program:

#include <iostream>
#include "myClass"

int main() {
  return 0;
}

C program:
#include <stdlib.h>
#include "myClass.h"

int main() {
  return 0;
}