Programming Guide:xppguide

Differences between Clipper 5.x and Xbase++ Foundation

The most important aspect when porting a Clipper program to Xbase++ is given by the fact that the first routine of an Xbase++ application must be called MAIN. Without a MAIN procedure, the linker cannot determine the entry point in the program and the executable file cannot be loaded.

Program code located outside a FUNCTION or PROCEDURE declaration is automatically associated with the MAIN procedure by the Xbase++ compiler. Because of this, there can be only one PRG file with program code located outside FUNCTION or PROCEDURE declarations. Otherwise the MAIN procedure appears to be declared multiple times and an executable file is not created.

The second point to keep in mind is that Xbase++ programs always run in multiple threads. The thread executing implemented program code has the highest priority. It receives CPU access on preference by the operating system. This is why permanently polling DO WHILE loops should be avoided. For example:

DO WHILE Inkey() <> 0 
   <program code> 
ENDDO 

Querying the keyboard in this way leads to permanent polling since there is no wait state in the loop. This is allowed under DOS but contradicts the architecture of a pre-emptive operating system. The above loop must be coded with Xbase++ as follows:

DO WHILE Inkey(0.1) <> 0 
   <program code> 
ENDDO 

Now, the loop has a wait state of 1/10th of a second. This is sufficient to guarantee that all threads of a program will be executed. A Clipper program should be searched for DO WHILE loops which do a permanent polling and provide no wait state. Some functions accept a parameter to achieve a wait state. Another possibility for this is the Sleep() function.

The following table gives an overview of the commands that can lead to incompatibilities when a Clipper program is compiled using Xbase++.

Differences in commands
Clipper Xbase++
CALL Not available
COPY..TO..SDF Xbase++ uses SDF as file extension for a structure extended SDF file
DIR Not available
LABEL FORM Not available
REPORT FORM Not available
SET FORMAT Not available
SET FUNCTION Not available
SET TYPEAHEAD TO 0 Not allowed (min 10, max 100)
KEYBOARD Chr(0) Chr(0) is ignored
RESTORE FROM Existing MEM files cannot be read
RUN Starts a new command shell
FRM file The FRM file format is not supported
LBL file The LBL file format is not supported
MEM file The MEM file format is not supported

When using commands to import or export data the features of a corresponding Xbase++ DatabaseEngine (DBE) must be taken into consideration. DBEs are loaded implicitly when commands are used. This applies to commands like COPY TO ... SDF or APPEND FROM ... SDF. The import or export file, respectively, is maintained by a DatabaseEngine which is loaded into memory if it is not found. In the case of the SDF format this is the SDF Datebase Engine. It creates a structure-extended file with the extension 'SDF'. For this reason, the following code is not allowed:

USE Customer 
COPY TO Customer.sdf SDF 

In this case, the Xbase++ SDFDBE implicitly creates the structure-extended file 'Customer.sdf'. It has the same name as the target file. As a result, a runtime error is raised. At this point, it is recommended to all Clipper programmers to read the specifications of the "Xbase++ DatabaseEngines" in the Xbase++ documentation. The Xbase++ DBEs differ in many aspects from Clipper's RDDs.

Xbase++ differs from Clipper in that keyboard entries are registered in the event queue. The command SET TYPEAHEAD TO 0 in Xbase++ causes all events in the event queue to be deleted. The number of events in the event queue cannot be set to 0, since this would lead to a system halt. The minimum number of events that can be stored in the event queue is ten.

The KEYBOARD command is translated to a keyboard event that is added to the event queue. The character Chr(0) is an invalid event and is ignored. This can lead to problems in Clipper programs which use Chr(0) for setting the return code of the Lastkey() function to a defined value. If this is the case, another character must be used for this purpose, e.g. Chr(255). An alternative is also given by the preprocessor:

