#error and #stdout

What are #error and #stdout directives ?

#error : Generate a compiler error and display a message

Syntax :

#error [<messageText>]

Arguments :

<messageText> is the text of the message to be displayed. <messageText> is a literal character string–do not enclose the message in quotations unless you want them to appear as part of the display.

Description :

#error causes the compiler to generate error number C2074. If the <messageText> parameter is specified, an error message is displayed.

Examples :

. This example displays an error message based on whether or not a NETWORK identifier was defined:

 #ifdef NETWORK
 #error Network version not implemented.
 #endif

~~~~~~~~~~~~~~~~~~~~~~~

#stdout : Send literal text to the standard output device

Syntax :

#stdout [<messageText>]

Arguments :

<messageText> is the text of the message to display. <messageTest> is a literal character string. Do not enclose the message in quotation marks unless you want them to appear as part of the display.

Description :

#stdout causes the compiler to output the literal text to the standard output device (stdout) during compilation. If <messageText> is not specified, a carriage return/line feed pair echoes to stdout.

Warning! Manifest constants are not translated in #stdout. Implementation is identical to #error with the following exceptions: output is written to STDOUT and no compiler error is generated.

Examples :

This example demonstrates use of #stdout:

 #ifdef DEBUG
     #stdout Compiling debugging version...
 #endif
PROCEDURE Main()
 ? "Hello world"
RETURN
#stdout End of "Hello World" program

			

#define and #undef

What are #define and #undef directives ?

#define : Define a manifest constant or pseudofunction.

Syntax :

#define <idConstant> [<resultText>]
#define <idFunction>([<arg list>]) [<exp>]

Arguments :

<idConstant> is the name of an identifier to define.

<resultText> is the optional replacement text to substitute whenever a valid <idConstant> is encountered.

<idFunction> is a pseudofunction definition with an optional argument list (<arg list>). If you include <arg list>, it is delimited by parentheses (()) immediately following <idFunction>. <exp> is the replacement expression to substitute when the pseudofunction is encountered. Enclose this expression in parentheses to guarantee precedence of evaluation when the pseudofunction is expanded.

Note: #define identifiers are case-sensitive, where #command and #translate identifiers are not.

Description :

The #define directive defines an identifier and, optionally, associates a text replacement string. If specified, replacement text operates much like the search and replace operation of a text editor. As each source line from a program file is processed by the preprocessor, the line is scanned for identifiers. If a currently defined identifier is encountered, the replacement text is substituted in its place.

Identifiers specified with #define follow most of the identifier naming rules. Defined identifiers can contain any combination of alphabetic and numeric characters, including underscores. Defined identifiers, however, differ from other identifiers by being case- sensitive. As a convention, defined identifiers are specified in uppercase to distinguish them from other identifiers used within a program. Additionally, identifiers are specified with a one or two letter prefix to group similar identifiers together and guarantee uniqueness. Refer to one of the supplied header files.

When specified, each definition must occur on a line by itself. Unlike statements, more than one directive cannot be specified on the same source line. You may continue a definition on a subsequent line by employing a semicolon (;). Each #define directive is specified followed by one or more white space characters (spaces or tabs), a unique identifier, and optional replacement text. Definitions can be nested, allowing one identifier to define another.

A defined identifier has lexical scope like a filewide static variable. It is only valid in the program (.prg) file in which it is defined unless defined in Std.ch or the header file specified on the compiler command line with the /U option. Unlike a filewide static variable, a defined identifier is visible from the point where it is defined in the program file until it is either undefined, redefined, or the end of the program file is reached.

You can redefine or undefine existing identifiers. To redefine an identifier, specify a new #define directive with the identifier and the new replacement text as its arguments. The current definition is then overwritten with the new definition, and a compiler warning is issued in case the redefinition is inadvertent. To undefine an identifier, specify an #undef directive with the identifier as its argument.

#define directives have three basic purposes:

. To define a control identifier for #ifdef and #ifndef

. To define a manifest constant : an identifier defined to represent a constant value

. To define a compiler pseudofunction

The following discussion expands these three purposes of the #define directive in your program.

Preprocessor Identifiers :

The most basic #define directive defines an identifier with no replacement text. You can use this type of identifier when you need to test for the existence of an identifier with either the #ifdef or #ifndef directives. This is useful to either exclude or include code for conditional compilation. This type of identifier can also be defined using the /D compiler option from the compiler command line. See the examples below.

Manifest Constants :

The second form of the #define directive assigns a name to a constant value. This form of identifier is referred to as a manifest constant. For example, you can define a manifest constant for the INKEY() code associated with a key press:

#define K_ESC 27
IF LASTKEY() = K_ESC
 .
 . <statements>
 .
ENDIF

Whenever the preprocessor encounters a manifest constant while scanning a source line, it replaces it with the specified replacement text.

Although you can accomplish this by defining a variable, there are several advantages to using a manifest constant: the compiler generates faster and more compact code for constants than for variables; and variables have memory overhead where manifest constants have no runtime overhead, thus saving memory and increasing execution speed. Furthermore, using a variable to represent a constant value is conceptually inconsistent. A variable by nature changes and a constant does not.

