10.7 Conditional Compilation (Compile Time Decisions)


10.7 Conditional Compilation (Compile Time Decisions)

HLA's compile time language provides an if statement, #if, that lets you make various decisions at compile time. The #if statement has two main purposes: the traditional use of #if is to support conditional compilation (or conditional assembly) allowing you to include or exclude code during a compilation depending on the status of various symbols or constant values in your program. The second use of this statement is to support the standard if statement decision-making process in the HLA compile time language. This section will discuss these two uses for the HLA #if statement.

The simplest form of the HLA compile time #if statement uses the following syntax:

 #if( constant_boolean_expression )      << text >> #endif 

Note that you do not place semicolons after the #endif clause. If you place a semicolon after the #endif, it becomes part of the source code, and this would be identical to inserting that semicolon immediately before the next item in the program.

At compile time, HLA evaluates the expression in the parentheses after the #if. This must be a constant expression, and its type must be boolean. If the expression evaluates true, HLA continues to process the text in the source file as though the #if statement was not present. However, if the expression evaluates false, HLA treats all the text between the #if and the corresponding #endif clause as though it were a comment (i.e., it ignores this text), as shown in Figure 10-2.

start figure

 #if( constand_boolean_expression )           HLA compiles this code if           the expression is true. Else           HLA treats this code like           a comment. #endif 

end figure

Figure 10-2: Operation of HLA Compile Time #IF Statement.

Keep in mind that HLA's constant expressions support a full-expression syntax like you'd find in a high level language like C or Pascal. The #if expression syntax is not limited to the syntax allowed by expressions in the HLA if statement. Therefore, it is perfectly reasonable to write fancy expressions like the following:

 #if( @length( someStrConst ) < 10*i & ( (MaxItems*2 + 2) < 100 | MinItems-5 < 10 ))      << text >> #endif 

Also keep in mind that the items in a compile time expression must all be const or val identifiers or an HLA compile time function call (with appropriate parameters). In particular, remember that HLA evaluates these expressions at compile time so they cannot contain run-time variables.[5] HLA's compile time language uses complete boolean evaluation, so any side effects that occur in the expression may produce undesired results.

The HLA #if statement supports optional #elseif and #else clauses that behave in the intuitive fashion. The complete syntax for the #if statement looks like the following:

 #if( constant_boolean_expression1 )      << text1 >> #elseif( constant_boolean_expression2 )      << text2 >> #else      << text3 >> #endif 

If the first boolean expression evaluates true then HLA processes the text up to the #elseif clause. It then skips all text (i.e., treats it like a comment) until it encounters the #endif clause. HLA continues processing the text after the #endif clause in the normal fashion.

If the first boolean expression above evaluates false, then HLA skips all the text until it encounters a #elseif, #else, or #endif clause. If it encounters a #elseif clause (as above), then HLA evaluates the boolean expression associated with that clause. If it evaluates true, HLA processes the text between the #elseif and the #else clauses (or to the #endif clause if the #else clause is not present). If, during the processing of this text, HLA encounters another #elseif or, as above, a #else clause, then HLA ignores all further text until it finds the corresponding #endif.

If both the first and second boolean expressions in the previous example evaluate false, HLA skips their associated text and begins processing the text in the #else clause. As you can see, the #if statement behaves in a relatively intuitive fashion once you understand how HLA "executes" the body of these statements; the #if statement processes the text or treats it as a comment, depending on the state of the boolean expression. Of course, you can create a nearly infinite variety of different #if statement sequences by including zero or more #elseif clauses and optionally supplying the #else clause. Because the construction is identical to the HLA if..then..elseif..else..endif statement, there is no need to elaborate further here.

A very traditional use of conditional compilation is to develop software that you can easily configure for several different environments. For example, the fcomip instruction makes floating point comparisons very easy, but this instruction is available only on Pentium Pro and later processors. If you want to use this instruction on the processors that support it and fall back to the standard floating point comparison on the older processors, you would normally have to write two versions of the program — one with the fcomip instruction and one with the traditional floating point comparison sequence. Unfortunately, maintaining two different source files (one for newer processors and one for older processors) is very difficult. Most engineers prefer to use conditional compilation to embed the separate sequences in the same source file. The following example demonstrates how to do this:

 const      PentProOrLater: boolean := false; // Set true to use FCOMIxx instrs.           .           .           .      #if( PentProOrLater )           fcomip();      // Compare st1 to st0 and set flags.      #else           fcomp();       // Compare st1 to st0.           fstsw( ax );   // Move the FPU condition code bits           sahf();        // into the FLAGS register.      #endif 