#xtranslate KEYBOARD( Chr(0) )   =>  KEYBOARD Chr(255) 
#xtranslate KEYBOARD Chr(0)      =>  KEYBOARD Chr(255) 
#xtranslate Lastkey() == 0       =>  Lastkey() == 255 
#xtranslate Lastkey() = 0        =>  Lastkey() == 255 

The MEM file format is not supported by Xbase++. This means existing MEM files cannot be read. In Xbase++, XPF files replace MEM files. Objects, arrays and code blocks can be stored using this file format as well as character, date, numeric and logical data types. This opens a completely new dimension for communicating between workstations on a network. Using the XPF file format, code blocks can be exchanged between two workstations.

Differences in functions
Clipper Xbase++
AEval() Fifth parameter determines whether array elements are passed by reference to the code block
CurDir() Returns or changes the current directory
Not available CurDrive() returns or changes the current drive
File() Includes second parameter "D", "H", "S"
FkLabel() Not available
FkMax() Not available
NoSnow() Not available
ReadKey() Not available
SetBlink() Only available in full screen mode (VIO mode)
SetColor() Intensity attribute is supported for background color
Word() Not available

In Xbase++, the function AEval() accepts a fifth parameter that is a logical value. This specifies whether array elements are passed by reference or by value to the code block. The following line creates an array and fills the 10 elements with their corresponding indexes:

aArray := AEval( Array(10), {|x,i| x:=i },,, .T.) 

The functions CurDir() and File() have expanded functionality in Xbase++ (compared to Clipper). The additional function CurDrive() is also included in Xbase++. The current directory and drive can be changed using CurDir() and CurDrive() without using the command RUN. Since RUN operates differently under Xbase++ by starting another command shell, the Clipper technique will not change the directory in the application under Xbase++. The function File() accepts a file type as a second parameter. An example of the enhanced file function is shown in the following code that checks for the existence of a directory:

cDir := Space(64) 
@ 10, 10 GET cDir 
READ 

IF ! File( Trim(cDir), "D") 
   Alert( "Invalid directory") 
ELSE 
   CurDir( cDir ) 
ENDIF 

The GetDoSetKey() function programmed in GETSYS.PRG calls GetPostValidate() prior to evaluating a SetKey() code block. By this, Xbase++ guarantees that no invalid data is written to database fields.

Reserved keywords

Xbase++ has more reserved keywords than Clipper. Using an Xbase++ reserved keyword as an identifier for variables, functions etc., causes the Xbase++ compiler to assert an error. A list of all reserved keywords is found in the online help in the section "Language elements of Xbase++".

Differences in instance variables and methods of the class Get()
Clipper Xbase++
oGet:assign oGet:_assign()
oGet:end oGet:_end()
oGet:badDate oGet:badDate()
oGet:minus oGet:minus()
oGet:picture When not indicated, default picture
oGet:subScript Always NIL
Not available oGet:posInBuffer()

Differences in instance variables and methods of the class TBrowse()
Clipper Xbase++
oTbrowse:end() oTBrowse:_end()
oTBrowse:rowPos:=<n> Does not synchronize with the data source but only moves the TBrowse cursor
oTBrowse:nTop:=<n> :configure() must be subsequently executed
oTBrowse:nLeft:=<n> :configure() must be subsequently executed
oTBrowse:nBottom:=<n> :configure() must be subsequently executed
oTBrowse:nRight:=<n> :configure() must be subsequently executed
Not available oTBrowse:firstScrCol()
Not available oTBrowse:viewArea()

Parameters for navigation code blocks of the class TBrowse()
Clipper Xbase++
EVAL( oTBrowse:skipBlock, nSkip, oTBrowse )
EVAL( oTBrowse:goTopBlock, oTBrowse )
EVAL( oTBrowse:goBottomBlock, oTBrowse )

A leading underscore is added to the names of the :assign() and :end() methods of the Get and TBrowse classes in Xbase++. This is done because the previous identifiers are reserved keywords. No changes need to be made in Clipper programs, since the Xbase++ preprocessor makes this change.

