Preprocessor Directives

   


You can view the C# compiler as containing a separate preprocessor that processes and prepares the source code for the compiler before the actual compilation takes place. Programmers can use preprocessor directives to mark parts of the source code and thereby instruct the preprocessor to treat these parts in a certain way.

Note

graphics/common.gif

Several of C#'s predecessors, including the C language, contain preprocessors that are separate from the compiler. This is not the case for C# where the preprocessor directives are processed as part of the lexical analysis (this explains the highlighted word view in the first line of this section). Nevertheless, the terms are used with C# for historical reasons and because it helps us to better understand what preprocessor directives do. You can ignore this note while you read through the rest of this section.


Exclude and Include Code with #define, #if, and #endif

The most common use of preprocessor directives is to exclude parts of the source code from being compiled. For example, you might, during the testing of your source code, include several WriteLine statements to print out variable values that otherwise could not be verified. You obviously don't want the end user to see these test printouts, so you must prevent them from being compiled into the finished program. Furthermore, you might want to switch back and forth between the program's testing and end user version. There are several ways to achieve this.

You can manually insert and manually delete WriteLine statements every time you need to switch from one version to another. However, this can be a cumbersome process.

You can insert the WriteLine statements and then hide them from the compiler by commenting them out. This is easier than the previous method, but you must still move around in the code and manually insert and delete the required // and /* */ symbols.

These two procedures might work well for small programs, but larger programs sometimes have many test WriteLines and related test statements scattered around in the source code, making it a tedious, time-consuming task to test your programs.

The preprocessor directives allow you to include and exclude test code (that is sprinkled around in your program) from compilation by changing just one line of code. Before we look at Listing 21.1, which demonstrates this ability, we need to briefly introduce the preprocessor directives to understand how this can be achieved.

All preprocessor directives, of which #define and #if are typical examples, begin with the pound sign (#) symbol. Fundamentally, the directives presented in this and the next two sections allow you to put identifiers into and out of existence (by defining and undefining them) at one point in the code and then test for their existence in other parts of the code.

The #define preprocessor directive is used to define and thereby put an identifier into existence. For example, the following line

 #define TEST 

defines the identifier TEST. You can choose any name for an identifier, but conventionally it is written with uppercase letters. #define must, as the only directive, be written before any other source code.

The preprocessor directives #if and #endif work as a pair and enclose one or more lines of code as shown in the following:

 #if TEST     <Enclosed source code is only compiled if TEST is defined> #endif 

An identifier (TEST in this case) must follow the #if directive. The enclosed code is only compiled if this identifier is defined. The #if-#endif pair can be positioned in an unlimited number of places in your code.

Listing 21.1 demonstrates the #define, #if, and #endif directives.

Listing 21.1 CarTesting.cs
01: #define TESTING 02: 03: using System; 04: 05: class Car 06: { 07:     int odometer = 0; 08:     int moveCounter = 0; 09: 10:     public double MoveForward(int distance) 11:     { 12:         moveCounter++; 13:         odometer += distance; 14:         Console.WriteLine("Car is moving forward by { 0}  kilometers", distance); 15:       #if TESTING 16:         Console.WriteLine("Testing. Odometer: { 0} ", odometer); 17:       #endif 18:         return (odometer / moveCounter); 19:     } 20: } 21: 22: class Controller 23: { 24:     public static void Main() 25:     { 26:         double averageMoveDistance; 27:         Car myCar = new Car(); 28: 29:         averageMoveDistance = myCar.MoveForward(20); 30:         averageMoveDistance = myCar.MoveForward(10); 31:       #if TESTING 32:         Console.WriteLine("Testing. averageMoveDistance: { 0} ", 33:             averageMoveDistance); 34:       #endif 35:     } 36: } 

Sample output 1: If line 1 contains the code #define TESTING

 Car is moving forward by 20 kilometers Testing. Odometer: 20 Car is moving forward by 10 kilometers Testing. Odometer: 30 Testing. averageMoveDistance: 15 

Sample output 2: If line 1 is deleted; meaning TESTING is undefined and you recompile the code

 Car is moving forward by 20 kilometers Car is moving forward by 10 kilometers 

Suppose the WriteLine statements in line 16 and lines 32 and 33 exist purely for testing purposes. We want, with little effort, the ability to switch between a test version and an end user version, so we have enclosed these WriteLine statements between #if and #endif directives. Line 1 defines the TESTING identifier with the #define directive. If you include line 1 in your code, both of the test WriteLine statements will be included in the compilation, as you can see in sample output 1. However, if you simply erase line 1, none of the WriteLine statements are included, as verified by sample output 2.

#undef: Undefining Identifiers

Instead of deleting the first line of Listing 21.1 to undefine TESTING, you can explicitly undefine TESTING by using the #undef directive, as shown in the following line:

 #undef TESTING 

#undef, like its #define sibling, must appear before any statements in your code that is not also preprocessor directives.

#elif and #else

With #elif and #else, you can implement the same type of logic for the preprocessor as the corresponding familiar else-if constructs we have used in previous chapters for constructing branching statements in C#. Their impact is illustrated in the following lines:

 #define DEBUGGING #define TESTING     ... #if DEBUGGING     <This code is only compiled if DEBUGGING is defined> #elif TESTING     <This code is only compiled if TESTING is defined and DEBUGGING is un-defined> #else     <This code is only compiled if both DEBUGGING and TESTING are un-defined> #endif     ... 

#elif (corresponding to else if) and #else are used together with the #if and #endif directives. If DEBUGGING (written after the #if directive in the example) is defined, only the code between #if and #elif is compiled. The #elif directive only gets a chance to verify the existence of TESTING if DEBUGGING is not defined. Thus, only if DEBUGGING is undefined and TESTING is defined, will the code between #elif and #else be compiled. If both TESTING and DEBUGGING are un-defined, only the code between #else end #endif is compiled.

#error and #warning

#error and #warning don't exclude or include code from being compiled. Instead, #error causes the compiler to report an error and #warning triggers a warning. The text inserted after any of these two directives decides the text written as part of the error or warning message. For example, the warning inserted in the following lines causes the text "Remember to remove these lines of code" to be written on the console:

 #define DEBUG     ... #if DEBUG     ...     #warning "Remember to remove these lines of code" #endif 

#region and #endregion

#region and #endregion let's you mark a certain portion of the code and provide a name for it. These directives are only relevant when you use editors, such as the C# editor in Visual Studio. NET, that can recognize these directives.

#line

You have probably noticed that whenever the compiler returns an error or warning message, it always passes the source code filename and line/character numbers along. You can alter this part of the message with the #line directive. For example, you can tell the compiler that a particular line is line number 59 and that the name of the file is SpaceInvaders.cs by using the following line:

 #line 59 "SpaceInvaders.cs" 

You will hardly ever need to use the #line directive.


   


C# Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 286
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net