4D v16.3

Utilisation des directives de compilation

Accueil

 
4D v16.3
Utilisation des directives de compilation

Utilisation des directives de compilation  


 

4D utilise trois catégories de variables :

  • les variables locales,
  • les variables process,
  • les variables interprocess.

Pour plus d'informations sur ce point, reportez-vous à la section Variables. Les variables process et les variables interprocess sont structurellement de même nature pour le compilateur.

Toutes les variables ont un type. Comme décrit dans la section Types de données, vous disposez de différents types pour les variables simples :

Booléen
Date
Entier long
Graphe
Heure
Image
Numérique (ou Réel)
Pointeur
Texte
BLOB
Objet

Pour les variables de type Tableau, vous disposez des types suivants :

Tableau Booléen
Tableau Date
Tableau Entier
Tableau Entier long
Tableau Image
Tableau Numérique (ou Réel)
Tableau Heure
Tableau Objet
Tableau Pointeur
Tableau BLOB
Tableau Texte

Notes :
- Le type Objet est disponible depuis 4D v14.
- Les anciens types Alpha (chaîne de longueur fixe) et Entier ne sont plus utilisés pour les variables. Dans le code existant, ils sont automatiquement redirigés vers les types Texte et Entier long.

Lorsque vous travaillez avec 4D en mode interprété, une variable peut avoir plusieurs types. Cette tolérance se justifie parfaitement puisque vous êtes en mode interprété. En effet, à chaque ligne de code, 4D interprète l’instruction et comprend le contexte.
Lorsque vous travaillez en mode compilé, vous êtes dans une situation différente. Alors que l’interprétation agit ligne par ligne, la compilation s’intéresse à une base dans sa globalité.

La manière d’opérer du compilateur est la suivante :

  • Le programme explore systématiquement les objets qui lui sont proposés par 4D.
    Ces objets sont les méthodes base, les méthodes projet, les méthodes formulaire, les méthodes table (triggers) et les méthodes objet.
  • Le programme peigne ces objets pour retrouver le type de chacune des variables utilisées dans la base et génère la table des variables et des méthodes.
  • Une fois qu’il a retrouvé le type de toutes les variables, le compilateur traduit la base. Encore faut-il qu’il puisse identifier un type d’une manière univoque pour chacune des variables.

Si le compilateur trouve un même nom de variable avec deux types différents, il n’a aucune raison de privilégier l’un par rapport à l’autre. En d’autres termes, avant de pouvoir ranger un objet, de lui donner une adresse mémoire, le compilateur a besoin de connaître l’identité exacte de cet objet, c’est-à-dire son nom et son type, le type permettant au compilateur de déduire sa taille. Ainsi, pour chaque application compilée, le compilateur crée un plan, contenant, pour chaque variable, son nom (ou identificateur), son emplacement (ou adresse mémoire) et la taille qu’elle occupe (représentée par son type). Ce “plan” s’appelle la table de symboles. Une option des Préférences vous permet de générer ou non cette table sous forme de fichier lors de la compilation.
Ce plan est également utilisé pour la génération automatique des méthodes compilateur.

Si vous voulez que le compilateur vérifie votre typage ou bien s’en charge lui-même, placer une directive de compilation est simple. Vous avez le choix entre deux possibilités, qui correspondent d’ailleurs à deux méthodes de travail :

  • ou bien vous écrivez cette directive lors de la première utilisation de la variable, qu’il s’agisse d’une variable locale, process ou interprocess. La seule recommandation en la matière est que vous l’inscriviez bien à la première utilisation de la variable dans la première méthode exécutée. Attention, lors de la compilation le compilateur prend les méthodes dans l’ordre de leur création, et non dans l’ordre dans lequel elles apparaissent dans l’Explorateur.
  • ou bien, si vous êtes systématique, regroupez toutes vos variables process ou interprocess avec les directives afférentes dans la On Startup database method ou une méthode appelée par la On Startup database method.
    Pour les variables locales, regroupez ces directives en tête de la méthode où elles sont utilisées.

