Typing Guide

4D - Documentation   Français   English   German   4th Dimension 2004, Command Theme List   4th Dimension 2004, Command Alphabetical List   4th Dimension 2004, Constant Theme List   Back   Previous   Next

version 2004 (Modified)


This section describes the main causes of typing conflicts on variables, as well as ways to avoid them.

Conflicts on simple variables


Simple data type conflicts can be summarized as follows:

conflict between two uses,

conflict between use and a compiler directive,

conflict resulting from implicit retyping,

conflict between two compiler directives.

Conflicts between two uses

The simplest data type conflict is one that stems from a single variable name designating two different objects. Suppose that, in an application, you write:

   Variable:=5

and that elsewhere, in the same application, you write:

   Variable:=True

This generates a data type conflict. The problem can be solved by renaming one of the variables.

Conflict between use and a compiler directive

Suppose that, in an application, you write:

   Variable:=5

and that elsewhere, in the same application, you write:

   C_BOOLEAN(Variable)

Since the compiler scans the directives first, it will type Variable as Boolean, but when it finds the statement:

   Variable:=5

it detects a data type conflict. You can solve the problem by renaming your variable or modifying the compiler directive.

Using variables of different data types in one expression creates inconsistencies. The compiler points out incompatibilities. Here is a simple example:

   vBool:=True    `The compiler infers that vBoolean is data type Boolean
   C_INTEGER(<>vInteger)   `Declaration of an Integer by a compiler directive
   <>vInteger:=3   `Command compatible with the compiler directive
   Var:= <>vInteger+vBool   `Operation containing variables with incompatible data types

Conflict stemming from implicit retyping

Some functions return variables of a very precise data type. Assigning the result of one of such variables to a variable already typed differently will cause a data type conflict if you are not careful.

For example, in an interpreted application, you can write:

   IdentNo:=Request("Identification Number")   `IdentNo is data type Text
   If(Ok=1)
      IdentNo:=Num(IdentNo)   `IdentNo is data type Real
      QUERY([Contacts]Id=IdentNo)
   End if