Use a manifest constant instead of a constant for several reasons. First, it increases readability. In the example above, the manifest constant indicates more clearly the key being represented than does the INKEY() code itself. Second, manifest constants localize the definition of constant values, thereby making changes easier to make, and increasing reliability. Third, and a side effect of the second reason, is that manifest constants isolate implementation or environment specifics when they are represented by constant values.

To further isolate the effects of change, manifest constants and other identifiers can be grouped together into header files allowing you to share identifiers between program (.prg) files, applications, and groups of programmers. Using this methodology, definitions can be standardized for use throughout a development organization. Merge header files into the current program file by using the #include directive.

For examples of header files, refer to the supplied header files.

Compiler Pseudo-functions :

In addition to defining constants as values, the #define directive can also define pseudofunctions that are resolved at compile time. A pseudofunction definition is an identifier immediately followed by an argument list, delimited by parentheses, and the replacement expression.

For example:

#define AREA(nLength, nWidth) (nLength * nWidth)
#define SETVAR(x, y) (x := y)
#define MAX(x, y) (IF(x > y, x, y))

Pseudofunctions differ from manifest constants by supporting arguments. Whenever the preprocessor scans a source line and encounters a function call that matches the pseudofunction definition, it substitutes the function call with the replacement expression. The arguments of the function call are transported into the replacement expression by the names specified in the argument list of the identifier definition. When the replacement expression is substituted for the pseudofunction, names in the replacement expression are replaced with argument text. For example, the following invocations,

? AREA(10, 12)
SETVAR(nValue, 10)
? MAX(10, 9)

are replaced by :

