4D v16.3

Guide du typage

Accueil

 
4D v16.3
Guide du typage

Guide du typage  


 

 

Cette section décrit les principales causes de conflits de types sur les variables, ainsi que les manières de les éviter.

Les conflits de types simples peuvent se résumer comme suit :

  • conflit entre deux utilisations,
  • conflit entre une utilisation et une directive de compilation,
  • conflit par retypage implicite,
  • conflit entre deux directives de compilation.

Le conflit de types le plus simple est celui pour lequel un même nom de variable désigne deux objets différents. Imaginez que dans une application, vous écriviez :

 LaVariable:=5

et que, quelque part ailleurs, dans la même application, vous écriviez :

 LaVariable:=True

Vous générez un conflit de types. Le remède est simple : renommez l’une des deux variables.

Imaginez que dans une application, vous écriviez :

 LaVariable:=5

et que, quelque part ailleurs, dans la même application, vous écriviez :

 C_BOOLEAN(LaVariable)

Le compilateur, peignant d’abord les directives de compilation, fera de LaVariable un Booléen mais lorsqu’il découvrira :

 LaVariable:=5

il signalera un conflit de types. Ici encore, le remède est simple : renommez votre variable ou modifiez la directive de compilation.

L’utilisation de variables de types différents dans une expression génère des incohérences. Le compilateur signale très logiquement les incompatibilités. Prenons un exemple simple. Vous écrivez :

 vBooléen:=True `Le compilateur déduit que vBooléen est de type Booléen
 C_LONGINT(<>vEntier`Déclaration d’un Entier long par une directive de compilation
 <>vEntier:=3 `Commande compatible avec la directive de compilation
 LaVar:=<>vEntier+vBooléen `Opération contenant des variables dont les types sont incompatibles

Certaines fonctions renvoient des variables d’un type bien précis. L’affectation du résultat d’une de ces variables à une variable déjà typée différemment provoquera un conflit de types si vous ne faites pas attention.
Dans une application interprétée, vous pouvez écrire :

 No_Ident:=Request("Numéro d’identification") `No_Ident est de type Texte
 If(Ok=1)
    No_Ident:=Num(No_Ident) `No_Ident est de type Numérique
    QUERY([Personnes]Id=No_Ident)
 End if

