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 JupyterHub.

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

TODO 2023-2024:

TODO 2024-2025:

  • [ ] Revoir les coordonnées dans l’exercice 2 du TD et premier-dessin.md pour réduire un peu la résolution.

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#

TD : compilation séparée, graphiques#

TP#

Exercice 0 : préliminaires : exercices d’entraînement#

À faire

Mettre à jour les lien pour les exercices d’entraînement dans Exercices/

  1. Effectuez quelques exercices sur les prototypes de fonction de la feuille entraîneur/entraîneur.md.

Vous pourrez, pour réviser, travailler les exercices sur les autres thèmes à la maison, en fonction de vos besoins.

Exercice 1 : préliminaires : compilation séparée#

  1. Consultez le contenu des fichiers suivants : factorielle.hpp, factorielle.cpp, factorielle-exemple.cpp.

  2. 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 nous a créé deux fichiers, factorielle.o et factorielle-exemple.o qui sont des bouts de programme binaires. Vérifiez avec ls que 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 ls que cette commande crée bien un exécutable factorielle-exemple.

  3. Exécutez le programme factorielle-exemple.

  4. Consultez le fichier factorielle-test.cpp. Créez un exécutable factorielle-test en adaptant les étapes ci-dessus, puis testez ce nouveau programme.

  5. Une autre méthode pour compiler factorielle-exemple est 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.o et factorielle-exemple de votre dossier avec rm. 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 .o mais uniquement de l’exécutable factorielle-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 .hpp dans tous les .cpp (ou inclure factorielle.hpp au lieu de fibonacci.hpp, ou mettre des < > au lieu des " ").

  • Faire un .hpp qui contient uniquement la documentation de la fonction mais pas son en-tête.

  • Certains laissent le lancement des tests dans le main de fibonacci-exemple.cpp ce 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.cpp et regardez son contenu. Remarquez que la fonction main mélange deux actions de nature très différente : d’une part lancer les tests de la fonction fibonacci, et d’autre part utiliser cette fonction pour interagir avec l’utilisateur. Ceci n’est pas très satisfaisant.

  • Réorganisez le fichier fibonacci.cpp en 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

END SOLUTION

  • Quels exécutables allez-vous construire? Pour chaque exécutable, quels fichiers allez-vous 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 combiner fibonacci.cpp et fibonacci-exemple.cpp. Pour le second, on combine fibonacci.cpp et fibonacci-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#

  1. 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#

Attention

SFML et sessions 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 de session graphique sous UNIX).

Option 1 : Sur les ordinateurs des salles de TP

Vous êtes automatiquement dans une session graphique. Vous n’avez donc rien de particulier à faire.

  1. Ouvrez les fichiers exemple-graphisme1.cpp et primitives.hpp et consultez le premier.

  2. Compilez ce programme depuis le terminal avec (en une seule ligne !) :

    info-111 compile exemple-graphisme1.cpp primitives.cpp -o exemple-graphisme1 -lsfml-system -lsfml-window -lsfml-graphics
    

    Explication : le code binaire de SFML est réparti dans trois bibliothèques sfml-system, sfml-window et sfml-graphics. Les arguments -lsfml-system, … indiquent au compilateur de les lier au programme (-l pour link).

  3. 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.

  4. Compilez le programme fourni premier-dessin.cpp en adaptant la commande utilisée plus haut pour compiler exemple-graphisme1.cpp. Complétez premier-dessin.cpp à partir de la feuille premier-dessin. N’hésitez pas à changer la valeur de la variable delai pour 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 l’option -lsfml*, ce qui crée plein d’erreurs.

Exercice 5 : Souris et clavier ♣#

  1. Pour vous donner une idée de l’utilisation de la SFML et de notre bibliothèque de primitives, lisez attentivement primitives.hpp et sa documentation.

  2. Consultez ensuite exemple-graphisme2.cpp et exemple-graphisme3.cpp pour en voir des exemples d’utilisation.

  3. 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 du Yams ♣#

Reprenez le jeu du 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.