Au moment de leur typage via une directive de compilation, les variables reçoivent une valeur par défaut, qu'elles conserveront au cours de la session tant qu'elles n'auront pas été affectées. 

La valeur par défaut dépend du type et de la catégorie de la variable, du contexte d'exécution (interprété ou compilé), ainsi que, pour le mode compilé, des options de compilation définies dans la Page Compilateur des Propriétés de la base : 

  • Les variables process et interprocess sont toujours positionnées "à zéro" (qui signifie selon les cas 0, chaîne vide, blob vide, pointeur nil, date 00-00-00…)
  • Les variables locales sont positionnées :
    • en mode interprété : à zéro
    • en mode compilé, dépendant de l'option Initialiser les variables locales des Propriétés de la base :
      • à zéro lorsque "à zéro" est sélectionné,
      • à une valeur arbitraire fixe lorsque "à une valeur aberrante" est sélectionné (0x72677267 pour les numériques et les heures, toujours vrai pour les booléens), équivalent de "à zéro" pour les autres,
      • à une valeur aléatoire (pour les numériques) lorsque "non" est sélectionné. 

Le tableau suivant illustre ces valeurs par défaut :

TypeInterprocessProcessLocal interprétéLocal compilé "à zéro"Local compilé "aberrant"Local compilé "non"
BooléenFalseFalseFalseFalseTrueTrue (varie)
Date00-00-0000-00-0000-00-0000-00-0000-00-0000-00-00
Entier long00001919382119909540880 (varie)
Graphe00001919382119775946656 (varie)
Heure00:00:0000:00:0000:00:0000:00:00533161:41:59249345:34:24 (varie)
Imagepicture size=0picture size=0picture size=0picture size=0picture size=0picture size=0
Réel00001.250753659382e+2431.972748538022e-217 (varie)
PointeurNil=trueNil=trueNil=trueNil=trueNil=trueNil=true
Texte""""""""""""
BlobBlob size=0Blob size=0Blob size=0Blob size=0Blob size=0Blob size=0
Objetundefinedundefinedundefinedundefinedundefinedundefined

Les directives de compilation sont utiles dans deux cas :

  • lorsque le compilateur ne peut pas déduire seul le type d’une variable,
  • lorsque vous voulez éviter que le compilateur fasse des déductions.

Par ailleurs, utiliser des directives de compilation peut vous permettre de réduire le temps de compilation.

Il arrive que le compilateur ne puisse pas déduire le type d’une variable, et cela pour plusieurs raisons. Il est impossible de recenser tous les cas de figure. Une chose est certaine, c’est qu’en cas d’impossibilité à compiler, le compilateur vous en donnera la raison précise ainsi que les moyens d’y remédier.
On peut cependant distinguer trois causes majeures d’hésitation pour le compilateur : l’ambiguïté proprement dite, l’ambiguïté sur une déduction forcée, et l’impossibilité totale de déduire un type.

L’ambiguïté proprement dite
L’ambiguïté sur le nom de la variable est généré dans le cas suivant : le compilateur choisit la première variable qu’il rencontre et assigne arbitrairement à la suivante, de même nom mais de type différent, le type qu’il a précédemment attribué. Prenons un exemple simple :
dans une méthode A,

 LaVariable:=True
dans une méthode B,
 LaVariable:="La lune est verte"

Dans le cas où la méthode A est compilée avant la méthode B, le compilateur considérera que LaVariable:="La lune est verte" est un changement de type d’une variable précédemment rencontrée. Il vous signalera qu’il y a retypage. Il génère une erreur qu’il vous appartient de corriger. 

Ne vous inquiétez pas, le compilateur ne transformera pas votre variable LaVariable:="La lune est verte" en variable booléenne sans vous demander ce que vous en pensez !

