Chapter 4.1

Advanced Programming using X2c

List of Techniques

Calling C functions from X2c code

Debugging with gdb

UNIX Terminal/keyboard definitiions

Calling C functions from X2c code

The underlying basis of X2c is standard C code, so linking in C routines requires only knowing the details of the equivalent Xbase statements and how they work.

There are a number of ways of calling a routine written directly in C from code written in Xbase and processed by X2c. These sections explain what code X2c expects to find in a C function.

Standard DO to an Xbase procedure:

DO procname [ WITH parm-list]

X2c processes this statement into:

procname(dbx_pn, Pparm1, Pparm2, ....)

int dbx_pn; dbx_Hany *Pparm1, *Pparm2 ... ;

Where the procname is all lower case.

This is based on the normal var function statement created by ffunc of [P-#VVV].

Creating the proper C code to link to this is extremely hard and not recommended. The best way to create such a linkage is use X2c to compile a PRG function and edit the resulting C code.

Standard CALL to a C function, with no return value:

CALL Cname [ WITH parm-list]

X2c processes this statement into:

procname(parm1, parm2, ....)

type-of parm1, parm2 ... ;

Where the procname is the same case as the Xbase statement, with an exact upper-lower case match.

This is based on the var function statement created by the user of [P-xxx], where x is the type of each variable using standard Xbase letters as described below.

Creating the proper C code to link to this is a matter of using the proper C declarations as noted above and is recommended where no return value is required from the function.

Standard CALL to a C function, with a return value:

returnvalue = Cname([parm-list])

X2c processes this statement into:

For numerics and logicals:

returnvalue = procname(parm1, parm2, ....)

type-of parm1, parm2 ... ;

For Character strings and dates:

char *

procname(returnvalue, parm1, parm2, ....)

char *returnvalue; type-of parm1, parm2 ... ;

Where the procname is the same as the Xbase statement in both forms, except in lower case.

This is based on the VAR FUNCTION statement created by the user of [Frxxx], where 'r' is the type the function returns, and 'x' is the type of each variable using standard Xbase letters as described below. If the VAR FUNCTION statement has an Cname clause, that name is used. If not, the name used is the same as the Xbase name, except all in lower case.

Calling functions returning numerics and logicals:

These functions just return the proper value, of the type required.

Calling functions returning character strings or dates:

These functions are passed the pointer (char *) to a fixed length string area into which they must place their result value, and they must return this same pointer.

This scheme is used so that only fixed length strings are used and dynamic string allocation is avoided. These strings will be either 255 character temporary strings or the target variable for simple expressions, such as "svar = cfunc()"

eg:

Using the var function statement in the DBD of

var function cfunc [FCC]

The Xbase statement:

svar = cfunc(svar1

will produce the C statement:

cfunc(svar,svar1)

The Xbase statement:

svar = upper(cvar(svar1))

will produce the C statement:

cvar(dbx_s1,svar1);

stcupper(svar,dbx_s1);

Where: stcupper() is an X2c builtin function for UPPER()

Xbase data type to C data types:

Functions use the VAR FUNCTION parameter description of: [Frxxx]

Procedures use the VAR FUNCTION parameter description of: [P-xxx], which has no defined return value.

where x is the type of each variable using standard Xbase letters of:

C - Character (Ctype: char *)

N - Numeric (Ctype: double)

D - Date (Ctype: char *)

L - Logical (Ctype: BOOL)

M - Memo (Complex C type: not recommended)

The type BOOL is defined as 'unsigned char' in the dbx.h header.

Dates are character strings of the form CCYYMMDD,

where:

CC is Centry, YY is Year,

MM is Month, DD is Day.

Complex parameter definitions:

The VAR FUNCTION statement has 4 parameters of which only two are normally used. The remaining two can be used to control the generated C code in minute detail. This is explained more completely in section 6.3 "User defined functions"

VAR FUNCTION Xbase-name [Cname] param-spec [generator]

Normally, the Cname is the defaulted to be the same as the Xbase-name except totally in lower case and a default generator is provided that maps each Xbase parameter to a matching C parameter in order.

The Cname can be specified exactly using the second parameter and the fourth parameter is used to specify alternate code sequences from the standard one, as detailed in section 6.3

Linking in C code:

The X2c for Lilnux program will recognize the file suffixes of .c (C sources), .o (object modules) and .a (link libraries) and compile or just link as approproprate. The standard X2c headers are automatically available when compiled this way.

Debugging using gdb

Starting gdb:

gdbx2c TOP

.gdbinit: This file sets the initial values for a gdb session. The X2c release set includes a sample .gdbinit, named 'gdbint' (without the dot), in $TPGM/bin. Copy this into your working program directory as the name '.gdbinit'.

Debugging ANY-type variables:

The X-base variable 'xvar' is stored in the C variable 'Vxvar'. The C variable 'Vxvar' is of C type: dbx_Hany, which is a pointer to a complex set of structures managed by the ANY logic for your program. The subsequent sections show how to display an ANY-type variable and then details the internal form of an ANY-type variable. Normal debugging does not normally require internal exploration of an ANY-type variable.

Using command repeating:

gdb supports an emacs-like command recall. More details about these command are found in the gdb manual and readline documentation. (These are provided on the X2c release disks as print files.)

To recall a previous line:

ctl-p recalls a previous like

ctl-r<typing>

does a reverse search for the characters typed.

To edit the current line:

ctl-a goes to beginning of the line

ctl-b goes back one character

ctl-e goes to end of line

ctl-f goes forward one character

Display of ANY-type variables:

gdb provides a means to call a program function and return the result. This is used to provide a simple way of displaying the contents of an ANY-type variable.

To print the variable UVAR, use the gdb commands:

p Adsp(&Vuvar)

ps

This displays the contents of ANYshow, which Adsp() fills with a description of the file.

To display a variable when gdb steps, use the gdb command:

disp "%d,%s", Adsp(&Vuvar), ANYshow

Warning:

Be careful using Adsp() because if you give gdb a bad call to Adsp(), it will cause an signal 11 and prevent you from continuing the running program. Normally Adsp() will not disturb a program being traced, but a signal 11 will disturb it.

Eg: Adsp(Vvar) would cause errors rather than Adsp(&Vvar)

Because the first call to Adsp() allocates a data buffer, Adsp() can not be first called after a program is stopped by interrupt 11 'memory fault'. This will be the error message displayed when gdb stops a program execution.

Note:

ps is defined in .gbdinit as a user command:

define ps

printf "%s", ANYshow

end

To display the internal format of an ANY-type variable:

The X-base variable 'xvar' is stored in the C variable 'Vxvar'. The C variable 'Vxvar' is of C type: dbx_Hany, which is a pointer to a complex set of structures managed by the ANY logic for your program.

The general outline of the structure is:

(1) Vxvar -> ANY variable header

(2) ANY variable header->Current value structure

(3a) For Character values:

Current value structure->current value

(3b) For Other data type: (numeric, logical, date)

Current value structure contains current value

Variables only have a meaningful value once a program is running.

This example will use the X-base variable 'XVAR'.

First load the program and stop at a break point.

Test the variable to see if has been allocated. Variables are initially NULL.

cmd:

p Vxvar

This will return the current address contained in the ANY header pointer:

$1 = (dbx_Hany) 0x0

(This indicates the variable has never been allocated)

$2 = (dbx_Hany) 0x18a1558

(This indicates the variable has been allocated, the actual address is not important)

Now test the header to see if the variable has a current value.

cmd:

p Vxvar[0]

produces:

$3 = {Dvaraddr = 0x...., Dvarname = 0x0,

Dcurrent = 0x..., Dfldref = -1, Dalias = 0 }

This is how a structure is displayed (with pretty print off).

The two meaningful entries values are:

Dvarname: which is the X-base name of the variable,

This will be looked in the t_TOP.c tables, and

is used to reference database fields and macros.

Dcurrent: which is a pointer to the current value structure.

0x0 (NULL) indicates no current value exists.

If Dcurrent is non-NULL, display the current variable structure

cmd:

p Vxvar[0]->Dcurrent[0]

produces:

$4 = {dy_data=..., .....,

dy_content = {Xchar=...., Xreal = <nvalue>}}

This structure contains the current information about a memory variable. Field information is referenced from the database as needed and not stored here. The member of this structure are described in dbx.h under structure 'DBX_dynamic' and tell the type and scope as well as other information about the variable.

The actual data will be in dy_content if the variable is numeric or logical, which has dy_type = 78(N) or 76(L)

Character data is stored indirectly via the pointer dy_data, which has dy_type = 67 (C)

Date data is can be stored either directly or via the pointer dy_data, which has dy_type = 68 (D) depending on whether dy_alloced is set. if dy_alloced = 0, then the data local otherwise it is stored via the dy_data pointer.

Memo data is a memo reference value with is a long on most systems and can not be decoded by hand.

To reference character data, display via the dy_data pointer:

cmd:

x/s Vxvar[0]->Dcurrent[0]->dy_data

This displays the character string the dy_data pointer references.

Using .gdbinit:

The following features can be enabled via .gdbinit

Print of ANY display

This is the ps user command described above

Program terminal output

The gdb command 'tty' can be used to output to an alternate terminal, either a real terminal or virtual console. The sample gdbint has:

tty /dev/tty10

This redirects (input and output) to virual console 10 (Alt-10). To make this work properly you must disable a normal login on this console, with the shell command:

disable tty10

UNIX Terminal / keyboard handling

X2c uses the UNIX system functions of TERMCAP or TERMINFO in conjunction with CURSES for screen output and keyboard input. These C sub-systems provide a database of terminal details, including output and input codes. The environment variable TERM is used by the UNIX system to specify the control codes to be used the terminal running a program. The terminal specified must be defined. (For this discussion, termcap and terminfo are used interchangeably.) The Xenix and most UNIX systems is configured to use TERMINFO, which must be installed with your development system. SUN and other BSD systems use TERMCAP.

X2c for Linux is compiled using the New Curses (libncurses.a) provided. This is a full SYSV terminfo based curses. The best way to have all special keys available with this curses is to make sure the terminal definition is complete. The curses documentation details how to do this. Check the X2c installation procedure for how to install this if you do not already have it installed on your linux system.

For most terminals, the input key definitions are missing or weak. This means that the special Xbase function keys must be augmented by the system manager. The additional key definitions are placed in each user's .profile login file.

The X2c key input system needs these keys defined:

Key name Xbase termcap

control key code meaning

left arrow control-S kl

right arrow control-D kr

up arrow control-E ku

down arrow control-X kd

page up control-R PU

page down control-C PD

home control-A kh

end control-F ke

insert control-V kI

delete control-G kD

escape 27 (fixed)

backspace ctrl-H or 8 (fixed)

The key definitions are needed so the screen handler logic can convert the terminal specific key sequence into a proper Xbase code.

Form 1: the environment variable DBXTERM can be used to define all keys or only missing keys. DBXTERM uses a format like termcap for entries with two styles: character strings assignments or redirection to standard termcap entries.

A redirection entry is written as (X2c code)>(termcap code). The greater than symbol ('>') indicates redirection. The effect is to define the needed X2c key using the termcap code.

The DBXTERM variable can also have capacity entries for E1 through En where each entry defines one extra dBASE key cap. The entries must be in numeric order, so if three extra keys are needed, use the entries E1,E2 and E3. Each entry is of the form: En=keycode*keystring. Where the keycode is a numeric value (which can be negative); and the keystring is standard TERMCAP capability entry

Form 2: Direct lookup is made of the termcap codes listed. For most terminals, only arrow keys are defined. These are written in standard termcap form as detailed in termcap(4) manual section.

Form 3: If no termcap code is found, standard old-form Xbase control keys are available.

Example:

DBXTERM=':ku=\033[A:PD>k0:'

This assignment sets 'ku' to (escape)[A and 'PD' to the value that termcap entry 'k0' has set.

The order of assignments is form 1,2,3, where control keys are needed only if none of the termcap type entries are defined.

A sample for the main XENIX screen of F1-F10 keys is:

(sample from the script file dbxterm)

DBXTERM=:l1=\E[F:kh=\E[H:ic=\E[L :l2=\177:E1=28*\E[M:E2=-1*\E[N:

:E3=-2*\E[O:E4=-3*\E[P:E5=-4*\E[Q:

:E6=-5*\E[R:E7=-6*\E[S:E8=-7*\E[T:

:E9=-8*\E[U:E10=-9*\E[V:

Special Keys:

A program created with X2c also recognizes the BREAK key in XENIX (delete on the console) as an ESCape while a program is running, since Escape is only processed on keyboard waits.

The DBD file:

X2c for Linux automatically loads the standard X2c function definitions from:

x2c.dbd

x2ctail.dbd

The dbd consists of four parts:

option include x2c.dbd

add 'option include third' for third party libraries

var function statements

These are automatically entered using ffunc:

The command is (* below)

ffunc *.prg >>TOP.dbd

procedure statements:

These are automatically entered using ffunc. They are needed if macro operations are done on parameters,

such as (type('param')='U').

The command is (* below)

ffunc -p *.prg >>TOP.dbd

Macro controls:

Use the statement:

option include x2ctail.dbd

It includes the config macro statements described below:

There are three forms, that are used to enable generation of the run-time symbol table for macros:

config macro public all

Enable static public access via macros

config macro any all

Enable ANY variable access via macros, this allows a macro to access an ANY variable, regardless of whether it is a public, private, param or a field

config macro proc all

Enable DO macro and UDF calls from via macros, this fills in the procedure name table with all UDF's and procedure names.

Note about '*' in the command 'ffunc *.prg':

The use of *.prg with ffunc only works properly where only one system is in the current directory. The 'dbd.init' script uses the X2c control file (TOP.def) to determine what prg files are part of a system, hence it should be used.

Chapter 4.2

User Defined Functions

Many serious Xbase applications programmers have turned to User Defined Functions (UDF's) in order to get more power and flexibility into their applications.

Because User Defined Functions are based on individual programming needs, you must provide X2c with the information it needs to include these functions as part of your application.

In X2c for linux, this is done automatically for Xbase written UDF's. X2c creates function definitions and functions using the UNIX stdarg facility for handling functions with a variable number of parameters. As such, the rest of this is interesting only for users writting functions in C.

X2c UDF philosophy.

A user defined function (UDF) is a procedure written in the Xbase language that returns a value. The X2c UDF mechanism is an extension of the internal process for handling Xbase built-in functions and follows the same rules.

X2c must process each UDF in two ways: when the PRG code for the function is compiled to C, and when the UDF is called in an expression.

A UDF declaration is required so X2c knows how to recognize the UDF reference and create the proper C calling sequence and external function declaration. Clipper or Foxbase does not require this because all related actions take place at run-time.

The VAR FUNCTION statement has two forms: a simple form used to define Xbase UDF's, and a more complex form that allows wide flexibility in calling functions written directly in C.

Aspects of UDF's:

There are a number of facets of using UDF's:

Setting up so that X2c expects UDF,

Declaring a UDF: done via a VAR FUNCTION statement,

Defining a UDF: done via a FUNCTION or PROCEDURE statement

and the the actual logic of the function,

Using a UDF: done via the appearance of a function in

an expression, or standing alone in a statement.

Setting up X2c for UDF's:

Since X2c has all the features of all of dBASE III, Clipper and Foxbase, it can disable or enable each set individually. To use UDF's, the 'Extended language' feature must be enabled.

Make sure to use the 'Extended language' feature setup procedure in the installation guide.

Defining Xbase User Defined Functions

X2c supports the Clipper syntax for UDF's, where the UDF begins with the keyword FUNCTION, and the FOX syntax for UDF's, which uses the keyword PROCEDURE.

Recognition by X2c analyzer.

For a UDF to be included in the X2c process, the X2c analyzer must recognize it is needed. This can occur in a number of ways.

UDF's can be part of any .PRG just like in Clipper. If the enclosing procedure is called via DO then the UDF will be processed as part of it. The FUNCTION may be in a procedure file declared in the Xbase source by using the Xbase SET PROCEDURE TO or clipper EXTERN statement. These statements must occur in the TOP PRG or any PRG file otherwise referenced.

Parameter data types.

As in C, all actual parameters in X2c must be of a fixed data type. All references to a UDF must have the same number of parameters. X2c uses these constraints and the VAR FUNCTION statement to allow handling of parameters in ways appropriate to an Xbase function.

X2c allows handling variable parameters types by passing an ANY variable, which itself can take on different data types, thus not violating the C run of fixed data types. This allows the Xbase test of an undefined parameter by using:

TYPE("variable")='U'

X2c allows optional parameters by filling the missing parameters with a dummy and passing a parameter count which can be tested in the PCOUNT() function. This is useful for functions that do not use the stdargs form of parameters.

Returning character, date and ANY values:

Because C has no real character strings, X2c uses the C substitute - static character arrays. C does not have standard routines to efficiently create and destroy strings dynamically. This is also a very slow process and not required for most Xbase programs.

X2c passes a string area to the UDF for which the UDF to place the result string. A phantom parameter is added that does not show in the Xbase expression.

X2c uses a similar approach to return ANY-type values from functions.

Using Xbase User Defined Functions

A UDF is used as a standard Xbase function in an Xbase expression. Depending on the definition, the UDF name matching follows the Xbase 4 character abbreviation rules.

Declaring Xbase User Defined Functions

All User Defined Functions must be declared with a VAR FUNCTION statement. The VAR FUNCTION statements should be placed in the TOP.dbd file.

The statement for declaring Xbase UDF's is:

VAR FUNCTION xbase-name parameter-description

Where:

xbase-name is the name of the function

parameter-description is a string which is a list of the parameter.

These are the two parameters in the VAR FUNCTION statement needed when the function called is also Xbase source. The other parameters have default values filled in by X2c.

The X2c process will automatically create the proper VAR FUNCTION statement for all UDF's that occur as Xbase FUNCTIONs or PROCEDUREs. This is because all parameters are of type ANY, and the process must .only determine the number of parameters. In addtion, the parameter form of 'Q', supports parameters using the UNIX stdarg declaration. With 'stdarg', the number of parameters is handled automatically. 'stdarg' parameters are always of X2c type ANY.

This form of the VAR FUNCTION statement creates C code following the following rules:

Function name: same as Xbase name, except all in lower case.

Returned value is based on function type as per item 5.

First parameter is a integer count of the number of parameters passed.

Second parameter for functions returning character, date or ANY values, is a pointer to an area to place the return value.

Subsequent parameters are of type based on Xbase parameter type:

ANY dbx_Hany *

Character char *

Date char *

Numeric double

Logical int

Declaring C User Defined Functions

The VAR FUNCTION statement has an extended form that allows detailed control over the processing of UDF's into C code. This is needed for any function that does not use the same calling conventions as a Xbase UDF.

The syntax of the extended VAR FUNCTION statement is:

VAR FUNCTION [!] xbase-name [C-name]

parameter-description [code-generator]

Abbreviation flag: X2c allows a '!' before the Xbase name to indicate that the Xbase 4-character abbreviation rules apply to this function.

Xbase-name:

The Xbase function name. The letter case is ignored.

C-name:

The function name placed in the C code. If absent, the C-name will be the same as the X-base name except all in lower case.

This is a keyword which is the exact name placed in the C code. The letter case is significant, since C distinguishes between letter case in function names.

parameter-description

This is a character string specifying the type of value returned by the function and the types of each parameter. The parameter type can be either a simple (upper case) type letter or can have a (lower case) modifier letter to indicate optional or array parameters. The modifiers provide fine control over parameters passed to functions written in C or assembler.

Code-Generator

This is a character string giving a special pattern used to tell X2c how to create the C call. The Code Generator has a type letter and sequence number for each parameter. Type letters can be either normal (upper case) type letters or (lower case) special operations. The special operations provide fine control over parameters passed to functions written in C or assembler.

Function Parameter Description

This description is a constant character string (quoted as usual), which starts with a letter for the function nature, then a letter for the type of the function and each parameter

In a function description, each position has a special meaning:

First character indicates the nature of the function that is being defined (F = Explicit Function, P = Procedure).

The second character identifies what is returned by the function.

The third position can have a '#' to indicate PCOUNT() is enabled.

Remaining positions describe parameters which are passed to the function. An parameter may be modified by being preceded by a lower case letter.

ex. the standard function REPLICATE(expC, expN) is described by "FCCN" - function returning character and taking a character and a numeric.

Function Nature:

F - Explicit function

(only one of this name)

A - Ambiguous function

(more the one of this name)

U - Unsupported function

(known but not available)

P - Procedure

Type Codes: (resulting C type)

- - No value Procedure with no return value

C - Character char *

D - Date char [9]

L - Logical BOOL

M - Memo dbx_memo

N - Numeric REAL (which is double)

A - Array of any type dbx_ary

V - ANY-type dbx_Hany *

Modifier Codes:

Optional: To indicate an optional Xbase parameter, the parameter type code can be preceded by a lower case oh ('o'). This can be used only to call functions written in C or other languages.

Array: To indicate an array of a specific type precede the parameter type code by an lower case aye ('a').

PCOUNT(): To enable PCOUNT() for a function or procedure, the third position must be '#'. This is the position after the return type and before the parameter types. This causes a parameter count value to be placed in the code when the function is processed, for PCOUNT() to be allowed as a function, and for missing parameters to be filled in with NULL (CDV) or 0 (IL).

C code Generator specification:

The X2c expression evaluator uses a template pattern to determine how to create a C statement from the Xbase expression. The pattern is a constant character string (quoted as usual), containing special codes which control the creation of the C function call.

There are a few literal codes that are simply output and a number of parameter output codes.

All parameter output codes are two characters: the first indicates the general output format and the second is a selector which is the sequence number of the parameter to use. When no number is needed, a "*" is used as a space holder.

Literal codes:

These are just output when found in the generator string.

Code Outputs

@ Cname

(), Same character "(),"

More complex literals can be output by enclosing them with vertical bars as quotes (|text|).

Regular codes:

Upper case characters are used for selecting data items of the indicated type character. The second character is the number of the parameter to output in the type selected. The first parameter of the function is numbered zero (0).

Code Outputs As C type

C# Character Parameter char *

D# Date Parameter char *

L# Logical Parameter BOOL

N# Numeric Parameter double

I# Integer Parameter int

M# Memo Parameters dbx_memo

A# Array Parameter dbx_ary

V# ANY-type Parameter dbx_Hany

Note that Xbase UDF's only pass numeric values as type 'double' expressions, so using 'I#' will not work. This is because it will produce a call to the function passing an 'int' value with the function expecting a 'double' value.

Special codes:

The character return value, optional parameters and other differences between C and Xbase environments require flexibility in calling C functions. This is handled by special codes, with the effects listed:

Code meaning

a# Special for CHR() function

b# Optional int parameter, use 0 if missing

c* Adds the value -1 where placed

e* Adds the value +1 where placed

f# Optional int parameter, use 255 if missing

g# Optional int parameter, use 10 if missing

h# Optional int parameter, use -1 if missing

i# Array parameter, required

j# Array parameter, Output NULL if missing or

not an array value given

k# Optional Character value, use NULL if missing

r* Placed before @,

indicates C function returns int value.

v* Places result character string

The Optional parameter codes output a replacement value, if the parameter selected is missing.

'c' and 'e' are used to position inside C array or string

Controlling C function declarations.

Normally, C functions are assumed to return integers unless otherwise explicitly stated. X2c creates C extern function for all functions as required; two forms are created: with and without parameter prototypes.

r* defines a function that returns an integer or logical value, rather than a REAL numeric which is normally expected.

s* will suppress output of the function declaration in the hf file. It is used for functions that are executed inline or are declared in standard system headers.

v* place result string name here

In the last two special codes, the asterisk (*) serves as a place holder.

Beginning the C generator column with r* forces the casting of a int returned value to REAL.

Special handling:

Introductory example:

This example is to orient you for the details to follow. This section ends with a variety of further examples.

Calling the standard function REPLICATE(expC, expN) generates a call to the C function

char *strrep(char * output string,

char * input string, int count)

by the generator "@(v*,C1,I2)". The "v*" is a place holder for the string return value and is explained below.

Parameter typing and 'getting it right'

One of the biggest differences between Xbase and C is the data typing of C. As a result, there are a number of type control codes in the generator. To test a generator, it pays to create a sample program to call your function, run X2c to create the C code and exercise it extensively. If you test functions separately as opposed to trying to test them within a larger program, much effort can be saved.

Returning character and date values:

This applies to any function that returns a character or date value.

C does not have standard routines to efficiently create and destroy strings dynamically. This is also a very slow process and not required for most Xbase programs. Because of this, X2c uses the C substitute - static character arrays.

To properly handle the returning of string values in a UDF, X2c passes a string area to the UDF in which the UDF can place the result string. This requires a special 'return value' place holder parameter that does not appear in the Xbase source. The special generator 'v*' is used for this.

Passing Array values:

Array parameters can be specified in two ways, in the parameter description entry:

1) As an explicit type array or

2) As an array of any type.

The explicit type array is specified by placing a small 'a' in front of the type letter, while any type array can be indicated by using upper case 'A'. This example function has a first parameter of a character array and the second as any type array. Its parameter description is 'aCA'. The generator entries would be either 'i' or 'j' depending on if the arrays are required (i) or optional (j).

Ambiguous function processing:

X2c has type determining function call processor. This allows calling different C functions for the same Xbase name, depending on the parameters. This is called function overloading in an object language like C++ or Objective-C.

A example of a loaded function is the Clipper EMPTY() which takes any type data but does very different tests to determine if the value is empty.

A function is defined as AMBIGUOUS in X2c by having a number of VAR FUNCTION entries, each with a different parameter description. The first letter of the parameter description must be 'A' rather than the usual 'F'. X2c will pick the right declaration based on the supplied parameters. Make sure to have a unique parameter set to choose between each alternative, and do not have optional parameters that will confuse the selection.