In this example, you create a type conflict in the third line. The solution consists in controlling the behavior of the variable. In some cases, you must create an intermediate variable that uses a different name. In other cases, such as this, your method can be structured differently:

   IdentNo:=Num(Request("Identification Number"))  `IdentNo is data type Real
   If(Ok=1)
      QUERY([Contacts]Id=IdentNo)
   End if

Conflict between two compiler directives

Declaring the same variable through two conflicting compiler directives constitutes a retyping. If, in the same database, you write:

   C_BOOLEAN(Variable)
   C_TEXT(Variable)

the compiler detects the conflict and reports an error in the error file. Typically, you can solve the problem by renaming one of the variables.

Keep in mind that a data type conflict can arise concerning the use of C_STRING if you modify the maximum string length. Thus, if you write:

   C_STRING(5;MyString)
   MyString:="Hello"
   C_STRING(7;MyString)
   MyString:="Flowers"

the compiler identifies a conflict because it must provide an adequately-sized location when declaring String variables.

The solution is to use a compiler directive that gives the maximum length, since, by default, the compiler will accept a shorter length. You can write:

   C_STRING(7;String)
   String:="Flowers"
   String:="Hello"

Note: If you have written C_STRING(7;String) twice, i.e.:

   C_STRING(7;String)
   String:="Flowers"
   C_STRING(7;String)
   String:="Hello"

the compiler will nevertheless accept the directives; the second directive is simply redundant.

Note concerning local variables

Data type conflicts involving local variables are identical to those in process or interprocess variables. The only difference is that there must be consistency only within a specified method.

For process and interprocess variables, conflicts occur at the general leve of teh database. For local variables, conflicts occur at the level of the method. For example, you cannot write in the same method:

   $Temp:="Flowers" 

and then

   $Temp:=5

However, you can write:

   $Temp:="Flowers"

in method M1, and:

   $Temp:=5

in method M2, because the scope of local variables is the method itself and not the entire database.

Conflicts in arrays


Conflicts concerning an array are never size-related. As in uncompiled databases, arrays are managed dynamically. The size of an array can vary throughout methods, and you do not have to declare a maximum size for an array.

Therefore, you can size an array to null, add or remove elements, or delete the contents.

You should follow these guidelines when writing a database intended for compilation:

Do not change data types of array elements,

Do not change the number of dimensions of an array,

For a String array, do not change character-string length.

Changing data types of array elements

If you declare an array as an Integer array, it must remain an integer array throughout the database. It can never contain, for example, Boolean type elements.

If you write:

   ARRAY INTEGER(MyArray;5)
   ARRAY BOOLEAN(MyArray;5)

the compiler cannot type MyArray.

Just rename one of the arrays.

Changing the number of dimensions of an array

In an uncompiled database, you can change the number of dimensions in an array. When the compiler sets up the symbol table, one-dimensional arrays and two-dimensional arrays are managed differently.

Consequently, you cannot redeclare a one-dimensional array as two-dimensional, or vice versa.

Therefore, in the same database, you cannot have:

   ARRAY INTEGER(MyArray1;10)
   ARRAY INTEGER(MyArray1;10;10)

However, you can write the following statements in the same application:

   ARRAY INTEGER(MyArray1;10)
   ARRAY INTEGER(MyArray2;10;10)

The number of dimensions in an array cannot be changed in a database. However, you can change the size of an array. You can resize one array of a two-dimensional array and write:

   ARRAY BOOLEAN(MyArray;5)
   ARRAY BOOLEAN(MyArray;10)

Note: A two-dimensional array is, in fact, a set of several one-dimensional arrays. For more information, refer to the Two-dimensional Arrays section.

Case of fixed string arrays

String arrays follow the same rules as fixed strings, for the same reasons.

If you write:

   ARRAY STRING(5;MyArray;10)
   ARRAY STRING(10;MyArray;10)

the compiler detects a length conflict. The solution is simple: declare the maximum string length. The compiler automatically handles shorter length strings.

Implicit retyping

When using commands such as COPY ARRAY, LIST TO ARRAY, ARRAY TO LIST, SELECTION TO ARRAY, SELECTION RANGE TO ARRAY, ARRAY TO SELECTION, or DISTINCT VALUES, you may change, voluntarily or not, the data types of elements, the number of dimensions, or, in a String array, the string length. You will thus find yourself in one of the three situations previously mentioned.

The compiler generates an error message; the required correction is usually quite obvious. Examples of implicit array retyping are provided in the Syntax Details section.

Local arrays

If you want to compile a database that uses local arrays (arrays only visible by the methods that created them), you must explicitly declare them in 4th Dimension before using them.

Explicitly declaring an array means using a command of the type ARRAY REAL, ARRAY INTEGER, etc.

For example, if a method creates a local integer array of 10 elements, you should have the following line in your method:

   ARRAY INTEGER($MyArray;10)

Typing of variables created in forms


Variables created in a form (e.g., buttons, drop-down list boxes, and so forth) are always process or interprocess variables.

In an interpreted database, the data type of such variables is not important. However, in compiled applications, it may have to be taken into consideration. The rules are, nevertheless, quite clear:

You can type form variables using compiler directives, or

The compiler assigns it a default type that can be set in the compilation Preferences (see the Design Reference manual).

Variables considered by default as Real

The following form variables are typed as Real by default:

Check box

3D check box

Button

Highlight button

Invisible button

3D button

Picture button

Button grid

Radio button

3D radio button

Radio picture

Picture menu

Hierarchical pop-up menu

Hierarchical list

Ruler

Dial

Thermometer.

Note: The Ruler, Dial and Thermometer form variables are always typed as Reals, even if you choose Long integer as the Default Button Type in the Preferences.

For one of these variables, the only data type conflict that could arise would be if the name of a variable were identical to that of another one located elsewhere in the database. In this case, rename the second variable.

Graph variable

A graph area is automatically data type Graph (Longint). This variable never creates a data type conflict. For a Graph type variable, the only possible data type conflict that could arise would be if the name of a variable were identical to that of another one located elsewhere in the database. In this case, rename the second variable.

Plug-in area variable

A plug-in area is always a Longint. There can never be a data type conflict.

For a plug-in area, the only possible data type conflict that could arise would be if the name of a variable were identical to that of another one located elsewhere in the database. In this case, rename the second variable.

Variables considered by default as Text

These variables are of the following types:

Non-enterable variable,

Enterable variable,

Drop-down list,

Menu/drop-down list,

Scrollable area,

Combo box,

Pop-up Menu,

Tab control.

These variables are divided into two categories:

simple variables (enterable and non-enterable variables),

display variables (drop-down lists, menus/drop-down lists, scrollable areas, pop-up menus, combo boxes and tab controls).

Simple variables

Their default data type is Text. When used in methods or object methods, they are assigned the data type selected by you. There is no danger of conflict other than one resulting from assigning the same name to another variable.

Display variables

Some variables are used to display arrays in forms. If default values have been entered in the Form editor, you must explicitly declare the corresponding variables using the Array Declaration commands (ARRAY STRING, ARRAY TEXT...).

Pointers


When you use pointers in your database, you take advantage of a powerful and versatile 4th Dimension tool. The compiler preserves all the benefits of pointers.

A pointer can point to variables of different data types. Do not create a conflict by assigning different data types to a variable. Be careful not to change the data type of a variable to which a pointer refers.

Here is an example of this problem:

   Variable:=5.3
   Pointer:=-> Variable 
   Pointer->:=6.4
   Pointer->:=False

In this case, your dereferenced pointer is a Real variable. By assigning it a Boolean value, you create a data type conflict.

If you need to use pointers for different purposes in the same method, make sure that your pointers are defined:

   Variable:=5.3
   Pointer:=-> Variable 
   Pointer->:=6.4
   Bool:=True
   Pointer:=->Bool
   Pointer->:=False

A pointer is always defined in relation to the object to which it refers. That is why the compiler cannot detect data type conflicts created by pointers. In case of a conflict, you will get no error message while you are in the typing phase or in the compilation phase.

This does not mean that the compiler has no way to detect conflicts involving pointers. The compiler can verify your use of pointers when you check the Range Checking option in the compilation Preferences (see the Design Reference manual).

Plug-in Commands


General points

During compilation, the compiler analyzes the definitions of the plug-in commands used in the database, i.e. the number and type of parameters of these commands. There is no danger of confusion at the typing level if your calls are consistent with the declaration of the method.

Make sure that your plug-ins are installed in the PlugIns folder, in one of the locations authorized by 4th Dimension: next to the database structure file or next to the executable application (Windows) / in the software package (Mac OS). For compatibility reasons, it is still possible to use the Win4DX or Mac4DX folder next to the structure file. For more information, refer to the Installation Guide of 4th Dimension.

The compiler does not duplicate these files, but analyzes them to determine the proper declaration of their routines.

If your plug-ins are located elsewhere, the compiler will ask you to locate them during typing, via an Open file dialog box.

Plug-in commands receiving implicit parameters

Certain plug-ins, for example 4D Write, implement commands that implicitly call 4th Dimension commands.

Take the example of 4D Write. The syntax for the WR ON EVENT command is:

WR ON EVENT(area;event;eventMethod)

The last parameter is the name of the method that you have created in 4th Dimension. This method is called by 4D Write each time the event is received. It automatically receives the following parameters:

ParametersTypeDescription
$0LongintFunction return
$1Longint4D Write area
$2LongintShift key
$3LongintAlt key (Windows); Option key (Mac OS)
$4LongintCtrl key (Windows), Command key (Mac OS)
$5LongintType of event
$6 LongintValue depends on the Event parameter

For the compiler to take these parameters into account, you must make sure that they have been typed, either by a compiler directive, or by their usage in the method. If they have been used procedurally, the usage has to be explicit enough to be able to deduce the type clearly.

4D components


4th Dimension and 4D Insider allow creation and management of 4D components. 4D components can be compared to 4D object libraries, in which each object is assigned an attribute ("Private", "Protected" or "Public") to indicate if it will be visible, modifiable, etc. For more information about 4D components management, refer to 4D Insider documentation.

In principle, the 4D component developer should make sure that the component can be compiled and will not generate conflicts. However, this possibility can never be totally excluded. In case of a compilation error caused by an object belonging to a component, the compiler will display the following information, depending on the object attribute:

"Private": the compiler will not provide the name of the object concerned, but only the name of the 4D component.

"Protected" or "Public": the compiler will provide the object name, just as it would for any other database object (standard behavior).

Handling local variables $0…$N and parameter passing


The handling of local variables follows all the rules that have already been stated. As with all other variables, their data types cannot be altered while the method executes. In this section, we examine two instances that could lead to data type conflicts:

When you actually require retyping. The use of pointers helps avoid data type conflicts.

When you need to address parameters by indirection.

Using pointers to avoid retyping

A variable cannot be retyped. However, it is possible to use a pointer to refer to variables of different data types.

As an example, consider a function that returns the memory size of a one-dimensional array. In all but two cases, the result is a Real; for Text arrays and Picture arrays, the memory size depends on values that cannot be expressed numerically (see the Arrays and Memory section).

For Text and Picture arrays, the result is returned as a string of characters. This function requires a parameter: a pointer to the array whose memory size we want to know.

There are two methods to carry out this operation:

Work with local variables without worrying about their data types; in such case, the method runs only in interpreted mode.

Use pointers, and proceed in interpreted or in compiled mode.

MemSize function, only in interpreted mode (example for Macintosh)

   $Size:=Size of array($1->)
   $Type:=Type($1->)
   Case of
      :($Type=Real array)
         $0:=8+($Size*10)   ` $0 is a Real
      :($Type=Integer array)
         $0:=8+($Size*2)   
      :($Type=LongInt array)
         $0:=8+($Size*4)   
      :($Type=Date array)
         $0:=8+($Size*6)
      :($Type=Text array)
         $0:=String(8+($Size*4))+("+Sum of text lengths")   ` $0 is a Text
      :($Type=Picture array)
         $0:=String(8+($Size*4))+("+Sum of picture sizes")   ` $0 is a Text
      :($Type=Pointer array)
         $0:=8+($Size*16)   
      :($Type=Boolean array)
         $0:=8+($Size/8)   
   End case