L’ambiguïté sur une déduction forcée 
Il peut arriver que le compilateur déduise un type sur un objet qui ne convient pas à son utilisation finale. Dans ce cas, vous devrez typer explicitement vos variables à l’aide des directives de compilation. Voici un exemple de cas d’ambiguïté lors de l’utilisation des listes de valeurs par défaut sur un objet : dans les formulaires, il est possible de spécifier une liste de valeurs par défaut pour les objets de type combo box, pop up menu, menu/liste déroulante, onglet, zone de défilement et liste déroulante à l’aide du bouton d’édition Valeurs (voir à ce sujet le manuel Mode Développement de 4D). Les valeurs par défaut sont automatiquement chargées dans un tableau dont le nom est le même que celui de l’objet. 

Dans le cas simple où l’objet n’est pas utilisé dans une méthode, le compilateur peut déduire son type sans ambiguïté et l’objet sera typé en tableau texte par défaut.  En revanche, dans le cas où vous devez initialiser la position de votre tableau pour son affichage dans le formulaire, vous pouvez écrire les instructions suivantes dans la méthode formulaire :

 Case of
    :(Form event=On Load)
       MonPopUp:=2
       ...
 End case

C’est dans ce cas que l’ambiguïté apparaît : lors de l’analyse des méthodes, le compilateur déduira par défaut le type Réel pour la variable MonPopUp. Dans ce cas très précis, vous devez explicitement déclarer le tableau dans une méthode compilateur ou dans la méthode formulaire :

 Case of
    :(Form event=On Load)
       ARRAY TEXT(MonPopUp;2)
       MonPopUp:=2
       ...
 End case

L’impossibilité totale de déduire un type
Dans ce cas, seule une directive de compilation peut orienter le compilateur. Le compilateur peut se trouver dans cette situation lorsqu’une variable est utilisée sans être déclarée et dans un cadre qui ne donne aucune information sur son type possible.
Le phénomène se produit principalement dans quatre cas :

  • lorsque vous utilisez des pointeurs,
  • lorsque vous utilisez une commande à syntaxe multiple,
  • lorsque vous utilisez une commande 4D avec des paramètres optionnels de types différents,
  • lorsque vous utilisez une méthode appelée via un URL.

Cas des pointeurs
Un pointeur étant un outil universel qui a donc pour première caractéristique la flexibilité, il est inutile d’espérer qu’il renvoie un type d’une manière ou d’une autre, hormis le sien propre.
Supposons que vous écriviez dans une méthode la séquence suivante :

 LaVar1:=5,2(1)
 LePointeur:=->LaVar1(2)
 LaVar2:=LePointeur->(3)

Bien que la ligne (2) définisse le type de la variable pointée par le pointeur LePointeur, LaVar2 n’est pas pour autant typée. Lors de la compilation, le compilateur peut reconnaître un pointeur, mais n’a aucun moyen de savoir sur quel type de variable il pointe. Il ne peut donc pas déduire le type de LaVar2 ; une directive de compilation du type C_REEL(LaVar2) est donc indispensable.

Cas des commandes à syntaxe multiple
Lorsque vous utilisez une variable associée à la fonction Year of, la variable ne peut être, compte tenu de la nature même de la fonction, que de type Date. En revanche, prenons un cas extrême : la commande GET FIELD PROPERTIES admet deux syntaxes :
GET FIELD PROPERTIES(NoTable;NoChamp;Type;Longueur;Indexée)
GET FIELD PROPERTIES(Pointeur_Champ;Type;Longueur;Indexée)

Lorsque vous utilisez cette commande, le compilateur ne peut pas deviner quelle syntaxe et quels paramètres vous avez choisis. Il vous appartient alors d’orienter le compilateur par une directive de compilation.