Vous générez ici un conflit de type sur la troisième ligne. Le remède consiste à contrôler le comportement de la variable. Dans certains cas, vous aurez à créer des variables intermédiaires d’un nom différent. Dans d’autres cas, comme celui-ci en particulier, vous pouvez structurer différemment votre méthode :

 No_Ident:=Num(Request("Numéro d’identification")) `No_Ident est de type Numérique
 If(Ok=1)
    QUERY([Personnes]Id=No_Ident)
 End if

Déclarer deux fois la même variable par deux directives de compilation différentes constitue, bien sûr, un retypage. Si, dans la même base, vous écrivez :

 C_BOOLEAN(LaVariable)
 C_TEXT(LaVariable)

le compilateur est confronté à un dilemme et vous demande quelles étaient vos intentions. Le remède est simple : renommez l’une des deux variables.

Les conflits de types pour les variables locales sont absolument identiques aux conflits de types pour les variables process et interprocess, à ceci près que ces conflits se déroulent dans un espace plus restreint.
Les conflits se jouent au niveau général de la base pour les variables process et interprocess. Les conflits se jouent au niveau particulier de la méthode pour les variables locales. Vous ne pouvez donc pas écrire dans la même méthode :

 $Temp:="Bonjour"

et puis plus loin

 $Temp:=5

En revanche, vous pouvez écrire dans une méthode M1 :

 $Temp:="Bonjour"

et dans une méthode M2 :

 $Temp:=5

Les conflits possibles pour un tableau ne portent jamais sur la taille du tableau. En mode compilé comme en mode interprété, les tableaux sont gérés dynamiquement. La taille d’un tableau peut varier au fil des méthodes et vous n’avez pas, bien sûr, à déclarer une taille maximale pour un tableau.
Vous pouvez, en conséquence, dimensionner un tableau à zéro, ajouter ou retirer des éléments, en effacer le contenu. Dans l’optique de la compilation, et ce, dans une même méthode, pour un tableau local ou dans toute la base pour un tableau process ou interprocess, vous devez veiller aux points suivants :

  • ne pas changer le type des éléments du tableau,
  • ne pas changer le nombre de dimensions d’un tableau,
  • dans le cas des tableaux Alpha, ne pas changer la longueur des chaînes de caractères.

Si vous déclarez un tableau comme étant un tableau d’Entiers, il doit rester un tableau d’Entiers pour toute la base. Il ne pourra jamais contenir, par exemple, des éléments de type Booléen.
Si, dans une application, vous écrivez :

 ARRAY INTEGER(LeTableau;5)
 ARRAY BOOLEAN(LeTableau;5)

le compilateur ne peut identifier pour vous le type de LeTableau.
Renommez simplement l’un des deux tableaux.

En version interprétée, il peut vous arriver de changer le nombre de dimensions d’un tableau.
Lorsque le compilateur établit sa table des symboles, il gère différemment les tableaux à une dimension et les tableaux à deux dimensions.
En conséquence, un tableau déclaré une fois comme étant un tableau à une dimension ne peut être re-déclaré ou utilisé comme un tableau à deux dimensions et vice versa.
Dans une même base, vous ne pouvez donc pas avoir :

 ARRAY INTEGER(LeTableau1;10)
 ARRAY INTEGER(LeTableau1;10;10)

En revanche, vous pouvez, évidemment, avoir dans la même application

 ARRAY INTEGER(LeTableau1;10)
 ARRAY INTEGER(LeTableau2;10;10)

Par ailleurs, vous pouvez parfaitement écrire :

 ARRAY BOOLEAN(LeTableau;5)
 ARRAY BOOLEAN(LeTableau;10)

Comme vous pouvez le noter dans nos exemples, c’est le nombre de dimensions d’un tableau qu’on ne peut changer en cours d’application et non la valeur des dimensions du tableau.

Note : Un tableau à deux dimensions est en fait un ensemble de plusieurs tableaux à une dimension. Pour plus de précisions, reportez-vous à la section Tableaux à deux dimensions.

Lors de l’utilisation des commandes COPY ARRAY, LIST TO ARRAY, ARRAY TO LIST, SELECTION TO ARRAY, SELECTION RANGE TO ARRAY, ARRAY TO SELECTION, ou DISTINCT VALUES, vous pouvez, volontairement ou involontairement, être conduit à des changements de type d’éléments ou de nombre de dimensions. Vous vous retrouverez donc dans un des cas cités précédemment.
Le compilateur vous délivrera un message d’erreur et la correction que vous aurez à faire sera en général évidente. Des exemples de retypage implicite de tableaux sont fournis dans la section Précisions de syntaxe.

Si vous souhaitez compiler une base de données qui utilise des tableaux locaux (tableaux visibles uniquement par les méthodes qui les ont créés), il est nécessaire de les déclarer explicitement dans 4D avant de les utiliser.
La déclaration explicite d’un tableau signifie l’utilisation d’une commande de type ARRAY REAL, ARRAY INTEGER, etc.
Par exemple, si une méthode génère un tableau local d’entiers contenant 10 valeurs, vous devez écrire au préalable la ligne suivante :

 ARRAY INTEGER($MonTableau;10)

Les variables dessinées dans un formulaire, qu’il s’agisse d’une case à cocher ou d’une zone externe, sont toutes des variables soit process, soit interprocess.
En mode interprété, la question des types de ces variables ne se pose pas dans la pratique. Elle peut se poser, en revanche, dans l’optique d’une application compilée. Les règles du jeu sont cependant transparentes :

  • soit vous avez typé votre variable,
  • soit le compilateur lui attribue un type par défaut qui peut être défini dans les Préférences de compilation (voir le manuel Mode Développement).

Les variables suivantes sont considérées comme des Numériques par défaut :

Case à cocher
Case a cocher 3D
Bouton
Bouton inversé
Bouton invisible
Bouton 3D
Bouton image
Grille de boutons
Bouton radio
Bouton radio 3D
Radio image
Menu image
Menu déroulant hiérarchique
Liste hiérarchique
Règle
Cadran
Thermomètre
List box (type sélection)

Note : Les variables de type Règle, Cadran et Thermomètre seront toujours typées comme des Numériques, même si vous avez choisi l’option Entier long par défaut pour le type des boutons dans les Préférences.

Pour ces variables, vous ne pouvez jamais vous trouver dans un conflit de types puisqu’elles ne peuvent avoir d’autres types que ce type-là, qu’on soit en interprété ou en compilé.
Les seuls conflits de types possibles sur l’une de ces variables viendraient du fait que le nom d’une variable serait le même que celui d’une autre variable placée à un autre endroit dans l’application. Dans ce cas, le remède consiste à renommer cette deuxième variable.

Une zone externe est toujours un Entier long. Il ne peut jamais y avoir de conflit de types.
Les seuls conflits de types possibles sur une variable Zone externe viendraient du fait que le nom de cette variable serait le même que celui d’une autre variable placée à un autre endroit dans l’application. Dans ce cas, le remède consiste à renommer cette deuxième variable.

Les zones 4D Write Pro sont toujours des variables de type objet. Il ne peut pas y avoir de conflit de type, hormis si le même nom de variable a été utilisé dans un autre endroit de l'application.

Ces variables sont les suivantes :

Variable non-saisissable,
Variable saisissable,
Liste déroulante,
Menu/liste déroulante,
Zone de défilement,
Combo box,
Pop-up Menu,
Onglet,
Zone Web
Colonne de list box (type tableau).

Ces variables se divisent en deux catégories :
• les variables simples (variables saisissables et variables non-saisissables),
• les variables d’affichage de tableaux (listes déroulantes, menus/listes déroulantes, zone de défilement, pop-up menu, combo box, onglets, colonnes de list box).

  • Variables simples
    Par défaut, ces variables reçoivent le type Texte. Si elles sont utilisées dans une méthode objet ou une méthode formulaire, c’est le type que vous avez choisi qui leur sera attribué.
    Vous n’avez aucun risque de conflit de types autre que celui qui serait généré par une attribution préalable du même nom à une autre variable.
  • Variables d’affichage de tableaux
    De nombreuses variables vous servent à afficher des tableaux dans les formulaires. Si les valeurs par défaut ont été saisies au niveau des contrôles de saisie des variables en mode Développement, il est conseillé de typer les variables correspondantes explicitement par une déclaration de type tableau (ARRAY BOOLEAN, ARRAY TEXT...).

Chaque list box ajoute plusieurs variables dans les formulaires. Le type par défaut des variables dépend du type de list box :

List box type tableauList box type sélection
List boxTableau booléenNumérique (non utilisé)
Colonne de list boxTableau texteTypage dynamique
En-têteNumériqueNumérique
PiedTypage dynamiqueTypage dynamique
Tableau de contrôle des lignesTableau booléen (Tableau Entier long accepté)-
StylesTableau Entier longEntier long
Couleurs de policeTableau Entier longEntier long
Couleurs de fondTableau Entier longEntier long

Veillez à identifier et typer correctement ces variables et tableaux pour ne pas générer de conflit à la compilation. 

Si vous utilisez des pointeurs dans vos applications, vous avez pu profiter de la puissance et de la flexibilité de cet outil dans 4D. Le compilateur en conserve intégralement les avantages.
Un pointeur peut pointer indifféremment sur une table, une variable ou un champ. Un même pointeur peut pointer sur des variables de type différent : veillez à ne pas générer des conflits artificiellement, en attribuant à une même variable des types différents.
Faites simplement attention à ne pas changer en cours de route le type de la variable sur laquelle pointe le pointeur en faisant, par exemple, une manipulation du type suivant :

 LaVariable:=5,3
 LePointeur:=->LaVariable
 LePointeur->:=6,4
 LePointeur->:=False

Dans ce cas de figure, votre pointeur dépointé est une variable Numérique. En affectant à cette variable une valeur booléenne, vous provoquez un conflit de types.

Si vous avez besoin dans une même méthode d’utiliser les pointeurs pour des propos différents, prenez soin de définir votre pointeur :

 LaVariable:=5,3
 LePointeur:=->LaVariable
 LePointeur->:=6,4
 LeBool:=True
 LePointeur:=->LeBool
 LePointeur->:=False

Un pointeur n’a aucune existence propre. Il est toujours défini en fonction de l’objet qu’il représente. C’est pourquoi le compilateur ne peut pas détecter des conflits de types générés par une utilisation sans contrôle des pointeurs. Vous n’aurez donc pas, en cas de conflit, de message d’erreur durant la phase de typage ou celle de compilation.
Cela ne veut pas dire que lorsque vous utilisez des pointeurs, vous travaillez sans filet. Le compilateur peut vérifier vos utilisations de pointeurs lorsque vous cochez l’option Contrôle d’exécution dans les Préférences de compilation (cf. manuel Mode Développement).

Le compilateur a besoin, au moment de la compilation, des définitions des commandes des plug-ins utilisées dans la base à compiler, c’est-à-dire du nombre et du type des paramètres de ces commandes. Les risques d’erreur de typage sont inexistants à partir du moment où le compilateur trouve effectivement dans l’application ce que vous avez déclaré.

Assurez-vous que vos plug-ins sont installés dans le dossier PlugIns, à l'un des emplacements autorisés par 4D : à côté du fichier de structure de la base ou à côté de l’application exécutable (Windows) / dans le progiciel (Mac OS). Pour des raisons de compatibilité, il reste possible d'utiliser un dossier Win4DX ou Mac4DX à côté du fichier de structure. Pour plus d'informations, reportez-vous au Guide d'installation de 4D.
Le compilateur ne duplique pas le fichier mais tient compte des déclarations des commandes sans rien vous demander en supplément.
Si vos plug-ins sont placés ailleurs, le compilateur vous demande de les localiser lors du typage, via une boîte de dialogue d’ouverture de documents.

Certains plug-ins, par exemple 4D Write, utilisent des commandes qui provoquent l’appel implicite à des commandes 4D.
Prenons un exemple avec 4D Write. Vous disposez d’une commande appelée WR ON EVENT. La syntaxe de cette commande est :
WR ON EVENT(LaZone;Evénement;MéthodeEvénement)

Le dernier paramètre que vous passez à cette routine est le nom d’une méthode, que vous aurez vous-même écrite dans 4D. Cette méthode sera appelée par 4D Write chaque fois que l’événement attendu sera reçu et cette méthode recevra automatiquement les paramètres suivants :

ParamètresTypeDescription
$0Entier longRetour de fonction
$1Entier longZone 4D Write
$2Entier longTouche Maj.
$3Entier longTouche Alt (Windows), Option (Mac OS)
$4Entier longTouche Ctrl (Windows), Commande (Mac OS)
$5Entier longType d’événement qui a provoqué l’appel
$6 Entier longValeur variant en fonction du paramètre Evénement

Afin que le compilateur connaisse l’existence de ces paramètres et les prenne en compte, vous devez vous assurer qu’ils peuvent effectivement être typés, soit par une directive de compilation, soit parce que leur utilisation, suffisamment explicite, permet de déduire leur type.

4D permet de créer et de manipuler des composants. Un composant 4D est un ensemble d’objets 4D représentant une ou plusieurs fonctionnalité(s) groupée(s) dans un fichier de structure (appelé base matrice), qu’il est possible d’installer dans différentes bases (appelées bases hôtes).
Une base hôte exécutée en mode interprété peut utiliser indifféremment des composants interprétés ou compilés. Il est possible d’installer des composants interprétés et compilés dans la même base hôte. En revanche, une base hôte exécutée en mode compilé ne peut pas utiliser de composant interprété. Dans ce cas, seuls des composants compilés peuvent être employés.
Une base hôte interprétée contenant des composants interprétés peut être compilée si elle ne fait pas appel à des méthodes du composant interprété. Dans le cas contraire, une boîte de dialogue d’alerte apparaît lorsque vous tentez de compiler l'application et la compilation est impossible.

Un conflit de nom peut se produire lorsqu’une méthode projet partagée du composant a le même nom qu’une méthode projet de la base hôte. Dans ce cas, lorsque du code est exécuté dans le contexte de la base hôte, c’est la méthode de la base hôte qui est appelée. Ce principe permet de “masquer” une méthode du composant avec une méthode personnalisée (par exemple pour obtenir une fonctionnalité différente). Lorsque le code est exécuté dans le contexte du composant, c’est la méthode du composant qui est appelée. Un masquage est signalé par un warning lors de la compilation de la base hôte.

Si deux composants partagent des méthodes du même nom, une erreur est générée au moment de la compilation de la base hôte.

Pour plus d'informations sur les composants, reportez-vous au manuel Mode Développement.

Les manipulations des variables locales suivent toutes les règles déjà énoncées. De même que toutes les autres variables, elles ne peuvent être retypées en cours de méthode.
Dans ce paragraphe, nous abordons deux cas de figure où l’inattention pourrait conduire à des conflits de types :

  • Lorsque vous avez en fait besoin d’un retypage. L’utilisation de pointeurs vous permet alors d’éviter les conflits de types.
  • Lorsque vous avez besoin d’adresser des paramètres par indirection.

Il n’est pas possible de retyper une variable. Il est, en revanche, tout à fait possible de faire pointer un pointeur successivement sur des variables de type différent.
Un exemple nous permet d’illustrer ce propos : écrivons une fonction qui nous renvoie l’occupation mémoire d’un tableau à une dimension suivant son type. Le résultat est un numérique dans tous les cas sauf deux : dans le cas des tableaux Texte et des tableaux Image, la taille mémoire occupée par le tableau dépend de valeurs inexprimables sous forme numérique (cf. section Tableaux et mémoire).
Dans le cas des tableaux Texte et des tableaux Image, nous renverrons comme résultat une chaîne de caractères. Cette fonction requiert un paramètre : un pointeur sur le tableau dont on veut connaître l’occupation mémoire.
Pour effectuer cette opération, vous avez le choix entre deux méthodes :

  • ne travailler qu’avec des variables locales et ne pas vous soucier de leur type — mais dans ce cas, la méthode ne fonctionnera qu’en mode interprété.
  • utiliser des pointeurs et alors travailler indifféremment en interprété et en compilé.
Fonction OccupMém en interprété seulement (exemple pour Macintosh)
 $Taille:=Size of array($1->)
 $Type:=Type($1->)
 Case of
    :($Type=Real array)
       $0:=8+($Taille*10) ` $0 est un Numérique
    :($Type=Integer array)
       $0:=8+($Taille*2)
    :($Type=LongInt array)
       $0:=8+($Taille*4)
    :($Type=Date array)
       $0:=8+($Taille*6)
    :($Type=Text array)
       $0:=String(8+($Taille*4))+("+Somme des longueurs des textes") ` $0 est un Texte
    :($Type=Picture array)
       $0:=String(8+($Taille*4))+("+Somme des tailles des images") ` $0 est un Texte
    :($Type=Pointer array)
       $0:=8+($Taille*16)
    :($Type=Boolean array)
       $0:=8+($Taille/8)
 End case