? (10 * 12)
nValue := 10
? (IF(10 > 9, 10, 9)

It is important when defining pseudofunctions, that you enclose the result expression in parentheses to enforce the proper order of evaluation. This is particularly important for numeric expressions. In pseudofunctions, you must specify all arguments. If the arguments are not specified, the function call is not expanded as a pseudofunction and exits the preprocessor to the compiler as encountered.

Pseudofunctions do not entail the overhead of a function call and are, therefore, generally faster. They also use less memory. Pseudofunctions, however, are more difficult to debug within the debugger, have a scope different from declared functions and procedures, do not allow skipped arguments, and are case-sensitive.

You can avoid some of these deficiencies by defining a pseudofunction using the #translate directive. #translate pseudofunctions are not case- sensitive, allow optional arguments, and obey the dBASE four-letter rule. See the #translate directive reference in this chapter for more information.

Examples :

. In this example a manifest constant conditionally controls the compilation of debugging code:

 #define DEBUG
 .
 . <statements>
 .
 #ifdef DEBUG
     Assert(FILE("System.dbf"))
 #endif

. This example defines a manifest constant and substitutes it for an INKEY() value:

 #define K_ESC 27
 .
 . <statements>
 .
 IF INKEY() != K_ESC
    DoIt()
 ELSE
    StopIt()
 ENDIF

. This example defines pseudofunctions for the standard functions, MAX() and ALLTRIM():

 #define MAX(arg1, arg2) (IF(arg1 > arg2, arg1, arg2))
 #define ALLTRIM(cString) (RTRIM(LTRIM(cString)))
 .
 . <statements>
 .
 ? MAX(1, 2)
 ? ALLTRIM(" Hello ")
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#undef : Remove a #define macro definition

Syntax :

#undef <identifier>

Arguments :

<identifier> is the name of the manifest constant or pseudofunction to remove.

Description :

#undef removes an identifier defined with the #define directive. After an #undef, the specified identifier becomes undefined. Use #undef to remove an identifier before you redefine it with #define, preventing the compiler warning that occurs when an existing identifier is redefined. Also, use #undef to make conditional compilation specific to certain sections of a program.

Examples :

. To define and then undefine a manifest constant and a pseudofunction:

 #define K_ESC 27
 #define MAX(x, y) IF(x > y, x, y)
 .
 . <statements>
 .
 #undef K_ESC
 #undef MAX

. To use #undef to undefine an identifier before redefining it:

 #define DEBUG
 .
 . <statements>
 .
 #undef DEBUG
 #define DEBUG .T.

. To undefine an identifier if it exists, and otherwise define it for later portions of the program file:

 #ifdef TEST
    #undef TEST
 #else
    #define TEST
 #endif

Conditional compilation

What are conditional compilation directives ?

They are : #ifdef#ifndef#else and #endif.

#ifdef

Compile a section of code if an identifier is defined.

Syntax :

  #ifdef <identifier>
    <statements>...
  [#else]
    <statements>...
  #endif

Arguments :

<identifier> is the name of a definition whose existence is being verified.

Description :

#ifdef…#endif lets you perform a conditional compilation. It does this by identifying a section of source code to be compiled if the specified <identifier> is defined. The <identifier> can be defined using either the #define directive or the /D compiler option which lets you define an identifier or manifest constant from the compiler command line.

The #else directive specifies the code to compile if <identifier> is undefined. The #endif terminates the conditional compilation block.

Conditional compilation is particularly useful when maintaining many different versions of the same program. For example, the demo code and full system code could be included in the same program file and controlled by a single #define statement.

Examples :

. This code fragment is a general skeleton for conditional compilation with #ifdef:

   #define DEMO
    .
    . <statements>
    .
  #ifdef DEMO
   <demo specific statements>
  #endif

. This example controls conditional compilation with an identifier defined on the compiler command line with the /D option.

In the program (.prg) file:

 #ifdef DEBUG
    Assert(<some condition>)
 #endif

. This example defines a manifest constant to one value if it does not exist and redefines it to another if it exists:

 #ifdef M_MARGIN
    #undef M_MARGIN
    #define M_MARGIN 15
 #else
    #define M_MARGIN 10
 #endif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#ifndef

Compile a section of code if an identifier is undefined

Syntax :

  #ifndef <identifier>
    <statements>...
  [#else]
    <statements>...
  #endif

Arguments :

<identifier> is the name of a definition whose absence is being verified.

Description :

#ifndef…#endif lets you perform conditional compilation by identifying a section of source code to compile if the specified <identifier> is undefined.

The #else directive specifies the code to compile if <identifier> is defined. The #endif terminates the conditional compilation block.

Examples :

. This code fragment is a general skeleton for conditional compilation with #ifndef:

 #define DEBUG
   .
   . <statements>
   .
 #ifndef DEBUG
   <optimized version of code>
 #else
   <debugging version of code>
 #endif

. This example compiles a section of code if a specific identifier is undefined.

In the program (.prg) file:

  #ifndef NODEBUG
     Assert(<some condition>)
  #endif

. This example overrides a default definition in the program (.prg) file using a manifest constant defined on the compiler command line with the /D option

In the program (.prg) file:

 #ifndef M_MARGIN
   #define M_MARGIN 15
 #endif

#include directive

What is #include directive ?

#include

Include a file into the current source file

Syntax :

#include “<headerFileSpec>”

Arguments

<headerFileSpec> specifies the name of another source file to include in the current source file. As indicated in the syntax,  the name must be enclosed in double quotation marks.

<headerFileSpec> may contain an explicit path and file name as well as a file extension. If, however, no path is specified,  the preprocessor searches the following places:

. Source file directory

. Directories supplied with the /I option

. Directories specified in the INCLUDE environment variable

#include directives may be nested up to 15 levels deep–that is, a file that has been included may contain #include  directives, up to 15 levels.

Description

#include inserts the contents of the specified file in place of the #include directive in the source file. By convention, the file inserted is referred to as a header file. Header files should contain only preprocessor directives and external declarations. By convention header files have a .ch extension.

When deciding where to locate your header files, you have two basic choices. You can place them in the source file directory where they are local to the current system; or, you can make them globally available by placing them in the directory specified in the INCLUDE environment variable. A list of one or more directories can be specified.

Header files overcome the one major drawback of defining constants or inline functions–the #define directive only affects the file in which it is contained. This means that every program which needs access to these statements must have a list of  directives at the top. The solution to this problem is to place #define statements in a separate file and use the #include directive to tell the preprocessor to include that file before compiling.

For example, suppose the file “Inkey.ch” contains a list of #define directives assigning key values to constants. Instead of including these directives at the top of each program file (.prg) requiring access to them, you can simply place the following line at the top of each program file:

#include “Inkey.ch”

This causes the preprocessor to look for Inkey.ch and place all the directives contained within it at the top of this program.

Another advantage of using the #include directive is that all the #define statements are contained in one file. If any modifications to these statements are necessary, only the #include file need be altered; the program itself remains untouched.

Note that the scope of definitions within an included header file is the current program file unless the header file is included on the compiler command line with the /U option. In this case, the scope is all the program files compiled in the current invocation of the compiler.

Notes

Supplied header files: The compiler provides a number of header files containing manifest constants for common operations.

Std.ch–the standard header file: Std.ch is the standard header file provided with compiler. Std.ch contains the definitions of all compiler commands and the standard functions specified as pseudofunctions. It is strongly recommended that no changes be made to Std.ch. If changes are desired, it is advisable to copy Std.ch to a new name, make the changes, and compile with /U.

This header file differs somewhat from a header file you might #include in that everything defined in Std.ch, with #define, #translate, or #command, has a scope of the entire compile rather than the current source file.

Examples

This example uses #include to insert Inkey.ch, a file of common keyboard definitions, into a key exception handler called by an interface function:

#include "Inkey.ch"
FUNCTION GetEvent()
   LOCAL nKey, nResult
   nKey = INKEY(0)
   DO CASE
      CASE nKey = K_F10
         nResult := DoMenu("Browse")
      CASE nKey = K_ESC
         nResult := DoQuit()
      .
      . <statements>
      .
      CASE nKey = K_CTRL_RIGHT
          nResult := DoNextRec()
   ENDCASE

RETURN nResult