Cas des commandes 4D ayant des paramètres optionnels de types différents
Lorsque vous utilisez une commande 4D qui accepte plusieurs paramètres optionnels de différents types, le compilateur ne peut pas deviner quels paramètres ont été passés.
Par exemple, la commande GET LIST ITEM admet deux paramètres optionnels. Le premier est de type Entier long, le second est de type Booléen.
La commande peut donc être utilisée comme ceci :
GET LIST ITEM (liste;position;num;texte;sous-liste;déployé)
ou comme cela :
GET LIST ITEM (liste;position;num;texte;déployé)
Vous devez donc utiliser des directives de compilation pour typer les paramètres optionnels passés à la commande (s’ils n’ont pas déjà été typés suite à leur utilisation dans un autre endroit de la base).

Cas des méthodes appelées via un URL
Si vous écrivez des méthodes appelées via un URL, il est nécessaire de déclarer explicitement la variable Texte $1 dans vos méthodes, par l’intermédiaire de l’instruction C_TEXT($1), dans le cas où vous n’utilisez pas $1 dans la méthode. En effet, le compilateur ne peut pas deviner qu’une méthode 4D va être appelée via un URL.

Si toutes les variables utilisées dans votre base sont explicitement déclarées, il n’est pas nécessaire que le compilateur refasse tout le typage. Dans ce cas, vous pouvez lui demander d’effectuer uniquement la phase de traduction de vos méthodes dans le chemin de compilation. Ainsi, vous économiserez environ 50 % du temps de compilation.

Les directives de compilation peuvent vous aider à accélérer vos méthodes. Pour plus de précision à ce sujet, reportez-vous à la section Conseils d’optimisation. Pour nous en tenir à un exemple simple dans cette section de présentation, imaginez que vous incrémentiez un compteur. Si vous n’avez pas déclaré la variable, le compilateur considérera par défaut qu’elle est de type Numérique (ou réel). Si vous prenez soin de préciser qu’il s’agit d’un Entier long, l’exécution de la base compilée sera plus satisfaisante. En effet, un Réel occupe, sur PC par exemple, 8 octets en mémoire alors que si vous choisissez un type Entier long, le compteur n’en occupera que 4. Il est bien évident que l’incrémentation d’un compteur de 8 octets est plus longue que celle d’un compteur de 4 octets.

Vous avez deux possibilités selon que vous voulez que le compilateur vérifie ou non votre typage.

Le compilateur doit respecter les critères d’identification des variables.

Il existe deux possibilités :
1) Si les variables ne sont pas typées, le compilateur s’en occupera automatiquement pour vous. Toutes les fois que c’est possible, dans la mesure où il n’y a pas d’ambiguïtés, le compilateur déduit automatiquement pour vous le type des variables utilisées. Si, par exemple, vous écrivez :

 V1:=True

le compilateur pourra en déduire que la variable V1 est de type Booléen. De même, si vous écrivez :

 V2:="Ceci est une phrase exemple"

le compilateur en déduira que V2 est une variable de type Texte.
Le programme peut également déduire le type des variables dans des cas moins faciles :

 V3:=V1 `V3 est du même type que V1
 V4:=2*V2 `V4 est du même type que V2

Le compilateur déduit également le type de vos variables d’après les appels aux commandes 4D et à vos propres méthodes. Si vous passez à une méthode un paramètre de type Booléen et un paramètre de type Date, le compilateur donnera le type Booléen et le type Date aux variables locales $1 et $2 de la méthode appelée.
Lors de ces déductions, sauf indication contraire dans les Propriétés de la base, le compilateur donne toujours le type le plus large possible à vos variables. Si vous écrivez :

 LeNombre:=4

le compilateur donnera à la variable LeNombre le type Réel, même si en toute rigueur la valeur 4 est entière. En d’autres termes, le compilateur n’exclut pas que dans d’autres occurrences de la variable, la valeur puisse être 4,5.

Si vous ne voulez pas de cette interprétation générale, vous pouvez préciser vos choix : c’est l’objet de ce qu’on appelle les directives de compilation. 


2) Les directives de compilation servent à déclarer de façon explicite les variables simples que vous utilisez dans vos bases.
Leur utilisation se fait de la façon suivante :

 C_BOOLEAN(Var)