Dans la méthode ci-dessus, il y a changement de type de $0 suivant les valeurs de $1.

Fonction OccupMém en mode interprété et compilé (exemple pour Macintosh)
Ecrivons maintenant cette méthode en utilisant un pointeur :
 $Taille:=Size of array($1->)
 $Type:=Type($1->)
 VarNum:=0
 Case of
    :($Type=Real array)
       VarNum:=8+($Taille*10) ` VarNum est un Numérique
    :($Type=Integer array)
       VarNum:=8+($Taille*2)
    :($Type=LongInt array)
       VarNum:=8+($Taille*4)
    :($Type=Date array)
       VarNum:=8+($Taille*6)
    :($Type=Text array)
       VarText:=String(8+($Taille*4))+("+Somme des longueurs des textes")
    :($Type=Picture array)
       VarText:=String(8+($Taille*4))+("+Somme des tailles des images")
    :($Type=Pointer array)
       VarNum:=8+($Taille*16)
    :($Type=Boolean array)
       VarNum:=8+($Taille/8)
 End case
 If(VarNum#0)
    $0:=->VarNum
 Else
    $0:=->VarText
 End if

Il faut noter la différence entre ces deux séquences :

  • dans le premier cas, le résultat de la fonction est la variable que l’on attendait,
  • dans le second cas, le résultat de la fonction est un pointeur sur cette variable. Il vous appartient alors de simplement dépointer le résultat reçu.

Le compilateur gère la puissance et la souplesse de l’indirection sur les paramètres. En mode interprété, 4D vous donne toute latitude concernant le nombre et le type des paramètres. Vous gardez en mode compilé cette même liberté à condition de ne pas introduire de conflit de types et de ne pas utiliser, dans la méthode appelée, plus de paramètres que vous en avez passés, ce qui est facile.
Afin de contourner un éventuel conflit, les paramètres adressés par indirection doivent tous être du même type.
Pour une bonne gestion de cette indirection, il est important de respecter la convention suivante : si tous les paramètres ne sont pas adressés par indirection, ce qui est le cas le plus fréquent, il faut que les paramètres adressés par indirection soient passés en fin de liste.
A l’intérieur de la méthode, l’adressage par indirection se fait sous la forme : ${$i}, $i étant une variable numérique. ${$i} est appelé paramètre générique.

Illustrons notre propos par un exemple : écrivons une fonction qui prend des valeurs, fait leur somme et renvoie cette somme formatée suivant un format qui peut varier avec les valeurs.
A chaque appel à cette méthode, le nombre de valeurs à additionner peut varier. Il faudra donc passer comme paramètre à notre méthode les valeurs, en nombre variable, et le format, exprimé sous forme d’une chaîne de caractères.
Un appel à cette fonction se fera de la façon suivante :

 Résultat:=LaSomme("##0,00";125,2;33,5;24)

La méthode appelante récupérera dans ce cas la chaîne : 182,70, somme des nombres passés, formatée suivant le format spécifié. D’après ce que l’on a vu plus haut, les paramètres de la fonction doivent être passés dans un ordre précis : le format d’abord et ensuite les valeurs, dont le nombre peut varier d’un appel à l’autre.

Examinons maintenant la fonction que nous appelons LaSomme :

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

Cette fonction pourra être appelée de diverses manières :

 Résultat:=LaSomme("##0,00";125,2;33,5;24)
 Résultat:=LaSomme("000";1;18;4;23;17)

De même que pour les autres variables locales, la déclaration du paramètre générique par directive de compilation n’est pas obligatoire. Si elle est nécessaire (cas d’ambiguïté ou d’optimisation), elle se fait avec la syntaxe suivante :

 C_LONGINT(${4})

La commande ci-dessus signifie que tous les paramètres à partir du quatrième (inclus) seront adressés par indirection. Ils seront tous de type Entier long. Les types de $1, $2 et $3 pourront être quelconques. En revanche, si vous utilisez $2 par indirection, le type utilisé sera le type générique. Il sera donc de type Entier long, même si pour vous, par exemple, il était de type Réel.

Note : Le compilateur utilisant cette commande durant le typage, le nombre, dans la déclaration, doit toujours être une constante et jamais une variable.

Des variables et des constantes de 4D ont un type et une identité fixés par le compilateur. On ne peut donc créer une nouvelle variable, une méthode, une fonction ou une commande de plug-in portant le nom d’une de ces variables ou d’une de ces constantes. Bien entendu, vous pouvez tester leur valeur et les utiliser comme auparavant.

Voici la liste complète des Variables système de 4D accompagnées de leur type.

VariableType
OKEntier long
DocumentTexte
FldDelimitEntier long
RecDelimitEntier long
ErrorEntier long
Error methodTexte
Error lineEntier long
Error formulaTexte
MouseDownEntier long
KeyCodeEntier long
ModifiersEntier long
MouseXEntier long
MouseYEntier long
MouseProcEntier long

Lorsqu’on crée une colonne calculée dans un état, 4D crée automatiquement une variable C1 pour la première, C2, C3... pour les autres. Généralement, cette création est faite de façon transparente pour vous.
Dans le cas où vous utiliseriez ces variables dans des méthodes, souvenez-vous que, comme les autres variables, les variables C1, C2... ne peuvent être retypées.

La liste des constantes prédéfinies de 4D peut être consultée dans ce manuel via la Liste des thèmes de constantes, vous pouvez également les visualiser dans l’Explorateur, en mode Développement.



Voir aussi  

Conseils d’optimisation
Messages d'erreurs
Précisions de syntaxe
Utilisation des directives de compilation
Variables système

 
PROPRIÉTÉS 

Produit : 4D
Thème : Compilateur

 
HISTORIQUE 

Modifié : 4D v15 R4

 
UTILISATION DE L'ARTICLE

4D - Langage ( 4D v16)
4D - Langage ( 4D v16.1)
4D - Langage ( 4D v16.2)
4D - Langage ( 4D v16.3)