In the above method, the data type of $0 changes according to the value of $1; therefore, it is not compatible with the compiler.

MemSize function in interpreted and compiled modes (example for Macintosh)

Here, the method is written using pointers:

   $Size:=Size of array($1->)
   $Type:=Type($1->)
   VarNum:=0
   Case of
      :($Type=Real array)
         VarNum:=8+($Size*10)   ` VarNum is a Real
      :($Type=Integer array)
         VarNum:=8+($Size*2)
      :($Type=LongInt array)
         VarNum:=8+($Size*4)
      :($Type=Date array)
         VarNum:=8+($Size*6)
      :($Type=Text array)
         VarText:=String(8+($Size*4))+("+Sum of text lengths")  
      :($Type=Picture array)
         VarText:=String(8+($Size*4))+("+Sum of picture sizes")
      :($Type=Pointer array)
         VarNum:=8+($Size*16)
      :($Type=Boolean array)
         VarNum:=8+($Size/8)
   End case
   If (VarNum#0)
      $0:=->VarNum
   Else
      $0:=->VarText
   End if

Here are the key differences between the two functions:

In the first case, the function's result is the expected variable,

In the second case, the function's result is a pointer to that variable. You simply dereference your result.

Parameter indirection

The compiler manages the power and versatility of parameter indirection. In interpreted mode, 4th Dimension gives you a free hand with numbers and data types of parameters. You retain this freedom in compiled mode, provided that you do not introduce data type conflicts and that you do not use more parameters than you passed in the calling method.

To prevent possible conflicts, parameters addressed by indirection must all be of the same data type.

This indirection is best managed if you respect the following convention: if only some of the parameters are addressed by indirection, they should be passed after the others.

Within the method, an indirection address is formatted: ${$i}, where $i is a numeric variable. ${$i} is called a generic parameter.

As an example, consider a function that adds values and returns the sum formatted according to a format that is passed as a parameter. Each time this method is called, the number of values to be added may vary. We must pass the values as parameters to the method and the format in the form of a character string. The number of values can vary from call to call.

This function is called in the following manner:

   Result:=MySum("##0.00";125,2;33,5;24)

In this case, the calling method will get the string "182.70", which is the sum of the numbers, formatted as specified. The function's parameters must be passed in the correct order: first the format and then the values.

Here is the function, named MySum:

   $Sum:=0
   For($i;2;Count parameters)
      $Sum:=$Sum+${$i}
   End for
   $0:=String($Sum;$1)

This function can now be called in various ways:

   Result:=MySum("##0.00";125,2;33,5;24)
   Result:=MySum("000";1;18;4;23;17)

As with other local variables, it is not necessary to declare generic parameters by compiler directive. When required (in cases of ambiguity or for optimization), it is done using the following syntax:

   C_INTEGER(${4})

This command means that all parameters starting from the fourth (included) will be addressed by indirection and will be of the data type Integer. $1, $2 and $3 can be of any data type. However, if you use $2 by indirection, the data type used will be the generic type. Thus, it will be of the data type Integer, even if for you it was, for instance, of the data type Real.

Note: The compiler uses this command in the typing phase. The number in the declaration has to be a constant and not a variable.

Reserved variables and constants


Some 4th Dimension variables and constants are assigned a data type and an identity by the compiler. Therefore, you cannot create a new variable, method, function or plug-in command using any of these variables or constant names. You can test their values and use them as you do in interpreted mode.

System variables

Here is a complete list of 4th Dimension System Variables with their data types.

VariableType
OKLongint
DocumentString (255)
FldDelimitLongint
RecDelimitLongint
ErrorLongint
MouseDownLongint
KeyCodeLongint
ModifiersLongint
MouseXLongint
MouseYLongint
MouseProcLongint

Quick report variables

When you create a calculated column in a report, 4th Dimension automatically creates a variable C1 for the first one, C2 for the second one, C3 and so forth. This is done transparently.

If you use these variables in methods, keep in mind that, like other variables, C1, C2, ... Cn cannot be retyped.

4D predefined constants

A complete list of the predefined constants in 4th Dimension can be found in this manual. 4D constants are also displayed in the Explorer, in Design mode.

See Also

Error messages, Optimization Hints, Syntax Details, Using Compiler Directives.


4D - Documentation   Français   English   German   4th Dimension 2004, Command Theme List   4th Dimension 2004, Command Alphabetical List   4th Dimension 2004, Constant Theme List   Back   Previous   Next