Par cette directive, vous forcez le compilateur à créer une variable Var dont le type sera Booléen.
Dans le cas où une application comporte des directives de compilation, le compilateur les détecte et n’a pas à se livrer à une quelconque estimation. Une directive a la priorité sur une déduction à partir d’une affectation ou d’une utilisation.
Les variables simples déclarées par la directive de compilation _o_C_INTEGER sont considérés comme des Entiers longs, compris entre –2147483648 et +2147483647 comme pour les variables déclarées par la directive C_LONGINT.

Si vous voulez que le compilateur n’ait pas à vérifier votre typage, vous devez lui donner les clés de l’identification de ses objets.
La convention à respecter est la suivante : les directives de compilation des variables process ou interprocess, ainsi que les paramètres, devront être placées dans une ou plusieurs méthodes dont le nom commence par Compiler.
Par défaut, le compilateur vous permet de générer automatiquement cinq types de méthodes Compilateur regroupant les directives pour les variables, les tableaux et les paramètres des méthodes (pour plus d'informations sur ce point, reportez-vous au manuel Mode Développement).

Note : La déclaration des paramètres des méthodes obéit à la syntaxe suivante : Directive (nom de méthode; param). Cette syntaxe n’est pas exécutable en mode interprété.

Paramètres particuliers

  • Les paramètres reçus par les méthodes base
    Ces paramètres sont typés par défaut par le compilateur, si la déclaration n’a pas été faite explicitement. Néanmoins, si vous les déclarez, la déclaration doit se faire à l’intérieur des méthodes base.
    La déclaration de ces paramètres ne peut pas se faire dans une méthode compilateur.
    Exemple : la Méthode base Sur connexion Web reçoit six paramètres $1 à $6 de type texte. En début de méthode, vous devez écrivez : C_TEXT ($1;$2;$3;$4;$5;$6)
  • Les triggers
    Le paramètre $0 (Entier long), résultat d'un trigger, est typé par défaut par le compilateur, si la déclaration n’a pas été faite explicitement. Néanmoins si vous le déclarez, la déclaration doit se faire à l’intérieur du trigger. La déclaration de ce paramètre ne peut pas se faire dans une méthode compilateur.
  • Les objets acceptant l'événement formulaire “Sur glisser”
    Le paramètre $0 (Entier long), résultat d’un événement formulaire “Sur glisser”, est typé par défaut par le compilateur, si la déclaration n’a pas été faite explicitement. Néanmoins si vous le déclarez, la déclaration doit se faire à l’intérieur de la méthode objet. La déclaration de ce paramètre ne peut pas se faire dans une méthode compilateur.
    Note : Le compilateur n’initialise pas le paramètre $0. Dès que vous utilisez l’événement formulaire Sur glisser, vous devez donc initialiser $0. Par exemple :
 C_LONGINT($0)
 If(Form event=On Drag Over)
    $0:=0
    ...
    If($TypeDeDonnées=Is picture)
       $0:=-1
    End if
    ...
 End if

Les directives de compilation lèvent toute ambiguïté sur les types. L’exigence de rigueur ne se fait pas pour autant intolérance.
S’il vous arrive d’utiliser un réel là où vous avez déclaré un entier, le compilateur ne considère pas qu’il y a conflit et suit simplement vos directives. Ainsi, si vous écrivez :

 C_LONGINT(vEntier)
 vEntier:=2,6

Le compilateur ne verra pas un conflit de types de nature à empêcher la compilation et prendra automatiquement en compte la partie entière arrondie du nombre affecté (3 au lieu de 2,6).



Voir aussi  

Conseils d’optimisation
Guide du typage
Messages d'erreurs
Précisions de syntaxe

 
PROPRIÉTÉS 

Produit : 4D
Thème : Compilateur

 
HISTORIQUE 

 
UTILISATION DE L'ARTICLE

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