Clipper requires the instance variable oGet:subScript to obtain correct references to array elements used as variables in the @...GET command. Xbase++ uses the reference operator @ instead, since single array elements can be passed by reference. For this reason, oGet:subScript is obsolete and always contains NIL. To achieve in Xbase++ the same behavior as in Clipper, an appropriate value must be assigned to oGet:subScript. For example:

LOCAL aArray := {.t., .f.} 
CLS 
SET CURSOR ON 
@ 10, 10 GET aArray[1] PICTURE "Y" SEND subScript := {1} 
@ 11, 10 GET aArray[2] PICTURE "Y" SEND subScript := {2} 
READ 

There is an essential difference between the TBrowse class in Xbase++ and Clipper. Assignments to the instance variable :rowPos do not automatically synchronize the record pointer in the data source. If the TBrowse cursor is repositioned by changing :rowPos, the record pointer of the data source must be explicitly moved. Also, the TBrowse object is passed as a parameter to the code blocks :skipBlock, :goTopBlock and :goBottomBlock.

In addition, methods have been added to the Get class and the TBrowse class which are required for supporting mouse control.

Functions and commands

Xbase++ Replacement functions
Clipper Xbase++
DbSetDriver() DbeSetDefault()
RddList() DbeList()
RddSetDefault() DbeSetDefault()

Different behavior of commands
Command Clipper Xbase++
SET EXCLUSIVE Default value is ON Default value is OFF
SET EXACT Is not considered with SEEK / DbSeek() Is considered with SEEK / DbSeek()
CREATE INDEX Sorting order of characters depends on NTX???.OBJ modules that must be linked Sorting order of characters depends on SET COLLATION and SET LEXICAL
COMMIT at end of program Is implicitly executed Is not implicitly executed

Different behavior of the function DbCreateExtStruct()
Column Clipper Xbase++
FIELD_LEN The field length is 3 The field length is 5
FIELD_DEC Used when field length > 999 Not Used
  1. See also the function !LINKDbCreateExtStruct()!ELINK

RDD versus DBE

The database function DbSetDriver() and all the Rdd...() functions of Clipper are not available in Xbase++. This is because Xbase++ does not support the proprietary concept of a monolithic RDD (Replaceable Database Driver). Instead of the RDD approach, the concept of database engines is used in Xbase++ and offers much more flexibility in data and file management. Because of this difference, Xbase++ offers the Dbe...() functions instead of the Rdd...() functions in Clipper.

SET EXCLUSIVE

SET EXCLUSIVE is set OFF by default in Xbase++. This means that by default, databases are opened in SHARED mode for multi-user (network) operation in Xbase++. If a Clipper application not designed for multi-user access is recompiled under Xbase++, the command SET EXCLUSIVE ON needs to be inserted in the startup routine before any databases are opened.

SET EXACT

When SEEK and DbSeek() are used to search for character values in a database, Xbase++ uses the toggle SET EXACT to determine whether blank spaces at the end of character strings should be ignored in comparing character values.

CREATE INDEX

Xbase++ supports collation tables. A collation table assigns weighing factors to single characters. This allows the sorting order of characters to be user-defined. A collation table is used for string comparison as well as for index creation. The collation table is extremely important when creating index files to be accessed from both Xbase++ and Clipper. In this case, Xbase++ must use the same collation table as Clipper. In Clipper, the sorting order of characters is defined at link time by a nation module, and cannot be changed at runtime.

Clipper includes various NTX???.OBJ files that must be linked to the EXE in order to define the country-specific sorting of characters. To achieve the same sorting order with Xbase++, the corresponding collation table must be activated using the SET COLLATION TO command. The following table lists all language specific differences between Clipper and Xbase++.

