Programming Guide:xppguide

Differences in the preprocessor and compiler Foundation

The results of the Xbase++ and Clipper preprocessors and compilers differ in some very special situations. The following code examples show some situations where the Clipper preprocessor or compiler deviates from the Xbase++ preprocessor or compiler.

Command options

Using reserved function names as options in user-defined commands can lead to problems with the preprocessor in Xbase++. For example:

#command @ <row>, <col> MYCOMMAND <expr> [MIN <min>] [MAX <max>] ; 
      => MyFunction( <row>, <col>, <expr>, <min>, <max>) 

This command is translated correctly unless the function Min() or Max() is used for the optional parameters <min> and <max>. The following would not be permitted in Xbase++:

@ 10,20 MYCOMMAND "DoSomething" MIN Min(10,x) 

In this case, the keyword used as part of the option of the command is identical to the keyword for the optional parameters. This is not permitted in Xbase++.

Numeric constants

Numeric constants can be programmed in Xbase++ using decimal, hexadecimal or scientific notation. Clipper understands only decimal notation. The following constants are valid in Xbase++:

3.1415926     // decimal 
0xFF          // hexadecimal 
10.1E-10      // scientific 

PARAMETERS statement

With the PARAMETERS statement, the Xbase++ compiler is more strict than Clipper. The statement is used to declare formal parameters for functions or procedures as variables of the PRIVATE storage class (Clipper '87). Since PARAMETERS is an executable statement, it may only be used after lexical variable declarations like LOCAL or STATIC. For example:

// Permitted                // Not permitted 
PROCEDURE MyProc            PROCEDURE MyProc            
   LOCAL cString               PARAMETERS p1, p2        
   PARAMETERS p1, p2           LOCAL cString            
                                                        
   <Code>                      <Code>                   
RETURN                      RETURN                      

In Xbase++, formal parameters should not be declared using PARAMETERS. Instead, a comma-separated list of parameters should be written within parentheses when declaring procedures, functions or methods:

PROCEDURE MyProc( p1, p2 ) 
   LOCAL cString    
                    
   <Code>           
RETURN              

Alias operator

In Xbase++, only one variable identifier or the macro operator may be specified after the alias operator for dynamic memory variables M-> or MEMVAR-> and field variables FIELD->. An expression, with or without parentheses, cannot be used in these cases.

MEMVAR->(&cField)           // is supported in Clipper, 
FIELD->(&cField)            // but not in Xbase++ 

Correct in Xbase++:

MEMVAR->&(cField) 
FIELD->&(cField) 

Macro operator &

When the macro operator is used within code blocks, Xbase++ always employs what is called "late evaluation". This can lead to different results in special cases. For example:

LOCAL  oTBrowse, oTBColumn, i 
PRIVATE cFieldName 

USE Customer NEW 
oTBrowse:= TBrowseNew() 

FOR i:=1 TO FCount() 
   cFieldName := FieldName(i) 
   oTBColumn  := TBColumnNew( cFieldName, {|| &cFieldName } ) 
   oTbrowse:addColumn( oTBColumn ) 
NEXT 

The intention of this program code is to create a TBrowse object that displays columns for all fields in a database. The field name is assigned to a PRIVATE variable that is macro-expanded within the code block. In Clipper, "early evaluation" occurs and the code block references the field whose name is contained in the PRIVATE variable cFieldName when the code block is created. In Xbase++, the macro expression is only evaluated when the code block is evaluated. Because of this, all columns in the TBrowse object display the contents of the last field, since its name is contained in the variable cFieldName after the FOR...NEXT loop terminates. The following would be the correct code to use in this situation under Xbase++:

oTBColumn  := TBColumnNew( cFieldName, &("{||"+cFieldName+"}") ) 

In this case, "early evaluation" is forced because the code block is first created as a character string and then macro-expanded. The code block then contains a reference to the actual field variable and not to the PRIVATE variable cFieldName.

The creation of file names with consecutive numbers as extension can be solved in Clipper in the following way:

PRIVATE nNumber := "1" 

RESTORE FROM SaveFile.&nNumber 

This macro expression is not valid in Xbase++. It must be recoded, for example by first assigning the file name to a variable and then using the variable as command parameter:

PRIVATE nNumber   := "1" 
PRIVATE cFileName := "SaveFile.&nNumber" 

RESTORE FROM &cFileName 

If a syntax error is reported when a Clipper program is compiled in Xbase++, the Xbase++ preprocessor output should be checked to see if it has generated the correct code. The output of the preprocessor can be written to a PPO file using the compiler option /p.

USE &(dictionary->cFilename) 

The line above can be used in Clipper to open a file (cFilename) whose name is stored in a database (dictionary). The Xbase++ preprocessor converts &(...) to a character string. The correct code for Xbase++ is:

USE (dictionary->cFilename) 

Array operator [ ]

The array operator is called index operator in Xbase++ and is not restricted to values of data type Array, but can also be used for character strings, numerics or Objects. In this case, the operator retrieves a single character of a string at a specific position, the bit value from a numeric or the exported member variable of the object. Using the index operator for Strings increases performance dramatically.

LOCAL cName := "Xbase++" 

cChar := SubStr( cName, 3, 1 )   // Clipper style 

cChar := cName[3]                // Xbase++ style 

Substring operator $

The substring operator is not restricted to values of data type Character, but can also be used to check if a value is stored in an array. If the right operand is a value of data type Array, the operator searches the value of the left operand in the array and returns .T. (true) if the value is found, otherwise it returns .F. (false). This is valid code in Xbase++, but not in Clipper:

? 2     $ {2,4,6}                // result: .T. 
? 4     $ {{1,2},{4,6}}          // result: .T. 

? {1,6} $ {1,2,4,6}              // result: .T. 

Reference operator @

Clipper allows the following:

x := IIf( y, @param, z ) 

When this code is compiled and executed under Clipper, x contains a reference to param if y equals .T. (true). This is not the case in Xbase++.

On the other hand, with Xbase++, any variable can be passed by reference to functions, procedures or methods. This includes array elements, member variables and field variables. The following function calls are allowed with Xbase++ but not with Clipper:

MyFunc( @Customer->Name ) 
MyFunc( @oTBrowse:colorSpec ) 
MyFunc( @aArray[3,12,5] ) 

#define __XPP__

The Xbase++ compiler defines the constant __XPP__. This allows the maintenance of different source code within a single PRG file when it is to be compiled by Clipper and Xbase++.

#ifdef __XPP__ 
   <Xbase++ Code> 
#else 
   <Clipper Code> 
#endif 

#pragma

The Xbase++ compiler knows pragmas which do not exist in Clipper. A pragma is a directive that toggles compile switches at compile time. This allows a particular compile switch for a single line in a PRG file to be set or unset.

Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.