Utiliser des conditions, des messages et des macros
Le compilateur Free Pascal prend en charge les conditions comme dans le Turbo Pascal normal, Delphi ou Mac OS Pascal. Il fait cependant plus que cela. Il vous permet de faire des macros utilisables dans votre code, et il vous permet de définir des messages ou des erreurs étant affichés lors de la compilation. Il prend également en charge les variables de compilation et les expressions de compilation, que l'on trouve couramment dans les compilateurs Mac OS.
Les diverses directives de compilation conditionnelle ($IF, $IFDEF, $IFOPT) sont utilisées en combinaison avec $DEFINE pour permettre au programmeur de choisir au moment de la compilation quelles parties du code doivent être compilées. Cela peut être utilisé par exemple :
- Pour choisir une implémentation pour un système d'exploitation plutôt qu'un autre.
- Pour choisir une version de démonstration ou une version complète.
- Pour faire la distinction entre une version de débogage et une version pour l'expédition.
Ces options sont ensuite choisies lors de la compilation du programme, en incluant ou en excluant des parties du code selon les besoins. Cela s'oppose à l'utilisation de variables normales et à l'exécution de portions de code sélectionnées au moment de l'exécution, auquel cas du code supplémentaire est inclus dans l'exécutable.
Les conditionnels
Les règles d'utilisation des symboles conditionnels sont les mêmes que sous Turbo Pascal ou Delphi. La définition d'un symbole se déroule comme suit :
{$define Symbol} |
À partir de ce point de votre code, le compilateur connaît le symbole Symbol. Les symboles sont, comme le langage Pascal, insensibles à la casse.
Vous pouvez également définir un symbole sur la ligne de commande. l'option -dSymbol définit le symbole Symbol. Vous pouvez spécifier autant de symboles que vous le souhaitez sur la ligne de commande.
L'annulation de la définition d'un symbole existant s'effectue de la même manière :
{$undef Symbol} |
Si le symbole n'existait pas encore, cela ne fait rien. Si le symbole existait auparavant, le symbole sera effacé, et ne sera plus reconnu dans le code suivant l'instruction {$undef ...}.
Vous pouvez également annuler la définition des symboles à partir de la ligne de commande avec le commutateur de ligne de commande -u. Pour compiler le code de manière conditionnelle, selon qu'un symbole est défini ou non, vous pouvez inclure le code dans un {$ifdef Symbol} . . . {$endif} paire. Par exemple, le code suivant ne sera jamais compilé :
- {$undef MySymbol}
- {$ifdef Mysymbol}
- DoSomething;
- ...
- {$endif}
De même, vous pouvez inclure votre code dans un {$ifndef Symbol} ... {$endif} paire. Ensuite, le code entre la paire ne sera compilé que lorsque le symbole utilisé n'existe pas. Par exemple, dans le code suivant, l'appel au DoSomething sera toujours compilé :
- {$undef MySymbol}
- {$ifndef Mysymbol}
- DoSomething;
- ...
- {$endif}
Vous pouvez combiner les deux alternatives dans une structure, à savoir comme suit :
- {$ifdef Mysymbol}
- DoSomething;
- {$else}
- DoSomethingElse
- {$endif}
Dans cet exemple, si MySymbol existe, alors l'appel à DoSomething sera compilé. S'il n'existe pas, l'appel à DoSomethingElse est compilé.
Symboles prédéfinis
Le compilateur Free Pascal définit certains symboles avant de commencer à compiler votre programme ou votre unité. Vous pouvez utiliser ces symboles pour différencier les différentes versions du compilateur et les différents compilateurs.
Remarque : Les symboles, même lorsqu'ils sont définis dans la partie interface d'une unité, ne sont pas disponibles en dehors de cette unité.
Les macros
Les macros ressemblent beaucoup à des symboles ou à des variables de compilation dans leur syntaxe, la différence est que les macros ont une valeur alors qu'un symbole est simplement défini ou n'est pas défini. De plus, suite à la définition d'une macro, toute occurrence de la macro dans la source pascal sera remplacée par la valeur de la macro (un peu comme la prise en charge des macros dans le préprocesseur C). Si la prise en charge des macros est requise, le commutateur de ligne de commande -Sm doit être utilisé pour l'activer, ou la directive doit être insérée :
{$MACRO ON} |
sinon les macros seront considérées comme un symbole.
La définition d'une macro dans un programme se fait de la même manière que la définition d'un symbole ; dans une instruction de préprocesseur {$define} :
{$define ident:=expr} |
Si le compilateur rencontre ident dans le reste du fichier source, il sera immédiatement remplacé par expr. Ce remplacement fonctionne de manière récursive, ce qui signifie que lorsque le compilateur développe une macro, il examine à nouveau l'expression résultante pour voir si un autre remplacement peut être effectué. Cela signifie que des précautions doivent être prises lors de l'utilisation de macros, car une boucle infinie peut se produire de cette manière (les versions récentes du compilateur en avertiront).
Voici deux exemples illustrant l'utilisation des macros :
- {$define sum:=a:=a+b;}
- { ... }
- sum { sera étendu à 'a:=a+b;'. Notez l'absence du point-virgule }
- { ... }
- {$define b:=100}
- sum { Sera étendu récursivement à a:=a+100 ; }
- { ... }
L'exemple précédent peut mal tourner :
- {$define sum:=a:=a+b;}
- { ... }
- sum { sera étendu à 'a:=a+b;'. Notez l'absence du point-virgule. }
- { ... }
- {$define b=sum} { Ne faites pas ça !!! }
- sum { Sera infiniment récursivement étendu... }
- { ... }
À partir de la version 3.3.1, le compilateur avertira des macros récursives, mais les versions précédentes du compilateur développeraient de manière récursive les macros jusqu'à ce qu'il manque de mémoire.
Remarque : Les macros définies dans la partie interface d'une unité ne sont pas disponibles en dehors de cette unité ! Ils peuvent simplement être utilisés comme commodité de notation ou dans des compilations conditionnelles.
Par défaut, le compilateur prédéfinit trois macros, contenant le numéro de version, le numéro de réalisation et le numéro de correctif. Ils sont répertoriés dans le tableau suivant :
Symbole | Contient |
---|---|
FPC_FULLVERSION | Un nombre entier de version du compilateur. |
FPC_VERSION | Le numéro de version du compilateur. |
FPC_RELEASE | Le numéro de version du compilateur. |
FPC_PATCH | Le numéro de correctif du compilateur. |
La macro FPC_FULLVERSION contient un numéro de version utilisant toujours 2 chiffres pour les numéros de version RELEASE et PATCH. Cela signifie que la version 2.3.1 entraînera FPC_FULLVERSION=20301. Ce nombre facilite la détermination des versions minimales.
Remarque N'oubliez pas que la prise en charge des macros n'est pas activée par défaut. Il doit être activé avec le commutateur de ligne de commande -Sm ou en utilisant la directive {$MACRO ON}.
Depuis la version 3.1.1, pour les cibles embarquées (comme AVR), le compilateur définit plusieurs macros définissant l'agencement mémoire de la cible, elles sont listées dans le tableau suivant (Ces macros sont des valeurs entières et peuvent être utilisées dans le code et dans les instructions $IF) :
Symbole | Contient |
---|---|
FPC_FLASHBASE | Adresse de base de la mémoire flash. |
FPC_FLASHSIZE | Taille de la mémoire flash. |
FPC_RAMBASE | Adresse de base de la mémoire RAM. |
FPC_RAMSIZE | Taille de la mémoire RAM. |
FPC_BOOTBASE | Adresse de base de la mémoire de démarrage. |
FPC_BOOTSIZE | Taille de la mémoire de démarrage. |
FPC_EEPROMBASE | Adresse de base de la mémoire EEPROM. |
FPC_EEPROMSIZE | Taille de la mémoire EEPROM. |
Variables de temps de compilation
En mode MacPas, des variables de temps de compilation peuvent être définies. Ils sont distincts des symboles en ce qu'ils ont une valeur, et ils sont distincts des macros en ce qu'ils ne peuvent pas être utilisés pour remplacer des parties du texte source par leur valeur. Leur comportement est compatible avec les variables de temps de compilation trouvées dans les compilateurs Pascal populaires pour Macintosh.
Une variable de temps de compilation est définie comme ceci :
{$SETC ident:= expression} |
L'expression est ce qu'on appelle une expression de temps de compilation, étant évaluée une fois, au point où la directive {$SETC } est rencontrée dans la source. La valeur résultante est ensuite affectée à la variable de temps de compilation.
Une seconde directive {$SETC } pour la même variable écrase la valeur précédente.
Contrairement aux macros et aux symboles, les variables de temps de compilation définies dans la partie Interface d'une unité sont exportées. Cela signifie que leur valeur sera disponible dans des unités utilisant l'unité dans laquelle la variable est définie. Cela nécessite que les deux unités soient compilées en mode macpas.
La grande différence entre les macros et les variables de temps de compilation est que la première est un mécanisme de substitution de texte pur (un peu comme en C), où la seconde ressemble à des variables de langage de programmation normales, mais elles ne sont disponibles que pour le compilateur.
En mode MacPas, les variables de temps de compilation sont toujours activées.
Compiler les expressions de temps
Définition
En plus des constructions Turbo Pascal habituelles pour la compilation conditionnelle, le compilateur Free Pascal prend également en charge un mécanisme de compilation conditionnelle plus puissant : la construction {$IF}, qui peut être utilisée pour évaluer les expressions au moment de la compilation.
Le prototype de cette construction est le suivant :
{$if expr} CompileTheseLines; {$else} BetterCompileTheseLines; {$endif} |
Le contenu d'une expression est limité à ce qui peut être évalué au moment de la compilation :
- Constantes (chaînes de caractères, nombres)
- Macros
- Variables de temps de compilation (mode MacPas uniquement)
- Expressions constantes Pascal (mode Delphi uniquement)
Les symboles sont remplacés par leur valeur. Pour les macros, une substitution récursive peut se produire. Les opérateurs booléens suivants sont disponibles :
=, <>, >, <, >=, <=, AND, NOT, OR, IN |
L'opérateur IN teste la présence d'une variable de compilation dans un ensemble. Les fonctions suivantes sont également disponibles :
Fonction | Description |
---|---|
TRUE | Défini en mode MacPas uniquement, il est évalué à True. Dans les autres modes, 1 peut être utilisé. |
FALSE | Défini en mode MacPas uniquement, il est évalué à False. Dans les autres modes, 0 peut être utilisé. |
DEFINED(sym) | Sera évalué à TRUE si un symbole de temps de compilation est défini. En mode MacPas, les parenthèses sont facultatives, c'est-à-dire : {$IF DEFINED(MySym)} est équivalent à {$IF DEFINED MySym} |
UNDEFINED | sym sera évalué à TRUE si un symbole de temps de compilation n'est pas défini, et à FALSE sinon (mode MacPas uniquement). |
OPTION(opt) | vaut TRUE si une option du compilateur est définie (mode MacPas uniquement). Elle est équivalente à la directive {$IFOPT }. |
SIZEOF(passym) | Évalue à la taille d'un type pascal, d'une variable ou d'une constante. |
DECLARED(passym) | Prend la valeur TRUE si le symbole pascal est déclaré à cet endroit dans les sources, ou FALSE s'il n'est pas encore défini. |
Dans les expressions, les règles suivantes sont utilisées pour l'évaluation :
- Si toutes les parties de l'expression peuvent être évaluées comme des booléens (avec 1 et 0 représentant TRUE et FALSE), l'expression est évaluée à l'aide de booléens.
- Si toutes les parties de l'expression peuvent être évaluées comme des nombres, alors l'expression est évaluée en utilisant des nombres.
- Dans tous les autres cas, l'expression est évaluée à l'aide de chaînes de caractères.
Si l'expression complète est évaluée à '0', elle est alors considérée comme fausse et rejetée. Sinon, il est considéré comme vrai et accepté. Cela peut avoir des conséquences inattendues :
- {$if 0}
sera évalué à FALSE et sera rejeté, tandis que :
- {$if 00}
sera évalué à TRUE.
Usage
L'utilisation de base des expressions de temps de compilation est la suivante :
{$if expr} CompileTheseLines; {$endif} |
Si expr est évalué à TRUE, alors CompileTheseLines sera inclus dans la source.
Comme en pascal normal, il est possible d'utiliser {$ELSE } :
{$if expr} CompileTheseLines; {$else} BetterCompileTheseLines; {$endif} |
Si expr vaut True, CompileTheseLines sera compilé. Sinon, BetterCompileTheseLines sera compilé. De plus, il est possible d'utiliser {$ELSEIF}
{$IF expr} // ... {$ELSEIF expr} // ... {$ELSEIF expr} // ... {$ELSE} // ... {$ENDIF} |
En plus des constructions ci-dessus, étant également prises en charge par Delphi, ce qui précède est complètement équivalent à la construction suivante en mode MacPas :
{$IFC expr} //... {$ELIFC expr} ... {$ELIFC expr} ... {$ELSEC} ... {$ENDC} |
c'est-à-dire que IFC correspond à IF, ELIFC correspond à ELSEIF, ELSEC est équivalent à ELSE et ENDC est l'équivalent de ENDIF. De plus, IFEND est équivalent à ENDIF :
{$IF EXPR} CompileThis; {$ENDIF} |
En mode MacPas, il est possible de mélanger ces constructions. L'exemple suivant montre certaines des possibilités :
- {$ifdef fpc}
- Var
- y:LongInt;
- {$else fpc}
- Var
- z:LongInt;
- {$endif fpc}
- Var
- x:LongInt;
- BEGIN
- {$IF (FPC_VERSION > 2) or ((FPC_VERSION = 2) and ((FPC_RELEASE > 0) or ((FPC_RELEASE = 0) and (FPC_PATCH >= 1))))}
- {$DEFINE FPC_VER_201_PLUS}
- {$ENDIF}
- {$ifdef FPC_VER_201_PLUS}
- {$info Au moins c'est la version 2.0.1}
- {$else}
- {$fatal Problème avec la vérification de version}
- {$endif}
- {$define x:=1234}
- {$if x=1234}
- {$info x=1234}
- {$else}
- {$fatal x devrait être 1234}
- {$endif}
- {$if 12asdf and 12asdf}
- {$info $si 12asdf et 12asdf sont corrects}
- {$else}
- {$fatal $si 12asdf et 12asdf rejetés}
- {$endif}
- {$if 0 or 1}
- {$info $si 0 ou 1 est ok}
- {$else}
- {$fatal $si 0 ou 1 rejeté}
- {$endif}
- {$if 0}
- {$fatal $si 0 accepté}
- {$else}
- {$info $si 0 est ok}
- {$endif}
- {$if 12=12}
- {$info $si 12=12 est ok}
- {$else}
- {$fatal $si 12=12 rejeté}
- {$endif}
- {$if 12<>12}
- {$info $si 12<>312 est ok}
- {$else}
- {$fatal $if 12<>312 rejeté}
- {$endif}
- {$if 12<=312}
- {$info $si 12<=312 est correct}
- {$else}
- {$fatal $si 12<=312 rejeté}
- {$endif}
- {$if 12<312}
- {$info $si 12<312 est ok}
- {$else}
- {$fatal $si 12<312 rejeté}
- {$endif}
- {$if a12=a12}
- {$info $si a12=a12 est ok}
- {$else}
- {$fatal $si a12=a12 rejeté}
- {$endif}
- {$if a12<=z312}
- {$info $si a12<=z312 est ok}
- {$else}
- {$fatal $si a12<=z312 rejeté}
- {$endif}
- {$if a12<z312}
- {$info $si a12<z312 est ok}
- {$else}
- {$fatal $if a12<z312 rejeté}
- {$endif}
- {$if not(0)}
- {$info $si non(0) est OK}
- {$else}
- {$fatal $si non(0) rejeté}
- {$endif}
- {$IF NOT UNDEFINED FPC}
- // Détecter les trucs FPC lors de la compilation sur MAC.
- {$SETC TARGET_RT_MAC_68881:= FALSE}
- {$SETC TARGET_OS_MAC := (NOT UNDEFINED MACOS) OR (NOT UNDEFINED DARWIN)}
- {$SETC TARGET_OS_WIN32 := NOT UNDEFINED WIN32}
- {$SETC TARGET_OS_UNIX := (NOT UNDEFINED UNIX) AND (UNDEFINED DARWIN)}
- {$SETC TYPE_EXTENDED := TRUE}
- {$SETC TYPE_LONGLONG := FALSE}
- {$SETC TYPE_BOOL := FALSE}
- {$ENDIF}
- {$info ************************************************************}
- {$info * Il faut maintenant suivre au moins 2 messages d'erreur : *}
- {$info ************************************************************}
- {$if not(0}
- {$endif}
- {$if not(<}
- {$endif}
- END.
Comme vous pouvez le voir dans l'exemple, cette construction n'est pas utile lorsqu'elle est utilisée avec des symboles normaux, uniquement si vous utilisez des macros. Elles peuvent être très utiles. Lorsque vous essayez cet exemple, vous devez activer la prise en charge des macros, avec le commutateur de ligne de commande -Sm.
L'exemple suivant ne fonctionne qu'en mode MacPas :
- {$SETC TARGET_OS_MAC := (NOT UNDEFINED MACOS) OR (NOT UNDEFINED DARWIN)}
- {$SETC DEBUG := TRUE}
- {$SETC VERSION := 4}
- {$SETC NEWMODULEUNDERDEVELOPMENT := (VERSION >= 4) OR DEBUG}
- {$IFC NEWMODULEUNDERDEVELOPMENT}
- {$IFC TARGET_OS_MAC}
- { ... nouveau code mac }
- {$ELSEC}
- { ... nouveau autre code }
- {$ENDC}
- {$ELSEC}
- { ... ancien code }
- {$ENDC}
Les messages
Le Free Pascal vous permet de définir des messages normaux, d'avertissement et d'erreur dans votre code. Les messages peuvent être utilisés pour afficher des informations utiles, telles que des avis de copyright, une liste de symboles auxquels votre code répond, etc.
Les avertissements peuvent être utilisés si vous pensez qu'une partie de votre code est encore boguée, ou si vous pensez qu'une certaine combinaison de symboles n'est pas utile.
Les messages d'erreur peuvent être utiles si vous avez besoin qu'un certain symbole soit défini, pour avertir qu'une certaine variable n'est pas définie ou lorsque la version du compilateur ne convient pas à votre code.
Le compilateur traite ces messages comme s'ils étaient générés par le compilateur. Cela signifie que si vous n'avez pas activé les messages d'avertissement, l'avertissement ne s'affichera pas. Les erreurs sont toujours affichées et le compilateur s'arrête si 50 erreurs se sont produites. Après une erreur fatale, le compilateur s'arrête aussitôt.
Pour les messages, la syntaxe est la suivante :
{$Message TYPE Message text} |
Où TYPE est l'un des éléments suivants :
Type | Description |
---|---|
NOTE | Un message de note est émis. Équivalent à $NOTE. |
HINT | Un message d'avertissement est émis. Équivalent à $HINT. |
WARNING | Un message d'avertissement est émis. Équivaut à $ATTENTION. |
ERROR | Un message d'erreur est émis. Équivalent à $ERROR. |
FATAL | Un message d'erreur fatale est émis. Équivalent à $FATAL. |
Alternativement, l'un des éléments suivants peut être utilisé :
{$Info Message text} |
Pour les notes :
{$Note Message text} |
Pour les avertissements :
{$Warning Warning Message text} |
Pour les conseils :
{$Hint Warning Message text} |
Pour les erreurs :
{$Error Error Message text} |
Enfin, pour les erreurs fatales :
{$Fatal Error Message text} |
{$Stop Error Message text} |
La différence entre les messages $Error et $Fatal ou $Stop est que lorsque le compilateur rencontre une erreur, il continue à compiler. Avec une erreur fatale ou un arrêt, le compilateur s'arrête immédiatement.
Remarque : Vous ne pouvez pas utiliser le caractère '}' dans votre message, car il sera traité comme l'accolade fermante du message.
Par exemple, le morceau de code suivant génère une erreur lorsqu'aucun des symboles RequiredVar1 ou RequiredVar2 n'est défini :
- {$IFNDEF RequiredVar1}
- {$IFNDEF RequiredVar2}
- {$Error L'une de Requiredvar1 ou Requiredvar2 doit être définie}
- {$ENDIF}
- {$ENDIF}
Mais le compilateur continuera à compiler. Cependant, il ne générera pas de fichier unité ni de programme (puisqu'une erreur s'est produite).