As currently written, this code fragment will compile the three-instruction sequence in the #else clause and ignore the code between the #if and #else clauses (because the constant PentProOrLater is false). By changing the value of PentProOrLater to true, you can tell HLA to compile the single fcomip instruction rather than the three-instruction sequence. Of course, you can use the PentProOrLater constant in other #if statements throughout your program to control how HLA compiles your code.

Note that conditional compilation does not let you create a single executable that runs efficiently on all processors. When using this technique you will still have to create two executable programs (one for Pentium Pro and later processors, one for the earlier processors) by compiling your source file twice: During the first compilation you must set the PentProOrLater constant to false; during the second compilation you must set this constant to true. Although you must create two separate executables, you need only maintain a single source file.

If you are familiar with conditional compilation in other languages, such as the C/C++ language, you may be wondering if HLA supports a statement like C's "#ifdef" statement. The answer is no, it does not. However, you can use the HLA compile time function @defined to easily test to see if a symbol has been defined earlier in the source file. Consider the following modification to the preceding code that uses this technique:

 const      // Note: uncomment the following line if you are compiling this      // code for a Pentium Pro or later CPU.      // PentProOrLater :=0; // Value and type are irrelevant           .           .           .      #if( @defined( PentProOrLater ) )           fcomip();      // Compare st1 to st0 and set flags.      #else           fcomp();       // Compare st1 to st0.           fstsw( ax );   // Move the FPU condition code bits           sahf();        // into the FLAGS register.      #endif 

Another common use of conditional compilation is to introduce debugging and testing code into your programs. A typical debugging technique that many HLA programmers use is to insert "print" statements at strategic points throughout their code; this enables them to trace through their code and display important values at various checkpoints. A big problem with this technique, however, is that they must remove the debugging code prior to completing the project. The software's customer (or a student's instructor) probably doesn't want to see debugging output in the middle of a report the program produces. Therefore, programmers who use this technique tend to insert code temporarily and then remove the code once they run the program and determine what is wrong. There are at least two problems with this technique:

  • Programmers often forget to remove some debugging statements, and this creates defects in the final program.

  • After removing a debugging statement, these programmers often discover that they need that same statement to debug some different problem at a later time. Hence they are constantly inserting, removing, and inserting the same statements over and over again.

Conditional compilation can provide a solution to this problem. By defining a symbol (say, debug) to control debug output in your program, you can easily activate or deactivate all debugging output by simply modifying a single line of source code. The following code fragment demonstrates this:

 const      debug: boolean := false; // Set to true to activate debug output.           .           .           .      #if( debug )           stdout.put( "At line ", @lineNumber, " i=", i, nl );      #endif 

As long as you surround all debugging output statements with an #if statement like the preceding, you don't have to worry about debug output accidentally appearing in your final application. By setting the debug symbol to false you can automatically disable all such output. Likewise, you don't have to remove all your debugging statements from your programs once they've served their immediate purpose. By using conditional compilation, you can leave these statements in your code because they are so easy to deactivate. Later, if you decide you need to view this same debugging information during a program run, you won't have to reenter the debugging statement: You simply reactivate it by setting the debug symbol to true.

Although program configuration and debugging control are two of the more common, traditional, uses for conditional compilation, don't forget that the #if statement provides the basic conditional statement in the HLA compile time language. You will use the #if statement in your compile time programs the same way you would use an if statement in HLA or some other language. Later sections in this text will present lots of examples of using the #if statement in this capacity.

[5]Except, of course, as parameters to certain HLA compile time functions like @size or @typeName.




The Art of Assembly Language
The Art of Assembly Language
ISBN: 1593272073
EAN: 2147483647
Year: 2005
Pages: 246
Authors: Randall Hyde

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