La compilation séparée

Nous venons de voir comment inclure une bibliothèque avec la pseudo-instruction #include et le fichier que nous importons ainsi est en général un fichier avec l’extension .h ou .hpp (spécifique à C++).

Contrairement à Java où nous n’avons qu’un fichier .java et où nous utilisons les modificateurs public, protected ou private pour indiquer ce qui est privé ou public, une bibliothèque C/C++ a besoin de 2 fichiers. Un fichier .c ou .cpp avec l’implémentation et un autre fichier .h ou .hpp avec la déclaration des éléments publics.

Les extensions .c et .h s’utilisent pour un fichier source en C et les extensions .cpp et .hpp pour un fichier en C++.

Note

Certaines bibliothèques en C++ utilisent aussi l’extension .h et parfois même aucune extension, mais dans le contexte de ce cours, nous utiliserons l’extension .hpp pour les fichiers en C++.

Illustrons ceci avec un exemple. Supposons que nous souhaitons mettre à disposition une bibliothèque avec une fonction qui calcule le maximum de deux nombres. La fonction ressemblerait à ça :

int max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

Pour pouvoir importer cette fonction dans un programme, nous écrivons un fichier .hpp (par exemple max.hpp) avec le contenu suivant :

#pragma once
int max(int a, int b);

On appelle ces fichiers des header files (d’où l’extension .h) et ils ne contiennent que les entêtes (ou les interfaces) des procédures.

Notez la directive #pragma once qui est une pseudo-instruction du préprocesseur. cette directive permet de s’assurer que le fichier n’est importé qu’une seule fois, évitant ainsi des erreurs de compilation.

On implémente ensuite la fonction dans un fichier .cpp (par exemple max.cpp) avec le contenu suivant.

#include "max.hpp"

int max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

Notez que l’implémentation importe sa propre interface (le fichier max.cpp importe max.hpp). Ce n’est pas toujours obligatoire, mais c’est une pratique très courante qui permet au compilateur de vérifier que l’implémentation correspond bien à l’interface.

Une application se compose souvent de plusieurs fichiers .c, .cpp / .h .hpp pour les bibliothèques locales ainsi qu’un fichier dans lequel est implémentée la méthode int main(). Vous pouvez appeler ce fichier comme vous voulez, mais on l’appelle souvent main.c ou main.cpp. La méthode main sera appelée lorsque vous exécuterez votre programme. Notez que ce fichier ne s’accompagne pas d’un fichier .h ou .hpp, car c’est le programme principal et il n’est pas importé par d’autres fichiers.

Le C et le C++ sont compatibles jusqu’à un certain point. Une fonction écrite en C peut être utilisée par un programme en C++ si on lui ajoute le préfixe extern "C". Si vous voulez écrire une bibliothèque utilisable en C comme en C++, la bonne pratique consiste à implémenter le fichier .h de la manière suivante :

#pragma once
#ifdef __cplusplus
extern "C"
{
#endif

// Declare C functions here

#ifdef __cplusplus
}
#endif

Le préprocesseur ajoute ce qu’il faut si on importe ce fichier depuis C++ et ignore la déclaration extern "C" supplémentaire si on importe depuis C.