Language-specific sorting of characters
Language Clipper module Xbase++ command
American not available SET COLLATION TO AMERICAN
British not available SET COLLATION TO BRITISH
Danish NTXDAN.OBJ SET COLLATION TO DANISH
Dutch NTXDUT.OBJ SET COLLATION TO DUTCH
Finnish NTXFIN.OBJ SET COLLATION TO FINNISH
French NTXFRE.OBJ SET COLLATION TO FRENCH
German NTXGER.OBJ SET COLLATION TO GERMAN
Greek 437 NTXGR437.OBJ SET COLLATION TO GREEK437
Greek 851 NTXGR851.OBJ SET COLLATION TO GREEK851
Icelandic 850 NTXIC850.OBJ SET COLLATION TO ICELANDIC850
Icelandic 861 NTXIC861.OBJ SET COLLATION TO ICELANDIC861
Italian NTXITA.OBJ SET COLLATION TO ITALIAN
Norwegian NTXNOR.OBJ SET COLLATION TO NORWEGIAN
Portuguese NTXPOR.OBJ SET COLLATION TO PORTUGUESE
Spanish NTXSPA.OBJ SET COLLATION TO SPANISH
Swedish NTXSWE.OBJ SET COLLATION TO SWEDISH
System not available SET COLLATION TO SYSTEM
ASCII SET COLLATION TO ASCII

The default for SET COLLATION TO is defined in DBESYS.PRG. It depends on the country specific version of Xbase++. If index files are to be accessed from Xbase++ as well as from Clipper, you must include the following lines in your code:

#ifdef __XPP__ 
   SET COLLATION TO <your country> 
#endif 

Accessing index files from Xbase++ and Clipper at the same time requires both using the same sorting order for characters. When a Clipper program uses a different sorting order than an Xbase++ program, index files will get corrupted sooner or later.

CREATE FROM

The format of a structure-extended DBF file is different in Xbase++. The length of the field FIELD_LEN is 5, not 3 as in Clipper. This allows the maximum possible field length to be entered directly. It is no longer necessary to use the decimal place field to enter field lengths longer than 999 characters.

Memo Files

The maximum number of characters that can be stored in a memo field is not limited to 64 KB under Xbase++. If memo fields are accessed in a multi-user environment from Clipper and Xbase++, the 64 KB limitation must be implemented in Xbase++.

COMMIT at program's end

When a Clipper program ends, it commits all pending record updates to databases still open (the COMMIT command is implicitly executed). This is not the case with Xbase++. Instead, Clipper's behaviour is programmed in the file APPEXIT.PRG which contains the implicit EXIT PROCEDURE AppExit(). This procedure is called when a program terminates and can be changed to meet a programmer's needs.

RETURN and QUIT

An optional numeric parameter can be specified for QUIT. It is passed to the ErrorLevel() function before an application terminates. The same can be achieved if the function Main() returns a numeric value with the RETURN statement.

Constants for Set() function
Constant Clipper Xbase++
_SET_DEBUG Supported Not supported
_SET_SCROLLBREAK Supported Not supported
_SET_CHARSET Not available Available
_SET_COLLATION Not available Available
_SET_LEXICAL Not available Available
_SET_TIME Not available Available

Other differences
Topic Clipper Xbase++
PROCDDURE Main Must be the first procedure of the application
APPSYS.PRG Not available Has implicit INIT procedure
DBESYS.PRG Not available Has implicit INIT procedure
ERRORSYS.PRG Has implicit INIT procedure Has implicit INIT procedure
RDDSYS.PRG Has implicit INIT procedure Not available
APPEXIT.PRG Has implicit EXIT procedure Not available
PICTURE function @E Numbers are displayed in the country specific format of the operating system
Transform(1.23,"@N") Decimal point and thousands separator are configurable
Time() Separators between HH:MM:SS are configurable
SetLocale() Not available Configures country specific settings

The configuration settings of the operating system are used to determine the default separators used in formatting numeric and date values as well as the return value of the function Time(). Thus, the PICTURE formatting with @E is obsolete and is ignored by Xbase++. However, numerous country specific settings can be configured with the function SetLocale().

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.