Semaine 10 : modularité, compilation séparée#
Notes aux enseignants
Avec la compilation séparée, et surtout les graphismes, cette semaine est un peu technique. Il est très important de tester le TP en salle TP et sur MyDocker.
Il peut y avoir un problème de bibliothèque libudev.so.0 manquante. Ce
problème peut être contourné avec :
mamba install -c conda-forge libudev
cd $CONDA_PREFIX/lib
ln -s libudev.so.1 libudev.so.0
À faire
2026-2027
[ ] automatiser l'ajout du lien pour libudev (fait sur le hub)
[ ] Bien tout tester sur mydocker
Objectifs pédagogiques#
Cette semaine, comme la précédente, notre objectif est de gérer de « gros » programmes, notamment pour continuer à vous préparer au projet. Nous avions eu un premier aperçu du concept de modularité, en découpant un programme en fonctions. Nous allons en rajouter une couche en découpant un programme en plusieurs fichiers, grâce à la compilation séparée. En prélude, nous discuterons le cycle de vie d'un programme, notamment pour clarifier ce qu'est la compilation. Comme application nous utiliserons la bibliothèque multimédia SFML pour faire des dessins et des interfaces graphiques. Cela pourra servir pour certaines parties optionnelles du projet.
En TD et TP, nous mettrons d'abord en application la compilation séparée, observant en détail un programme découpé en plusieurs fichiers, puis reproduisant le schéma pour découper un autre programme en plusieurs fichiers. Puis nous étudierons quelques exemples de programmes utilisant la bibliothèque SFML et nous en inspirerons pour programmer des dessins et interagir avec l'utilisateur.
Cours#
Cours : Cycle de vie d'un programme
Cours : Conclusion
TD : compilation séparée, graphiques#
TP#
Exercice 0 : préliminaires : sauvegarde du projet et exercices d'entraînement#
À faire en TP
Déposez votre projet depuis le tableau de bord pour en faire une sauvegarde.
Téléchargez le devoir « Entraînement » depuis le tableau de bord pour le mettre à jour.
Ouvrez-le et effectuez un exercice du thème Tableaux 2D : bases.
À faire à la maison
Travaillez les autres exercices de ce thème, ainsi que ceux sur les autres thèmes (dont le thème Flux et fichiers), en fonction de vos besoins.
Exercice 1 : préliminaires : compilation séparée#
Consultez le contenu des fichiers suivants :
factorielle.hpp,factorielle.cpp,factorielle-exemple.cpp.Compilez le programme entier en suivant les instructions suivantes. Commencez par compiler chacun des bouts de programme (fichiers
.cpp). Pour cela on utilise l'option-c:clang++ -c factorielle.cpp clang++ -c factorielle-exemple.cpp
Ceci a créé deux fichiers,
factorielle.oetfactorielle-exemple.oqui sont des bouts de programme binaires. Vérifiez aveclsque ces fichiers ont bien été créés.Combinez ensuite (en anglais : link pour « édition de liens ») ces deux bouts de programme de la façon suivante :
clang++ factorielle.o factorielle-exemple.o -o factorielle-exemple
Vérifiez avec
lsque cette commande crée bien un exécutablefactorielle-exemple.Exécutez le programme
factorielle-exemple.Consultez le fichier
factorielle-test.cpp. Créez un exécutablefactorielle-testen adaptant les étapes ci-dessus, puis testez ce nouveau programme.Une autre méthode pour compiler
factorielle-exempleest de remplacer les trois commandes de la question (2) par la seule commande :clang++ factorielle.cpp factorielle-exemple.cpp -o factorielle-exemple
Supprimez les fichiers
factorielle.o,factorielle-exemple.oetfactorielle-exemplede votre dossier avecrm. Testez alors la commande précédente. Quel(s) fichier(s) ont été créés ? En déduire les différences avec la méthode précédente. Selon le cas vous pourrez être amené à choisir l'une ou l'autre, notamment dans le projet.
BEGIN SOLUTION
Il n'y a pas eu création de fichiers
.omais uniquement de l'exécutablefactorielle-exemple. Cette commande compile et combine d'un seul coup.
END SOLUTION
Exercice 2 : compilation séparée#
Notes aux enseignants
Erreurs fréquentes :
Faute d'orthographe à fibonacci (typiquement nombre de n ou nombre de c) qui fait que le fichier n'est pas trouvé.
Ne pas mettre l'include du
.hppdans tous les.cpp(ou inclurefactorielle.hppau lieu defibonacci.hpp, ou mettre des< >au lieu des" ").Faire un
.hppqui contient uniquement la documentation de la fonction mais pas son en-tête.Certains laissent le lancement des tests dans le
maindefibonacci-exemple.cppce qu'ils ne sont pas censés faire (cf. le modèle donné pour factorielle) et pose le problème de la déclaration de la fonction de test.
Ouvrez le fichier
fibonacci.cppet regardez son contenu. Remarquez que la fonctionmainmélange deux actions de nature très différente : d'une part lancer les tests de la fonctionfibonacci, et d'autre part utiliser cette fonction pour interagir avec l'utilisateur. Ceci n'est pas très satisfaisant.Réorganisez le fichier
fibonacci.cppen plusieurs fichiers en suivant le modèle de l'exercice précédent. Il y aura donc quatre fichiers :Faites attention à ne pas dupliquer de code.
BEGIN SOLUTION
On réorganise les fichiers comme suit. Notez bien les
#include "fibonacci.hpp"au début de chaque fichier.cpp.
END SOLUTION
A partir de ces fichiers, quels exécutables peut-on construire ? Pour chaque exécutable, quels fichiers faut-il combiner pour l'obtenir ? Vérifiez pour chaque exécutable que chaque fonction utilisée (dont la fonction
main) est définie une et une seule fois dans l'ensemble de fichiers correspondant.
BEGIN SOLUTION
On construit autant d'exécutables qu'il y a de fonctions
main: un pour lancer l'exemple et un pour lancer les tests automatiques. Dans les deux cas, on doit combiner le fichier contenant le code de la bibliothèque et le fichier qui l'utilise. Ainsi pour obtenir le premier, on doit combinerfibonacci.cppetfibonacci-exemple.cpp. Pour le second, on combinefibonacci.cppetfibonacci-test.cpp.
END SOLUTION
Compilez chacun des deux programmes (test et exemple) grâce à la compilation séparée, exécutez les et vérifiez que tout fonctionne correctement. En cas d'erreur, lisez le message d'erreur puis comparez attentivement vos fichiers et commandes avec ceux donnés pour la fonction
factorielle(si vous avez la feuille de TD sous la main, l'énoncé de l'exercice 1 permet de visualiser facilement tous les fichiers pour factorielle).Notez ci-dessous les commandes utilisées pour la compilation :
BEGIN SOLUTION
On compile les deux programmes avec les commandes suivantes :
clang++ fibonacci.cpp fibonacci-exemple.cpp -o fibonacci-exemple clang++ fibonacci.cpp fibonacci-test.cpp -o fibonacci-test
END SOLUTION
Exercice 3 : Premiers graphiques avec Jupyter#
Refaites l'exercice 2 du TD en complétant la feuille premier-dessin. Implantez chacun des items en vérifiant à chaque fois le résultat.
Exercice 4 : Premiers graphiques avec SFML#
SFML et interfaces graphiques
Pour exécuter un programme utilisant la bibliothèque SFML, il est nécessaire d'être dans une interface graphique ; sinon, vous aurez une erreur comme :
./exemple-graphisme1
Failed to open X11 display; make sure the DISPLAY environment variable is set correctly
(X11 est le gestionnaire d'interfaces graphique sous UNIX).
Sur myDocker@Paris-Saclay, depuis tout autre ordinateur ou tablette connectée à Internet
En parallèle de l'interface usuelle JupyterLab, vous pouvez ouvrir un environnement de bureau (dite XFCE Desktop) avec une interface graphique. Vous pourrez ensuite basculer entre JupyterLab pour éditer vos programmes, et le terminal de XFCE pour les compiler et les exécuter.
Attention
Assurez-vous de compiler les programmes depuis le terminal de l'environnement de bureau pour que les binaires produits soient compatibles avec ce dernier.
Accédez à l'environnement de bureau sur myDocker.
Cliquez sur « Connexion à l'interface ».
Ouvrez un terminal depuis la barre d'outils en bas.
Déplacez-vous dans le dossier souhaité, par exemple avec :
cd ProgImperative/Semaine10
Sur votre machine personnelle
L'installation, la configuration et l'utilisation de la SFML peut être un peu technique selon la configuration de votre ordinateur (système d'exploitation, environnement de développement, ...). Nous donnons ici quelques pointeurs, mais après c'est à vous de fouiller la documentation, notamment sur le site de la SFML et de vous débrouiller. Dans le doute, utilisez plutôt l'option myDocker ci-dessus.
La section « Salle de TP virtuelle » sur la page logiciels du site du cours donne des instructions pour installer JupyterLab, compilateur C++ et SFML. Sous GNU/Linux (Ubuntu, ...) cela devrait être suffisant pour travailler.
Si vous souhaitez utiliser Code::Blocks, Visual Studio, ou tout autre environnement de développement intégré (IDE), il faudra configurer cet IDE pour utiliser la SFML. Plus encore que d'habitude, nous recommandons de compiler en ligne de commande dans le terminal.
Ouvrez le fichier
exemple-graphisme1.cppet consultez-le.Utilisez la commande suivante pour compiler ce programme avec la bibliothèque SFML et la bibliothèque
primitives.cpp. Veillez à bien saisir cette commande en une seule ligne :clang++ exemple-graphisme1.cpp primitives.cpp -o exemple-graphisme1 -Wno-narrowing -lsfml-system -lsfml-window -lsfml-graphics
Explications
Le code binaire de SFML est réparti dans trois bibliothèques
sfml-system,sfml-windowetsfml-graphics. Les arguments-lsfml-system, ... indiquent au compilateur de les lier au programme (-lpour link). En complément, l'option-Wno-narrowingindique de permettre un peu de souplesse dans les conversions entre entiers et flottants.À titre de raccourci, vous pouvez utiliser le raccourci suivant :
make exemple-graphisme1À propos de
makemakeest un utilitaire qui permet d'automatiser la compilation des programmes en la décrivant une fois pour toutes dans un fichier de configuration Makefile les commandes à lancer.makes'assure aussi de ne recompiler que ce qui doit l'être. Vous en explorez les détails au second semestre.Lancez le programme obtenu avec :
./exemple-graphisme1
Vous obtenez une fenêtre blanche, avec un (tout petit !) point rouge un peu à gauche, près du bord haut.
Lancez à nouveau le programme obtenu en cachant les messages d'erreur à propos du joystick :
./exemple-graphisme1 2>/dev/null
Compilez le programme fourni
premier-dessin.cppen adaptant la commande utilisée plus haut pour compilerexemple-graphisme1.cpp. Complétezpremier-dessin.cppà partir de la feuillepremier-dessin. N'hésitez pas à changer la valeur de la variabledelaipour voir le résultat s'afficher plus longtemps.
Notes aux enseignants
Certains élèves croient que le premier caractère de -lsfml-* est un
\(1\) alors qu'il s'agit d'un \(\ell\) (c'est vrai que ce n'est pas
forcément très clair mais cela se voit en comparant avec les autres
caractères de la ligne : on voit que ce n'est pas exactement le même
caractère que dans info-111 mais que c'est le même que dans sfml).
Certains élèves compilent le programme premier-dessin.cpp en oubliant
les options -lsfml*, ce qui crée plein d'erreurs.
Pour copier-coller la commande de compilation depuis l'énoncé vers le terminal de l'environnement de bureau, on peut copier la commande de compilation en cliquant sur l'icone copier à droite du texte qu'on veut copier, puis dans le terminal, clic droit, cliquer d'abord sur coller, puis cliquer sur paste. C'est un peu foireux. C'est plausiblement plus simple que les étudiants retapent la commande, ou utilisent la commande avec make.
Si un programme plante avec une fenêtre ouverte qu'on n'arrive pas à fermer, on peut cliquer sur le terminal puis taper Ctrl-C.
Pour les programme où on doit cliquer dans la fenêtre pour faire apparaître des segments, AP a observé que certains clics n'étaient pas pris en compte. Problème de souris non remarqué avant, ou de l'application dans MyDocker ?
Exercice 5 : Souris et clavier ♣#
Pour vous donner une idée de l'utilisation de la SFML et de notre bibliothèque de primitives, lisez attentivement
primitives.hppet sa documentation.Consultez ensuite
exemple-graphisme2.cppetexemple-graphisme3.cpppour en voir des exemples d'utilisation.Indication
Pensez à cacher comme ci-dessus les messages d'erreurs à propos du joystick s'ils deviennent trop nombreux.
Implantez l'exercice 4 du TD.
Notes aux enseignants
Erreur fréquente : oublier window.display().
Exercice 6 : Couche d'abstraction ♣#
Pour vous approprier la couche d'abstraction, consultez son
implantation dans primitives.cpp. En vous inspirant de
ce qui est déjà fait, complétez l'implantation de la fonction
draw_filled_rectangle (documentée dans primitives.hpp).
Vous pouvez vous aider de la
documentation en ligne de la SFML.
BEGIN SOLUTION
Voir primitives.cpp, lignes 42 à 48.
END SOLUTION
Exercice 7 : Jeu de Yams ♣#
Reprenez le jeu de Yams du TP 6 en ajoutant une interface graphique.
On affichera les dés (soit avec du texte, soit avec des points) dans
la fenêtre. Revoyez exemple-graphisme3.cpp
pour des fonctions rapides. L'utilisateur pourra cliquer sur les dés
à combiner pour former une figure. (Utiliser par exemple wait_mouse()
pour cliquer sur les dés, et wait_keyboard() pour valider.) Le nombre
de points sera ensuite affiché.
À vous de concevoir les fonctions à introduire pour décomposer le problème.