Newer
Older
---
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%
---
# 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**.
### Exemple
Considérons les trois programmes suivants :
+++ {"slideshow": {"slide_type": "fragment"}, "tags": []}
int monMax(int a, int b) {
if ( a >= b )
return a;
else
return b;
}
[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;
}
```
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
[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"}}
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.
#### Exemple : une bibliothèque `max` dans les règles
+++ {"slideshow": {"slide_type": "fragment"}}
% TODO: ici et dans les pages suivantes: use literalinclude
``` 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"}}
``` 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`
``` c++
#include <iostream>
using namespace std;
#include "max.hpp"
int main() {
cout << monMax(1, 3) << endl;
return 0;
}
```
+++ {"slideshow": {"slide_type": "fragment"}}
``` 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;
}
```
#### Exemple : les tests de la bibliothèque `max`
``` 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();
}
```
### Qu'avons-nous vu ?
+++ {"slideshow": {"slide_type": "fragment"}}
#### Déclaration de fonctions
+++ {"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
#### Compilation séparée (1)
- Un programme peut être composé de plusieurs ***fichiers source***
Contenu :
+++ {"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"}}
```
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`.
Il ne reste plus qu'à combiner ces deux bouts de programmes binaires
pour obtenir un programme complet.
```shell
Maintenant, on peut exécuter le programme obtenu autant de fois qu'on
le souhaite :
```shell
./programme
```
#### 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**
:::{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
Fichier `.hpp` (ou `.h` en C) contenant la *déclaration* des fonctions *définies* dans
le fichier `.cpp` correspondant
#### Utilisation d'un fichier d'entête
:::{admonition} Syntaxe
- Équivalent à copier-coller le contenu de `max.hpp` à l'emplacement du
`#include "max.hpp"`
- ♣ Géré par le préprocesseur (cpp)
- 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`, ...
### Résumé : implantation d'une bibliothèque en C++
- La déclaration de toutes les fonctions publiques
- *Avec leur documentation !*
- La définition de toutes les fonctions
- *Inclure le fichier .hpp !*
:::{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++
```c++
#include <iostream> // fichier d'entête standard
#include "max.hpp" // fichier d'entête perso
```
clang++ -c max.cpp
clang++ -c programme1.cpp
clang++ max.o programme1.o -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).