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.
Compilation séparée#
Exemple#
Considérons les trois programmes suivants :
Dans Jupyter :
int monMax(int a, int b) {
if ( a >= b )
return a;
else
return b;
}
monMax(10, 1)
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;
}
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;
}
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.
Exemple : une bibliothèque max
simpliste#
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;
}
Pour utiliser cette bibliothèque, il suffit de l” inclure (include directive) :
#include "max_simpliste.hpp"
monMax(1, 3)
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.
Exemple : une bibliothèque max
dans les règles#
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);
Contenu du fichier max.cpp :
#include "max.hpp"
int monMax(int a, int b) {
if ( a >= b )
return a;
else
return b;
}
Exemple : deux programmes utilisant la bibliothèque max
#
Contenu du fichier programme1.cpp:
#include <iostream>
using namespace std;
#include "max.hpp"
int main() {
cout << monMax(1, 3) << endl;
return 0;
}
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;
}
Exemple : les tests de la bibliothèque max
#
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();
}
Qu’avons-nous vu ?#
Déclaration de fonctions#
Syntaxe
int monMax(int a, int b);
Sémantique
Le programme définit (function definition) quelque part une fonction
monMax
avec cette signature (function signature) :
type des paramètres et type du résultatCette 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»
Indication
♣ Application Deux fonctions qui s’appellent réciproquement
Compilation séparée (1)#
Un programme peut être composé de plusieurs fichiers source (source file)
Extension :.cpp
Contenu :Des définitions de fonctions
Des variables globales, …
Chaque fichier source est compilé en un fichier objet (object file)
Extension :.o
Contenu :Le code binaire des fonctions, …
L” éditeur de liens (linker) combine plusieurs fichiers objet en un fichier exécutable (executable file).
Voyons cela pour un programme voulant utiliser la bibliothèque max
:
Les sources sont max.cpp et programme.cpp.
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
.
Il ne reste plus qu’à combiner ces deux bouts de programmes binaires pour obtenir un programme complet.
clang++ programme.o max.o -o programme
Maintenant, on peut exécuter le programme obtenu autant de fois qu’on le souhaite :
./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
Indication
♣ 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
Fichiers d’entête#
Définition : Fichier d’entête
Fichier .hpp
(ou .h
en C) contenant la déclaration des fonctions définies dans
le fichier .cpp
correspondant
Exemple : Fichier d’entête max.hpp
int monMax(int a, int b);
Utilisation d’un fichier d’entête#
Syntaxe
#include "max.hpp"
Sémantique
Utiliser la bibliothèque max
Indication
Implantation en C++
Équivalent à copier-coller le contenu de
max.hpp
à l’emplacement du#include "max.hpp"
♣ Géré par le préprocesseur (cpp)
Inclusion de fichiers d’entêtes standards#
Syntaxe
#include <iostream>
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èmeSous linux :
/usr/include
, …
Résumé#
Résumé : implantation d’une bibliothèque en C++#
Écrire un fichier d’entête (max.hpp)
La déclaration de toutes les fonctions publiques
Avec leur documentation !
Écrire un fichier source (max.cpp)
La définition de toutes les fonctions
Inclure le fichier .hpp !
Écrire un fichier de tests (maxTest.cpp)
Les fonctions de tests
Une fonction
main
lançant tous les tests
Résumé : utilisation d’une bibliothèque en C++#
Inclusion des entêtes
#include <iostream> // fichier d'entête standard
#include "max.hpp" // fichier d'entête perso
Compilation
clang++ -c max.cpp
clang++ -c programme1.cpp
clang++ max.o programme1.o -o programme1
En une seule étape
clang++ max.cpp programme1.cpp -o programme1
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.