Units et bibliothèques
Un jeu complet compte des milliers de lignes. Les écrire dans un seul fichier deviendrait vite ingérable. Ce chapitre montre comment répartir un programme en plusieurs fichiers, les units, et comment écrire ses propres bibliothèques, réutilisables d'un projet à l'autre.
Pourquoi découper en fichiers ?
Jusqu'ici, tout le programme tenait dans main.s8p. Découper en fichiers
permet de :
- s'y retrouver : le joueur dans un fichier, les ennemis dans un autre, l'affichage dans un troisième ;
- séparer ce qui dépend de la machine de ce qui n'en dépend pas : les règles du jeu restent les mêmes sur C64 et sur PC Engine, seul l'affichage change ;
- réutiliser : une bibliothèque bien écrite sert dans tous vos projets.
Simple8 propose deux sortes de fichiers pour cela :
| Fichier | Contient | S'ajoute avec | S'appelle par |
|---|---|---|---|
.s8u | une unit : une partie du programme | addunit "Joueur.s8u"; | Joueur.deplacer() |
.s8l | une bibliothèque : des fonctions réutilisables | addlib outils.outils; | outils.carre() |
Les units
La unit principale
Depuis le premier chapitre, main.s8p contient une unit : unit { … }.
Sans nom, elle s'appelle Main. C'est la unit principale, la
seule à avoir une fonction main().
Une unit dans son fichier
Une autre unit s'écrit dans son propre fichier, d'extension .s8u. Elle a un
nom, ses variables et ses fonctions, mais pas de main() :
Joueur.s8u
unit Joueur {
ubyte x;
void init() {
.x = 10;
}
void deplacer(ubyte dx) {
.x = .x + dx;
}
}
- À l'intérieur de la unit, rien ne change : ses variables s'écrivent
.x, ses fonctions s'appellent sans préfixe. - Une unit peut charger elle-même les bibliothèques dont elle a besoin, avec
addliben tête de fichier. Une bibliothèque déjà chargée par un autre fichier ne l'est pas deux fois. - La ligne
architectureest facultative dans un.s8u: la unit prend celle du programme.
Ajouter une unit : addunit
Le programme principal ajoute ses units avec addunit, après les
addlib. Le chemin est relatif au fichier qui contient l'addunit,
et peut désigner un sous-dossier :
main.s8p
architecture commodore.c64;
addunit "Joueur.s8u";
addunit "jeu/Ennemis.s8u"; // une unit rangée dans un sous-dossier
unit {
main() {
Joueur.init();
Joueur.deplacer(3); // Joueur.x vaut 13
}
}
Les deux écritures addunit "Joueur.s8u"; et
addunit("Joueur.s8u"); sont acceptées.
La règle de nommage
Comme en Java, le nom de la unit est celui de son fichier, sans l'extension :
| Fichier | Déclaration attendue |
|---|---|
main.s8p | unit { ou unit Main { |
Joueur.s8u | unit Joueur { |
jeu/Ennemis.s8u | unit Ennemis { (le dossier ne compte pas) |
Un autre nom est refusé :
Unit name mismatch: file 'Joueur.s8u' must declare 'unit Joueur {' but found 'unit Heros {'
Ainsi, en lisant Joueur.deplacer() n'importe où dans le projet, on sait
tout de suite dans quel fichier chercher.
Même nom, même casse. La vérification ignore les majuscules :
unit joueur est accepté dans Joueur.s8u. Mais la unit s'appelle
alors joueur, et un appel Joueur.init() ne la trouve pas.
Écrivez le nom exactement comme celui du fichier ; par convention, avec une majuscule.
Les autres règles vérifiées
| Règle | Message |
|---|---|
l'extension est .s8u | a unit file must have the .s8u extension |
| le fichier existe | file not found (Absent.s8u) |
| un fichier n'est ajouté qu'une fois | this unit file is already part of the program |
pas de main() dans un .s8u | main() is not allowed in a unit file (.s8u) outside a context |
Utiliser une unit depuis une autre
Depuis une autre unit, on écrit le nom de la unit, un point, puis la variable ou la fonction :
.position = Joueur.x; // lire une variable de Joueur
Joueur.deplacer(3); // appeler une fonction de Joueur
Main.ajouter(5); // depuis une unit : une fonction de la unit principale
| Ce qu'on désigne | Dans sa propre unit | Depuis une autre unit |
|---|---|---|
| une variable | .x | Joueur.x |
| une fonction | deplacer(3) | Joueur.deplacer(3) |
| une constante | .MAX | Joueur.MAX |
Deux units peuvent avoir des fonctions du même nom : Joueur.init() et
Ennemis.init() ne se confondent pas.
Modifier une autre unit : par ses fonctions
Lire une variable d'une autre unit et appeler ses fonctions marchent dans tous les sens. Pour modifier ses variables, passez par une de ses fonctions : c'est la unit elle-même qui change ses données.
// dans Joueur.s8u
Ennemis.changer(9); // plutôt que Ennemis.nombre = 9;
// dans Ennemis.s8u
void changer(ubyte n) {
.nombre = n;
}
Une limite actuelle. Une affectation directe
(Ennemis.nombre = 9;) n'est acceptée que vers une unit ajoutée
avant celle qui écrit. La unit principale peut donc écrire dans toutes ses units,
mais une unit ne peut pas écrire dans Main, ni dans une unit ajoutée après
elle : Variable 'Ennemis.nombre' not found !. Passer par une fonction évite
le problème, et rend le programme plus clair.
Les bibliothèques
Une bibliothèque, fichier .s8l, regroupe des fonctions réutilisables. Les
bibliothèques de libs8/ (vic.screentext, vce.base…)
sont écrites ainsi. Une bibliothèque se déclare avec library("nom") :
userlibs/outils/outils.s8l
library("outils")
{
const ubyte MAX = 200;
ubyte compteur;
uint carre(uint n) {
return n * n;
}
void incrementer() {
.compteur++;
}
}
Le programme la charge avec addlib, puis l'utilise avec le nom donné dans
library("…") :
addlib outils.outils; // le fichier outils/outils.s8l
unit {
main() {
.resultat = outils.carre(9); // 81
outils.incrementer();
.valeur = outils.MAX; // 200
}
}
Le préfixe est le nom de library("…"), pas celui du fichier.
Le compilateur ne vérifie pas qu'ils sont identiques : avec
library("util") dans outils.s8l, les appels
outils.carre() ne trouvent rien. Donnez toujours le même nom au fichier et à
la bibliothèque.
Où le compilateur cherche
addlib dossier.fichier; désigne le fichier dossier/fichier.s8l.
Le compilateur le cherche à quatre endroits, dans cet ordre, et prend le premier
trouvé :
libs8/constructeur/machine/: les bibliothèques de la machine ;libs8/: les bibliothèques communes à toutes les machines ;- le dossier de la variable
S8_USER_LIBS: vos bibliothèques, pour tous vos projets ; userlibs/, dans le dossier du projet : les bibliothèques de ce projet.
Introuvable, la bibliothèque donne Library not found: outils/absente.s8l.
Générique ou propre à une machine
- Une bibliothèque générique n'a pas de ligne
architecture. Elle prend celle du programme et marche sur toutes les machines, à condition de n'utiliser que du Simple8 : ni_asm, ni bibliothèque d'une machine. - Une bibliothèque propre à une machine commence par sa ligne
architecture. Elle peut utiliser le matériel et les bibliothèques de cette machine.
Une bibliothèque qui en utilise une autre
Une bibliothèque charge celles dont elle a besoin, avec addlib en tête de
fichier, ou avec require dans son bloc, comme le font celles de
libs8/ :
architecture commodore.c64;
library("texte")
{
require vic.screentext; // ou : addlib vic.screentext; avant library
ubyte lignes;
void ligne(uint t) {
vic.setCursor(2, .lignes);
vic.print(t);
.lignes++;
}
}
Une bibliothèque sur la mauvaise machine
Que se passe-t-il quand le code d'une bibliothèque ne convient pas à la machine du programme ? Tout dépend de la situation, et toutes ne sont pas signalées clairement :
| Situation | Conséquence |
|---|---|
Charger une bibliothèque d'une autre machine : addlib vic.screentext;
dans un programme PC Engine |
Refusée. Elle est rangée dans le dossier du C64, le compilateur
ne la cherche pas ailleurs : Library not found: vic/screentext.s8l. |
Une bibliothèque générique (sans architecture) contient un bloc
_asm 6502, et le programme est pour un Z80 (Amstrad CPC) |
Refusée. Les instructions sont vérifiées pour le processeur du
programme : Unknown mnemonic 'LDA' for architecture Zilog Z80A (Amstrad
CPC). |
| La même bibliothèque, dans un programme pour une autre machine 6502 | Acceptée sans message. Le processeur comprend les instructions,
mais le matériel n'est pas le même : une adresse comme $D020 (la
bordure du C64) n'a pas ce rôle sur une autre machine. Le programme compile, et se
trompe. |
| Une bibliothèque déclare une architecture différente de celle du programme | Un simple avertissement,
Architecture declaration is different from project, puis, selon les
cas : des erreurs d'assemblage obscures
(Symbol '_context_ram_start' already defined,
Unknown instruction: .IMPORTZP), ou une compilation sans erreur d'un
code écrit pour une autre machine. |
Ne pas ignorer cet avertissement. « Architecture declaration is different from project » veut dire qu'une bibliothèque a été écrite pour une autre machine. Les erreurs qui suivent, quand il y en a, ne désignent pas la vraie cause : c'est la bibliothèque qu'il faut changer.
D'où trois règles simples :
- Une bibliothèque qui touche au matériel (un bloc
_asm, une adresse de la machine, une bibliothèque commevicouvdc) déclare son architecture, et ne sert qu'aux programmes de cette machine. - Une bibliothèque pour toutes les machines n'a pas de ligne
architecture, et ne contient que du Simple8 : ni_asm, ni bibliothèque de machine. C'est le cas denombresdans l'exemple ci-dessous. - Pour une même bibliothèque sur plusieurs machines, on écrit une
version par machine, rangée sous le même chemin dans le dossier de chacune :
addlibtrouve d'abord celle de la machine du programme. Ainsi,libs8/math/bcd.s8lest la version générique demath.bcd, etlibs8/nintendo/dmg/math/bcd.s8lla version de la Game Boy, plus rapide.
addlib plutôt que require, pour l'instant.
Si une bibliothèque appelée par require est introuvable, le compilateur
affiche une exception Java, mais annonce 0 error(s), sans produire de
programme. Avec addlib, l'erreur est donnée normalement :
Library not found.
Un exemple : le tableau de score en fichiers
Le tableau de score du chapitre 5, réparti en quatre fichiers selon leur rôle :
Score/
├── main.s8p le programme principal : il enchaîne les étapes
├── Partie.s8u les règles du jeu : score, vies, gemmes
├── Ecran.s8u l'affichage, propre à la machine
└── userlibs/
└── nombres/
└── nombres.s8l une bibliothèque générique : un nombre en chiffres
main.s8p
// Units et bibliothèques : le tableau de score, réparti en fichiers (Commodore 64)
architecture commodore.c64;
addunit "Partie.s8u";
addunit "Ecran.s8u";
unit {
main() {
Partie.nouvelle();
Partie.ramasser(7);
Partie.perdreVie();
Ecran.preparer();
Ecran.ligne(10, "SCORE ", Partie.score, 5);
Ecran.ligne(12, "VIES ", Partie.vies, 1);
Ecran.ligne(14, "GEMMES ", Partie.gemmes, 2);
}
}
Partie.s8u
// Partie.s8u : les règles du jeu (sans rien de propre à une machine)
unit Partie {
const uint POINTS_PAR_GEMME = 250;
uint score;
ubyte vies;
ubyte gemmes;
// Une nouvelle partie
void nouvelle() {
.score = 0;
.vies = 3;
.gemmes = 0;
}
// Le joueur ramasse des gemmes
void ramasser(ubyte n) {
.gemmes = .gemmes + n;
.score = .score + .POINTS_PAR_GEMME * n;
}
// Le joueur perd une vie
void perdreVie() {
if (.vies > 0) {
.vies--;
}
}
}
Ecran.s8u
// Ecran.s8u : l'affichage, propre au Commodore 64
addlib vic.base;
addlib vic.screentext;
addlib nombres.nombres;
unit Ecran {
ubyte tampon[6]; // les chiffres d'un nombre, et le 0 de fin
// Fond bleu, texte blanc
void preparer() {
vic.setBorderColor(0);
vic.setBackgroundColor(6);
vic.initScreen();
vic.clearScreen();
vic.setFontColor(1);
}
// Un texte puis un nombre, à la ligne y
void ligne(ubyte y, uint texte, uint valeur, ubyte chiffres) {
vic.setCursor(13, y);
vic.print(texte);
nombres.enChiffres(valeur, @.tampon, chiffres);
vic.print(@.tampon);
}
}
userlibs/nombres/nombres.s8l
// nombres.s8l : une bibliothèque générique (sans ligne architecture), pour toutes les machines
library("nombres")
{
// Écrit les chiffres de valeur dans dest, avec des zéros devant, puis le 0 de fin
void enChiffres(uint valeur, ubyte *dest, ubyte chiffres) {
uint diviseur = 1;
ubyte k = 0;
for (ubyte i = 1; i < chiffres; i++) {
diviseur = diviseur * 10;
}
while (diviseur > 0) {
dest[k] = (ubyte)('0' + (valeur / diviseur) % 10);
k++;
diviseur = diviseur / 10;
}
dest[k] = 0;
}
}
main.s8pne fait plus qu'enchaîner les étapes : il se lit comme un résumé.Partiene contient que des règles, sans rien qui dépende de la machine.Ecranest la seule unit qui parle au matériel. Elle charge elle-même ses bibliothèques.nombresne fait aucun affichage : elle range les chiffres dans un tableau, queEcranaffiche ensuite. C'est ce qui la rend générique.
La version PC Engine
C'est là que le découpage paie : Partie.s8u et
nombres.s8l sont exactement les mêmes fichiers. Seuls
changent la ligne architecture de main.s8p (et la position des
lignes), et Ecran.s8u :
Ecran.s8u
// Ecran.s8u : l'affichage, propre à la PC Engine
addlib sys.resource;
addlib vce.base;
addlib vdc.base;
addlib vdc.screentext;
addlib nombres.nombres;
unit Ecran {
const int font = addResource("font.bin");
ubyte tampon[6]; // les chiffres d'un nombre, et le 0 de fin
// La police, fond bleu, texte blanc
void preparer() {
int source;
int length;
vdc.setBATSize(vdc.BAT_32x32);
vdc.initScreen();
resource.mapToMPR2(.font);
source = 0x4000 + resource.getOffset(.font);
length = resource.getLength(.font);
vdc.writeToVRAM(source, 0x1000, length);
vce.setPalette(0, 0, RGB(0, 0, 7));
vce.setPalette(0, 1, RGB(7, 7, 7));
vdc.clearScreen();
vdc.setFontColor(0);
}
// Un texte puis un nombre, à la ligne y
void ligne(ubyte y, uint texte, uint valeur, ubyte chiffres) {
vdc.setCursor(9, y);
vdc.print(texte);
nombres.enChiffres(valeur, @.tampon, chiffres);
vdc.print(@.tampon);
}
}
À retenir
- Une unit s'écrit dans un fichier
.s8u, s'ajoute avecaddunit, et porte le nom de son fichier, à la majuscule près. - Depuis une autre unit :
Unit.variable,Unit.fonction(). Pour modifier une unit, appelez ses fonctions. - Une bibliothèque s'écrit dans un fichier
.s8l, se déclare aveclibrary("nom"), se charge avecaddlib dossier.fichier;, et s'appelle par le nom delibrary. - Vos bibliothèques vont dans
userlibs/(un projet) ou dans le dossier deS8_USER_LIBS(tous vos projets). - Une bibliothèque qui touche au matériel déclare son architecture ; une bibliothèque générique n'en a pas, et ne contient que du Simple8. L'avertissement « Architecture declaration is different from project » ne s'ignore pas.
- Séparer ce qui dépend de la machine de ce qui n'en dépend pas rend un programme portable.
À vous de jouer
- Ajoutez un niveau à
Partie(une variableniveauet une fonctionniveauSuivant()), et affichez-le.Voir la solutionMasquer la solution
Le niveau fait partie des règles du jeu : sa variable et sa fonction vont dans
Partie, à côté du score et des vies.nouvelle()le met à 1 au début de la partie, etniveauSuivant()le fait avancer. Comme le reste dePartie, ce code ne dépend d'aucune machine : le même fichier sert au C64 et à la PC Engine.Partie.s8u// Partie.s8u : les règles du jeu (sans rien de propre à une machine) unit Partie { const uint POINTS_PAR_GEMME = 250; uint score; ubyte vies; ubyte gemmes; ubyte niveau; // Une nouvelle partie void nouvelle() { .score = 0; .vies = 3; .gemmes = 0; .niveau = 1; } // Le joueur ramasse des gemmes void ramasser(ubyte n) { .gemmes = .gemmes + n; .score = .score + .POINTS_PAR_GEMME * n; } // Le joueur perd une vie void perdreVie() { if (.vies > 0) { .vies--; } } // Le joueur passe au niveau suivant void niveauSuivant() { .niveau++; } }Dans
main.s8p, on fait passer un niveau, puis on affiche une ligne de plus. Le programme principal litPartie.niveaudirectement : lire la variable d'une autre unit est permis. Pour la changer, il passe parPartie.niveauSuivant(), comme le conseille le chapitre.main.s8pmain() { Partie.nouvelle(); Partie.ramasser(7); Partie.perdreVie(); Partie.niveauSuivant(); Ecran.preparer(); Ecran.ligne(10, "SCORE ", Partie.score, 5); Ecran.ligne(12, "VIES ", Partie.vies, 1); Ecran.ligne(14, "GEMMES ", Partie.gemmes, 2); Ecran.ligne(16, "NIVEAU ", Partie.niveau, 2); }
Le niveau 2, après un appel à niveauSuivant(). Sur PC Engine, seule la ligne change :
Ecran.ligne(17, "NIVEAU ", Partie.niveau, 2);, sous les gemmes (ligne 15).Ecran.s8un'est touché sur aucune des deux machines : sa fonctionligne()sait déjà afficher un texte et un nombre. - Créez une unit
Bonus.s8uavec une fonction qui donne 1 000 points par vie restante, en appelantPartie.ajouter()que vous écrirez.Voir la solutionMasquer la solution
La nouvelle unit ne contient que des règles, sans rien de propre à une machine : le même fichier
Bonus.s8usert au C64 et à la PC Engine. Elle lit le nombre de vies dansPartie, mais ne touche pas elle-même au score : elle demande àPartiede l'augmenter, par la fonctionajouter(). C'estPartiequi change ses propres données.Bonus.s8u// Bonus.s8u : le bonus de fin de partie (sans rien de propre à une machine) unit Bonus { const uint POINTS_PAR_VIE = 1000; // 1 000 points par vie restante, ajoutés au score de la partie void compter() { Partie.ajouter(.POINTS_PAR_VIE * Partie.vies); } }Dans
Partie.s8u, on ajoute la fonctionajouter(). Elle pourra servir à d'autres occasions : un trésor, un ennemi vaincu…Partie.s8u// Des points en plus void ajouter(uint points) { .score = .score + points; }Enfin,
main.s8pajoute la unit et compte le bonus en fin de partie (ici celui du C64 ; sur PC Engine, seuls les numéros de ligne de l'affichage diffèrent) :main.s8paddunit "Partie.s8u"; addunit "Bonus.s8u"; addunit "Ecran.s8u"; unit { main() { Partie.nouvelle(); Partie.ramasser(7); Partie.perdreVie(); Bonus.compter(); // 2 vies : 2 000 points de plus Ecran.preparer(); Ecran.ligne(10, "SCORE ", Partie.score, 5); Ecran.ligne(12, "VIES ", Partie.vies, 1); Ecran.ligne(14, "GEMMES ", Partie.gemmes, 2); } }Le score passe de 1 750 (7 gemmes à 250 points) à 3 750 : 2 vies restantes, 2 000 points de plus.

Le bonus compté, sur PC Engine. Pourquoi passer par
ajouter()plutôt que d'écrirePartie.score = Partie.score + …dansBonus? Nous avons essayé les deux. L'affectation directe compile tant queBonusest ajoutée aprèsPartie; si l'on inverse les deuxaddunit, le compilateur refuse :Variable 'Partie.score' not found !. Avecajouter(), l'ordre desaddunitn'a plus d'importance : le programme donne 3 750 dans les deux cas. - Ajoutez à la bibliothèque
nombresune fonction qui n'écrit pas les zéros de tête.Voir la solutionMasquer la solution
La nouvelle fonction suit la même idée que l'exercice 3 du chapitre 5 : on parcourt les chiffres de gauche à droite, et on n'écrit un chiffre que s'il n'est pas nul, ou si un chiffre a déjà été écrit (les zéros du milieu, comme dans 1050, doivent rester), ou si c'est le dernier (pour que 0 s'écrive
0). Ici, pas besoin d'une variable de plus pour savoir si l'écriture a commencé :k, le nombre de chiffres déjà rangés, suffit. Tant qu'il vaut 0, rien n'a été écrit.Comme
enChiffres, la fonction n'affiche rien : elle range les chiffres dans un tableau, et la bibliothèque reste générique. On part directement de 10 000, car unuinta au plus 5 chiffres (65 535) ; le paramètrechiffresdevient inutile.userlibs/nombres/nombres.s8l// Écrit les chiffres de valeur dans dest, sans zéros devant, puis le 0 de fin void enChiffresCourt(uint valeur, ubyte *dest) { uint diviseur = 10000; // un uint a au plus 5 chiffres (65 535) ubyte k = 0; // le nombre de chiffres déjà écrits while (diviseur > 0) { ubyte chiffre = (ubyte)((valeur / diviseur) % 10); // on écrit dès le premier chiffre non nul, et toujours le dernier (pour 0) if (chiffre != 0 || k > 0 || diviseur == 1) { dest[k] = (ubyte)('0' + chiffre); k++; } diviseur = diviseur / 10; } dest[k] = 0; }Pour s'en servir,
Ecran.s8ureçoit une fonction d'affichage à trois paramètres. Le tableautampon, de 6 cases, a la place pour 5 chiffres et le 0 de fin.Ecran.s8u// Un texte puis un nombre sans zéros devant, à la ligne y void ligneCourte(ubyte y, uint texte, uint valeur) { vic.setCursor(13, y); vic.print(texte); nombres.enChiffresCourt(valeur, @.tampon); vic.print(@.tampon); }main.s8pEcran.preparer(); Ecran.ligneCourte(10, "SCORE ", Partie.score); Ecran.ligneCourte(12, "VIES ", Partie.vies); Ecran.ligneCourte(14, "GEMMES ", Partie.gemmes);
Les nombres sans leurs zéros de tête. Nous avons aussi vérifié les cas limites, en affichant quelques valeurs de plus : 0 s'écrit
0, 40 s'écrit40, 10005 garde ses zéros du milieu, et 65535 s'écrit en entier. Sur PC Engine,nombres.s8lest le même fichier ; dansEcran.s8u, la fonction est la même avecvdcet la colonne 9. - Renommez
Ecran.s8uenAffichage.s8u: que faut-il changer d'autre ?Voir la solutionMasquer la solution
Le nom d'une unit est celui de son fichier : en renommant le fichier, on renomme la unit. Il faut donc changer, en plus du nom du fichier :
- la ligne
addunit "Ecran.s8u";demain.s8p, qui devientaddunit "Affichage.s8u";; - la déclaration dans le fichier :
unit Ecran {devientunit Affichage {; - chaque appel
Ecran.preparer()etEcran.ligne(…), qui devientAffichage.preparer()etAffichage.ligne(…), partout dans le projet (ici, seulement dansmain.s8p).
Le compilateur guide pas à pas. Nous avons renommé le fichier seul, puis corrigé une chose à la fois :
Ce qui a été changé Message du compilateur le nom du fichier seul addunit "Ecran.s8u": file not found (…)et la ligne addunitUnit name mismatch: file 'Affichage.s8u' must declare 'unit Affichage {' but found 'unit Ecran {'et la déclaration unitFunction 'preparer' not found !, puis trois foisFunction 'ligne' not found !et les appels aucun : 0 error(s) Le programme principal devient :
main.s8paddunit "Partie.s8u"; addunit "Affichage.s8u"; unit { main() { Partie.nouvelle(); Partie.ramasser(7); Partie.perdreVie(); Affichage.preparer(); Affichage.ligne(10, "SCORE ", Partie.score, 5); Affichage.ligne(12, "VIES ", Partie.vies, 1); Affichage.ligne(14, "GEMMES ", Partie.gemmes, 2); } }Rien d'autre ne change :
Partie.s8uetnombres.s8lne connaissent pasEcran, etfont.bin, sur PC Engine, reste à côté du fichier. L'écran obtenu est exactement le même qu'avant, sur les deux machines. Pensez aussi au commentaire en tête du fichier, qui citait l'ancien nom : le compilateur, lui, ne le vérifie pas. - la ligne
