--- jupytext: text_representation: extension: .md format_name: myst format_version: 0.13 kernelspec: display_name: C++17 language: C++17 name: xcpp17 rise: auto_select: first autolaunch: false centered: false controls: false enable_chalkboard: true height: 100% margin: 0 maxScale: 1 minScale: 1 scroll: true slideNumber: true start_slideshow_at: selected transition: none width: 90% --- +++ {"slideshow": {"slide_type": "slide"}} # Modularité, compilation séparée Rappelez-vous notre **livre de recettes**. À l'époque, nous avons vu comment **découper un programme en fonctions** pour plus de **modularité**. Cela permet de mieux le comprendre, petit bout par petit bout, d'éviter les redites, etc. Nous allons de même **découper un programme en plusieurs fichiers**. +++ {"slideshow": {"slide_type": "slide"}} ## Compilation séparée +++ {"slideshow": {"slide_type": "fragment"}} ### Exemple Considérons les trois programmes suivants : +++ {"slideshow": {"slide_type": "fragment"}, "tags": []} Dans Jupyter : ```{code-cell} :tags: [] int monMax(int a, int b) { if ( a >= b ) return a; else return b; } ``` ```{code-cell} monMax(10, 1) ``` +++ {"slideshow": {"slide_type": "slide"}, "tags": []} [programme1.cpp](programme1.cpp) : maximum de deux entiers, avec un exemple ``` c++ #include <iostream> using namespace std; int monMax(int a, int b) { if ( a >= b ) return a; else return b; } int main() { cout << monMax(1, 3) << endl; return 0; } ``` +++ {"slideshow": {"slide_type": "slide"}, "tags": []} [programme2.cpp](programme2.cpp) : maximum de deux entiers, avec interactivité ``` c++ #include <iostream> using namespace std; int monMax(int a, int b) { if ( a >= b ) return a; else return b; } int main() { cout << "Entrez a et b:" << endl; int a, b; cin >> a >> b; cout << "Le maximum est: " << monMax(a, b) << endl; return 0; } ``` +++ {"slideshow": {"slide_type": "slide"}} On constate une **répétition** : les trois programmes définissent exactement la même fonction `monMax`, qu'ils utilisent ensuite différemment. Pourrait-on **partager** la fonction `monMax` entre ces trois programmes ? C'est ce que nous allons faire en définissant une mini-bibliothèque. Voyons à quoi cela ressemble. +++ {"slideshow": {"slide_type": "slide"}} #### Exemple : une bibliothèque `max` simpliste Contenu du fichier `max_simpliste.hpp` : ``` c++ /** La fonction max * @param x, y deux entiers * @return un entier, * le maximum de x et de y **/ int monMax(int a, int b) { if ( a >= b ) return a; else return b; } ``` +++ {"slideshow": {"slide_type": "fragment"}} Pour utiliser cette bibliothèque, il suffit de l'******inclure****** : ```{code-cell} #include "max_simpliste.hpp" ``` ```{code-cell} monMax(1, 3) ``` +++ {"slideshow": {"slide_type": "fragment"}} :::{attention} On appelle cela une bibliothèque en ******entêtes seuls****** (******header only******). En `C++`, il y a des cas d'usage où cela peut être pertinent. Il y a de sérieuses limitations à cette façon de structurer une bibliothèque. Dans ce cours on évitera. ::: +++ {"slideshow": {"slide_type": "slide"}} #### Exemple : une bibliothèque `max` dans les règles +++ {"slideshow": {"slide_type": "fragment"}} % TODO: ici et dans les pages suivantes: use literalinclude Contenu du fichier [max.hpp](max.hpp) : ``` c++ /** La fonction max * @param x, y deux entiers * @return un entier, * le maximum de x et de y **/ int monMax(int a, int b); ``` +++ {"slideshow": {"slide_type": "fragment"}} Contenu du fichier [max.cpp](max.cpp) : ``` c++ #include "max.hpp" int monMax(int a, int b) { if ( a >= b ) return a; else return b; } ``` +++ {"slideshow": {"slide_type": "slide"}} #### Exemple : deux programmes utilisant la bibliothèque `max` +++ {"slideshow": {"slide_type": "fragment"}} Contenu du fichier [programme1.cpp](programme1.cpp): ``` c++ #include <iostream> using namespace std; #include "max.hpp" int main() { cout << monMax(1, 3) << endl; return 0; } ``` +++ {"slideshow": {"slide_type": "fragment"}} Contenu du fichier [programme2.cpp](programme2.cpp): ``` c++ #include <iostream> using namespace std; #include "max.hpp" int main() { cout << "Entrez a et b :" << endl; int a, b; cin >> a >> b; cout << "Le maximum est : " << monMax(a, b) << endl; return 0; } ``` +++ {"slideshow": {"slide_type": "slide"}} #### Exemple : les tests de la bibliothèque `max` +++ {"slideshow": {"slide_type": "fragment"}} Contenu du fichier [max-test.cpp](max-test.cpp) : ``` c++ #include <iostream> using namespace std; #include "max.hpp" /** Infrastructure minimale de test **/ #define CHECK(test) if (!(test)) cerr << "Test failed in file " << __FILE__ << " line " << __LINE__ << ": " #test << endl void monMaxTest() { CHECK( monMax(2,3) == 3 ); CHECK( monMax(5,2) == 5 ); CHECK( monMax(1,1) == 1 ); } int main() { monMaxTest(); } ``` +++ {"slideshow": {"slide_type": "slide"}} ### Qu'avons-nous vu ? +++ {"slideshow": {"slide_type": "fragment"}} #### Déclaration de fonctions +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} Syntaxe ``` c++ int monMax(int a, int b); ``` ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} Sémantique - Le programme ******définit****** quelque part une fonction `monMax` avec cette ******signature****** : type des paramètres et type du résultat - Cette définition n'est pas forcément dans le même fichier - Si cette définition n'existe pas ou n'est pas unique, une erreur est déclenchée par le compilateur - Cette erreur est déclenchée au moment où l'on combine les différents fichiers : voir plus loin «Édition de liens» +++ {"slideshow": {"slide_type": "fragment"}} :::{hint} ♣ Application Deux fonctions qui s'appellent réciproquement ::: +++ {"slideshow": {"slide_type": "slide"}} #### Compilation séparée (1) - Un programme peut être composé de plusieurs ***fichiers source*** Contenu : - Des définitions de fonctions - Des variables globales, ... +++ {"slideshow": {"slide_type": "fragment"}, "tags": []} - Chaque fichier source est compilé en un ***fichier objet*** (extension : .o) Contenu : - Le code binaire des fonctions, ... +++ {"slideshow": {"slide_type": "fragment"}, "tags": []} - L'******éditeur de liens****** combine plusieurs fichiers objet en un ******fichier exécutable****** +++ {"slideshow": {"slide_type": "fragment"}} Voyons cela pour un programme voulant utiliser la bibliothèque `max` : Les sources sont [max.cpp](max.cpp) et [programme.cpp](programme.cpp). +++ {"slideshow": {"slide_type": "fragment"}} On les compile séparément avec : ```shell clang++ -c max.cpp clang++ -c programme.cpp ``` Cela produit les fichiers objets `max.o` et `programme.o`. Chacun est un bout incomplet de programmes binaires : `max.o` contient le code binaire de la fonction `max` mais pas la fonction `main`, et réciproquement pour `programme.o`. +++ {"slideshow": {"slide_type": "fragment"}} Il ne reste plus qu'à combiner ces deux bouts de programmes binaires pour obtenir un programme complet. ```shell clang++ programme.o max.o -o programme ``` +++ {"slideshow": {"slide_type": "fragment"}} Maintenant, on peut exécuter le programme obtenu autant de fois qu'on le souhaite : ```shell ./programme ``` +++ {"slideshow": {"slide_type": "slide"}} #### Compilation séparée (2) **Au moment de l'édition de lien :** - Chaque fonction utilisée **doit être définie une et une seule fois** - La fonction `main` **doit être définie une et une seule fois** +++ {"slideshow": {"slide_type": "fragment"}} :::{hint} ♣ Quelques variantes autour des fichiers objets - Bibliothèques (.a) : Une archive contenant plusieurs fichiers objets .o - Bibliothèques dynamiques (.so) : Édition de lien dynamique au lancement du programme ::: +++ {"slideshow": {"slide_type": "slide"}} #### Fichiers d'entête :::{prf:definition} Fichier d'entête Fichier `.hpp` (ou `.h` en C) contenant la *déclaration* des fonctions *définies* dans le fichier `.cpp` correspondant ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{prf:example} Fichier d'entête `max.hpp` ``` c++ int monMax(int a, int b); ``` ::: +++ {"slideshow": {"slide_type": "fragment"}} #### Utilisation d'un fichier d'entête :::{admonition} Syntaxe ``` c++ #include "max.hpp" ``` ::: :::{admonition} Sémantique Utiliser la bibliothèque `max` ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{hint} Implantation en C++ - Équivalent à copier-coller le contenu de `max.hpp` à l'emplacement du `#include "max.hpp"` - ♣ Géré par le préprocesseur (cpp) ::: +++ {"slideshow": {"slide_type": "slide"}} #### Inclusion de fichiers d'entêtes standards :::{admonition} Syntaxe ``` c++ #include <iostream> ``` ::: :::{admonition} Sémantique - Charge la déclaration de toutes les fonctions définies dans la bibliothèque standard `iostream` de C++ - Le fichier `iostream` est recherché dans les répertoires standards du système - Sous linux : `/usr/include`, ... ::: +++ {"slideshow": {"slide_type": "skip"}, "tags": []} ## Résumé +++ {"slideshow": {"slide_type": "slide"}, "tags": []} ### Résumé : implantation d'une bibliothèque en C++ +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} Écrire un fichier d'entête (max.hpp) - La déclaration de toutes les fonctions publiques - *Avec leur documentation !* ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} Écrire un fichier source (max.cpp) - La définition de toutes les fonctions - *Inclure le fichier .hpp !* ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} Écrire un fichier de tests (maxTest.cpp) - Les fonctions de tests - Une fonction `main` lançant tous les tests ::: +++ {"slideshow": {"slide_type": "slide"}} ### Résumé : utilisation d'une bibliothèque en C++ :::{admonition} Inclusion des entêtes ```c++ #include <iostream> // fichier d'entête standard #include "max.hpp" // fichier d'entête perso ``` ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} Compilation ```shell clang++ -c max.cpp clang++ -c programme1.cpp clang++ max.o programme1.o -o programme1 ``` ::: +++ {"slideshow": {"slide_type": "fragment"}} :::{admonition} En une seule étape ```shell clang++ max.cpp programme1.cpp -o programme1 ``` ::: +++ {"slideshow": {"slide_type": "skip"}, "tags": []} ## Suite Après cette discussion des notions de modularité et de compilation séparée, passons à quelques [digressions sur la surcharge, les templates et les espaces de noms](cours-digressions.md).