-
Nicolas M. Thiéry authoredNicolas M. Thiéry authored
- Modularité, compilation séparée
- Compilation séparée
- Exemple
- Exemple : une bibliothèque max simpliste
- Exemple : une bibliothèque max dans les règles
- Exemple : deux programmes utilisant la bibliothèque max
- Exemple : les tests de la bibliothèque max
- Qu'avons-nous vu ?
- Déclaration de fonctions
- Compilation séparée (1)
- Compilation séparée (2)
- Fichiers d'entête
- Utilisation d'un fichier d'entête
- Résumé
- Résumé : implantation d'une bibliothèque en C++
- Résumé : utilisation d'une bibliothèque en C++
- Suite
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 :
:tags: []
int monMax(int a, int b) {
if ( a >= b )
return a;
else
return b;
}
monMax(10, 1)
+++ {"slideshow": {"slide_type": "slide"}, "tags": []}
programme1.cpp : maximum de deux entiers, avec un exemple
#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 : maximum de deux entiers, avec interactivité
#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"}}
max
simpliste
Exemple : une bibliothèque Contenu du fichier max_simpliste.hpp
:
/** 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 :
#include "max_simpliste.hpp"
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"}}
max
dans les règles
Exemple : une bibliothèque +++ {"slideshow": {"slide_type": "fragment"}}
% TODO: ici et dans les pages suivantes: use literalinclude
Contenu du fichier max.hpp :
/** 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 :
#include "max.hpp"
int monMax(int a, int b) {
if ( a >= b )
return a;
else
return b;
}
+++ {"slideshow": {"slide_type": "slide"}}
max
Exemple : deux programmes utilisant la bibliothèque +++ {"slideshow": {"slide_type": "fragment"}}
Contenu du fichier programme1.cpp:
#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:
#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"}}
max
Exemple : les tests de la bibliothèque +++ {"slideshow": {"slide_type": "fragment"}}
Contenu du fichier max-test.cpp :
#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
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}
+++ {"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 et programme.cpp.
+++ {"slideshow": {"slide_type": "fragment"}}
On les compile séparément avec :
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.
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 :
./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}
- 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
int monMax(int a, int b);
:::
+++ {"slideshow": {"slide_type": "fragment"}}
Utilisation d'un fichier d'entête
:::{admonition} Syntaxe
#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
#include <iostream> // fichier d'entête standard
#include "max.hpp" // fichier d'entête perso
:::
+++ {"slideshow": {"slide_type": "fragment"}}
:::{admonition} Compilation
clang++ -c max.cpp
clang++ -c programme1.cpp
clang++ max.o programme1.o -o programme1
:::
+++ {"slideshow": {"slide_type": "fragment"}}
:::{admonition} En une seule étape
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.