version 2003 (Modified)
The compiler expects that the usual syntactic rules for 4th Dimension commands are followed. It does not require any special modifications for databases that will be compiled.
This section nevertheless provides certain reminders and specific details:
Some commands that affect a variable's data type may lead, if you are not careful, to data type conflicts.
Since certain commands use more than one kind of syntax or parameters, it is to your advantage to know which is the most appropriate one to select.
Strings
Ascii (character)
For commands operating on strings, only the Ascii function requires special attention. In interpreted mode, you can pass either a non-empty string or an empty string to this function.
In compiled mode, you cannot pass an empty string.
If you pass an empty string, and if the argument passed to Ascii is a variable, the compiler will not be able to detect an error in compilation.
Communications
SEND VARIABLE(variable)
RECEIVE VARIABLE(variable)
These two commands are used for writing and receiving variables sent to disk. Variables are passed as parameters to these commands.
The parameter you pass must always be of the same data type. Suppose you want to send a list of variables to a file. To eliminate the risk of changing data types inadvertently, we recommend that you specify the data type of the variables being sent at the head of the list. This way, when you receive these variables, you will always begin by getting an indicator. Then, when you call RECEIVE VARIABLE, the transfer is managed by a Case of statement.
Example:
SET CHANNEL(12;"File") If (OK=1) $Type:=Type([Client]Total_TO) SEND VARIABLE($Type) For($i;1;Records in selection) $Send_TO:=[Client]Total_TO SEND VARIABLE($Send_TO) End for End if SET CHANNEL(11) SET CHANNEL(13;"MyFile") If (OK=1) RECEIVE VARIABLE($Type) Case of :($Type=Is String Var) RECEIVE VARIABLE($String) `Processing variable received :($Type=Is Real) RECEIVE VARIABLE($Real) `Processing variable received :($Type=Is Text) RECEIVE VARIABLE($Text) `Processing variable received End case End if SET CHANNEL(11)
Structure access
Field (field pointer) or (table number;field number)
Table(table pointer) or (table number) or (field pointer)
These two commands return results of different data types, according to the parameters passed to them:
If you pass a pointer to the Table function, the result returned is a number.
If you pass a number to the Table function, the result returned is a pointer.
The two functions are not sufficient for the compiler to determine the data type of the result. In such cases, use a compiler directive to avoid any ambiguity.
Documents
Keep in mind that the document references returned by the Open document, Append document and Create document functions are of the data type Time.
Math
Mod (value;divider)
The expression "25 modulo 3" can be written in two different ways in 4th Dimension:
Variable:=Mod(25;3)
or
Variable:=25%3
The compiler sees a difference between the two: Mod applies to all numerics, while the operator % applies only to Integers and Long Integers. If the operand of the % operator exceeds the range of the Long Integer data type, the returned result is likely to be wrong.
Exceptions
IDLE
ON EVENT CALL (Method{; ProcessName})
ABORT
ON EVENT CALL
The IDLE command has been added to 4th Dimension language to manage exceptions. This command should be used whenever you use the ON EVENT CALL command.
This command could be defined as an event management directive.
Only the kernel of 4th Dimension is able to detect a system event (mouse click, keyboard activity, and so forth). In most cases, kernel calls are initiated by the compiled code itself, in a way that is transparent to the user.
On the other hand, when 4th Dimension is waiting passively for an eventfor example, in a waiting loopit is clear that there will be no call.
Example under Windows
`MouseClick Method If (MouseDown=1) <>vTest:=True ALERT("Somebody clicked the mouse") End if `Wait Method <>vTest:=False ON EVENT CALL("MouseClick") While(<>vTest=False) `Event's waiting loop End while ON EVENT CALL("")
In this case, you would add the IDLE command in the following manner:
`Wait Method <>vTest:=False ON EVENT CALL("MouseClick") While(<>vTest=False) IDLE `Kernel call to sense an event End while ON EVENT CALL("")
ABORT
Use this command only in error-handling project methods. It works exactly as it does in 4th Dimension, except in a method that has been called by one of the following commands: EXECUTE, APPLY TO SELECTION and APPLY TO SUBSELECTION. Try to avoid this situation.
Arrays
Seven 4th Dimension commands are used by the compiler to determine the data type of an array. They are:
COPY ARRAY(source;destination)
SELECTION TO ARRAY(field;array)
ARRAY TO SELECTION(array;field)
SELECTION RANGE TO ARRAY(start;end;field;array)
LIST TO ARRAY(list;array{; itemRefs})
ARRAY TO LIST(array;list{; itemRefs})
DISTINCT VALUES(field;array)
COPY ARRAY
The COPY ARRAY command accepts two array type parameters. If one of the array parameters is not declared elsewhere, the compiler determines the data type of the undeclared array from the data type of the declared one.
This deduction is performed in the two following cases:
The array typed is the first parameter. The compiler assigns the data type of the first array to the second array.
The declared array is the second parameter. Here, the compiler assigns the data type of the second array to the first array.
Since the compiler is strict about data types, COPY ARRAY can be performed only from an array of a certain data type to an array of the same type.
Consequently, if you want to copy an array of elements whose data types are similar, i.e., Integers, Long Integers and Reals, or Texts and Strings, or Strings with different lengths, you have to copy the elements one by one.
Suppose you want to copy elements from an Integer array to a Real array. You can proceed as follows:
$Size:=Size of array(ArrInt) ARRAY REAL(ArrReal;$Size) `Set same size for Real array as the Integer array For($i;1;$Size) ArrReal{$i}:=ArrInt{$i} `Copy each of the elements End for
Remember that you cannot change the number of dimensions of an array during the process. If you copy a one-dimensional array into a two-dimensional array, the compiler generates an error message.
SELECTION TO ARRAY, ARRAY TO SELECTION, DISTINCT VALUES, SELECTION RANGE TO ARRAY
As with 4th Dimension in interpreted mode, these four commands do not require the declaration of arrays. The undeclared array will be assigned the data type of the field specified in the command.
If you write:
SELECTION TO ARRAY([MyTable]IntField;MyArray)
the data type of MyArray would be an Integer array having one dimension (assuming that IntField is an integer field).
If the array has been declared, make sure that the field is of the same data type. Although Integer, Longint and Real are similar types, they are not equivalent.
On the other hand, in the case of Text and String data types, you have a little more latitude. By default, if an array was not previously declared and you apply a command that includes a String type field as a parameter, the default data type assigned to the array is Text. If the array was previously declared as String or Text, these commands will follow your directives.
The same is true for Text type fieldsyour directives have priority.
Remember that the SELECTION TO ARRAY, SELECTION RANGE TO ARRAY, ARRAY TO SELECTION and DISTINCT VALUES commands can only be used with a one-dimensional array.
The SELECTION TO ARRAY command also has a second syntax:
SELECTION TO ARRAY(table;array).
In this case, the MyArray variable will be an array of Longints. The SELECTION RANGE TO ARRAY command works in the same way.
LIST TO ARRAY, ARRAY TO LIST
The LIST TO ARRAY and ARRAY TO LIST commands only concern two types of arrays:
one-dimensional String arrays, and
one-dimensional Text arrays.
These commands do not require that the array passed as a parameter be declared. By default, the non-declared array will be typed as a Text array. If the array was previously declared as String or Text, these commands will follow your directives.
Using pointers in array-related commands
The compiler cannot detect a data type conflict if you use a dereferenced pointer as a parameter to an array-declaration command. If you write:
SELECTION TO ARRAY([Table]Field;Pointer->)
where Pointer-> stands for an array, the compiler cannot check whether the field type and array type are identical. It is up to you to prevent such conflicts; you should type the array referred to by the pointer.
The compiler issues a warning whenever it encounters an array declaration statement in which one of the parameters is a pointer. These messages can be helpful in detecting this type of conflict.
Local arrays
If your database uses local arrays (arrays recognized only in the method in which they were created), it is necessary to declare them explicitly in 4th Dimension before using them.
To declare a local array, use one of the array commands such as ARRAY REAL, ARRAY INTEGER, etc.
For example, if a method creates a local Integer array with 10 elements, you need to declare the array before using it. Use the command:
ARRAY INTEGER($MyArray;10)
Language
Get pointer(varName)
Type (object)
EXECUTE(statement)
TRACE
NO TRACE
Get pointer
Get pointer is a function that returns a pointer to the parameter that you passed to it. Suppose you want to initialize an array of pointers. Each element in that array points to a given variable. Suppose there are twelve such variables named V1, V2, V12. You could write:
ARRAY POINTER(Arr;12) Arr{1}:=->V1 Arr{2}:=->V2 Arr{12}:=->V12
You could also write:
ARRAY POINTER(Arr;12) For($i;1;12) Arr{$i}:=Get pointer("V"+String($i)) End for
At the end of this operation, you get an array of pointers where each element points to a variable Vi.
These two sequences can be compiled. However, if the variables V1 to V12 are not used explicitly elsewhere in the database, the compiler cannot type them. Therefore, they must be used or declared explicitly elsewhere.
Such explicit declaration may be performed in two ways:
By declaring V1, V2, V12 through a compiler directive:
C_LONGINT(V1;V2;V3;V4;V5;V6;V7;V8;V9;V10;V11;V12)
By assigning these variables in a method:
V1:=0 V2:=0 V12:=0
Type (object)
Since each variable in a compiled database has only one data type, this function may seem to be of no use. However, it can be useful when you work with pointers. For example, you may need to know the data type of the variable to which a pointer refers; due to the flexibility of pointers, one cannot always be sure to what object it points.
EXECUTE
This command offers benefits in interpreted mode that are not carried over to compiled mode.
In compiled mode, a method name passed as a parameter to this command is interpreted. Therefore, you miss some of the advantages provided by the compiler, and your parameter's syntax cannot be checked.
Moreover, you cannot pass local variables as parameters to it.
EXECUTE can be replaced by a series of statements. Two examples are given below.
Given the following sequence:
i:= FormFunc EXECUTE("INPUT FORM (Form"+String(i)+")")
It can be replaced by:
i:=FormFunc VarForm:="Form"+String(i) INPUT FORM(VarForm)
Below is another example:
$Num:=SelPrinter EXECUTE("Print"+$Num)
Here, EXECUTE can be replaced with Case of:
Case of : ($Num=1) Print1 : ($Num=2) Print2 : ($Num=3) Print3 End case
The EXECUTE command can always be replaced. Since the method to be executed is chosen from the list of the database's project methods or the 4th Dimension commands, there is a finite number of methods. Consequently, it is always possible to replace EXECUTE with either Case of or with another command. Furthermore, your code will execute faster.
TRACE, NO TRACE
These two commands are used in the debugging process. They serve no purpose in a compiled database. However, you can keep them in your methods; they will simply be ignored by the compiler.
Variables
Undefined(variable)
SAVE VARIABLES(document;variable1{; variable2 })
LOAD VARIABLES(document;variable1{; variable2 })
CLEAR VARIABLE(variable)
Undefined
Considering the typing process carried out by the compiler, a variable can never be undefined in compiled mode. In fact, all the variables have been defined by the time compilation has been completed. The Undefined function therefore always returns False, whatever parameter it is passed.
Note: To know if your application is running in compiled mode, call the Compiled application command.
SAVE VARIABLES, LOAD VARIABLES
In interpreted mode, you can check that the document exists by testing if one of the variables is undefined after performing a LOAD VARIABLES. This is no longer feasible in compiled databases, because the Undefined function always returns False.
This test can be performed in either interpreted or compiled mode by:
1. Initializing the variables that you will receive to a value that is not a legal value for any of the variables.
2. Comparing one of the received variables to the initialization value after LOAD VARIABLES.
The method can be written as follows:
Var1:="xxxxxx" `"xxxxxx" is a value that cannot be returned by LOAD VARIABLES Var2:="xxxxxx" Var3:="xxxxxx" Var4:="xxxxxx" LOAD VARIABLES("Document";Var1;Var2;Var3;Var4) If(Var1="xxxxxx") `Document not found Else `Document found End if
CLEAR VARIABLE
This routine uses two different syntaxes in interpreted mode:
CLEAR VARIABLE(variable)
CLEAR VARIABLE("a")
In compiled mode, the first syntax of CLEAR VARIABLE(variable) reinitializes the variable (set to null for a numeric; empty string for a character string or a text, etc.), since no variable can be undefined in compiled mode.
Consequently, CLEAR VARIABLE does not free any memory in compiled mode, except in four cases: Text, Picture, BLOB and Array type variables.
For an array, CLEAR VARIABLE has the same effect as a new array declaration where the size is set to null.
For an array MyArray whose elements are of the Integer type, CLEAR VARIABLE(MyArray) has the same effect as one of the following expressions:
ARRAY INTEGER(MyArray;0) `if it as a one-dimensional array ARRAY INTEGER(MyArray;0;0) `if it is a two-dimensional array
The second syntax, CLEAR VARIABLE("a"), is incompatible with the compiler, since compilers access variables by address, not by name.
Pointers with certain commands
The following commands have one common feature: they accept an optional first parameter [Table], and the second parameter can be a pointer.
ADD TO SET | LOAD SET |
APPLY TO SELECTION | LOCKED ATTRIBUTES |
COPY NAMED SELECTION | ORDER BY |
CREATE EMPTY SET | ORDER BY FORMULA |
CREATE SET | OUTPUT FORM |
CUT NAMED SELECTION | PAGE SETUP |
DIALOG | Print form |
EXPORT DIF | PRINT LABEL |
EXPORT SYLK | QR REPORT |
EXPORT TEXT | QUERY |
GOTO RECORD | QUERY BY FORMULA |
GOTO SELECTED RECORD | QUERY SELECTION |
GRAPH TABLE | QUERY SELECTION BY FORMULA |
IMPORT DIF | REDUCE SELECTION |
IMPORT SYLK | RELATE MANY |
IMPORT TEXT | REMOVE FROM SET |
INPUT FORM |
In compiled mode, it is easy to return the optional [Table] parameter. However, when the first parameter passed to one of these commands is a pointer, the compiler does not know to what the pointer is referring; the compiler treats it as a table pointer.
Take the case of the QUERY command whose syntax is as follows:
QUERY({table{;formula{;*}})
The first element of the formula parameter must be a field.
If you write :
QUERY(PtrField->=True)
the compiler will look for a symbol representing a field in the second element. When it finds the "=" sign, it will issue an error message, since it cannot identify the command with an expression that it knows how to process.
On the other hand, if you write:
QUERY(PtrTable->;PtrField->=True)
or
QUERY([Table];PtrField->=True)
you will avoid any possible ambiguity.
Constants
If you create your own 4DK# resources (constants), make sure that numerics are declared as Longints (L) or Reals (R) and character strings as Strings (S). Any other type will generate a warning.
See Also
Error messages, Optimization Hints, Typing Guide, Using Compiler Directives.