Utilisation de sources écrites C++ dans un programme en C

JEG

Le présent tutoriel explique comment utiliser des classes C++ dans un programme écrit en C et compilé avec GCC sous Linux, Unix, etc.

Merci d'apporter vos avis. ♪
Commentez

- Titre : Utilisation de sources écrites C++ dans un programme en C

- Auteur : JEG

- Parution : 16 février 2016

- Public visé :

- Licence : Copyright ® 2016 JEG. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Création des fichiers de la bibliothèque C++

- III. Compilation de la bibliothèque C++

- IV. Construction de la bibliothèque

- IV-A. Suppression d'une version précédente

- IV-B. Concaténation des fichiers objet

- IV-C. Liaison de la bibliothèque

- V. Utilisation de la bibliothèque dans un programme C++

- V-A. Création du fichier C++ du programme

- V-B. Compilation du programme

- V-C. Lancement du programme

- VI. Modification de la bibliothèque C++ pour être utilisée en C

- VII. Utilisation de la bibliothèque dans un programme C

- VII-A. Création du fichier C du programme

- VII-B. Compilation du programme

- VII-C. Lancement du programme

- VIII. Utilisation des classes dans un programme C, via la bibliothèque C++

- IX. Utilisation de plusieurs instances de classes dans un programme C, via la bibliothèque C++

I - Introduction

Le présent tutoriel explique comment utiliser des classes C++ dans un programme écrit en C et compilé avec GCC sous Linux, Unix, etc.

Le cas peut se présenter par exemple lorsque des fonctionnalités ont été écrites en C++ dans le cadre d'un autre projet et seraient réutilisables dans un nouveau projet écrit en C par une tierce personne. Plutôt que de réécrire entièrement l'une ou l'autre les parties, il est possible de rassembler toutes les sources C++ dans une bibliothèque spécifique offrant une interface pour les sources de l'application écrite en C.

II - Création des fichiers de la bibliothèque C++

Nous allons dans un premier temps créer un répertoire mylib et dans ce répertoire, nous allons créer deux fichiers simples que nous nommerons (par exemple) mylibtest.cpp et mylibtest.h.

mylibtest.cpp contiendra le code suivant :

1. #include "mylibtest.h"

3. #include <iostream>

5. void helloWorld()

6. {

7. std::cout << "hello" << std::endl;

8. }

et mylibtest.h contiendra celui-ci :

1. #ifndef MYLIBTEST_H

2. #define MYLIBTEST_H

4. void helloWorld();

6. #endif

donc le répertoire mylib contiendra :

1. mylibtest.cpp

2. mylibtest.h

III - Compilation de la bibliothèque C++

Plaçons-nous dans le répertoire mylib et compilons sans édition de liens (option -c) le(s) fichier(s) de la bibliothèque

1. g++ -c mylibtest.cpp

ou bien

1. find . -name "*.cpp" -exec g++ -c ‘{}' \;

si plusieurs fichiers .cpp sont à compiler.

Il doit y avoir maintenant autant de fichiers objets (avec extension .o) que de fichiers .cpp et chacun d'eux doit porter le même nom que le fichier .cpp auquel il se rapporte.

Dans notre exemple, le répertoire mylib contiendra :

1. mylibtest.cpp

2. mylibtest.h

3. mylibtest.o

IV - Construction de la bibliothèque

IV-A - Suppression d'une version précédente

Dans un premier temps nous allons supprimer toute éventuelle instance de cette bibliothèque qui aurait été préalablement créée.

1. rm -f libtest.a

IV-B - Concaténation des fichiers objet

Puis nous allons concaténer les fichiers objet compilés dans un seul fichier :

1. ar cr libtest.a fichier_obj1.o fichier_obj2.o, etc.

dans notre exemple cela sera :

1. ar cr libtest.a mylibtest.o

IV-C - Liaison de la bibliothèque

Nous allons enfin finaliser (en liant entre eux les objets de la bibliothèque) avec la commande ranlib

1. /bin/ranlib libtest.a

ou bien

1. /usr/bin/ranlib libtest.a

suivant la location du fichier ranlib

À la fin de cette phase, le répertoire mylib doit contenir :

1. mylibtest.cpp

2. mylibtest.h

3. mylibtest.o

4. libtest.a

le nom de notre bibliothèque est donc « test ». Dans le cas où nous aurions choisi de créer avec la commande ar un fichier nommé libnomdemalibrairie.a, notre bibliothèque s'appellerait nomdemalibriaire (le préfixe lib et l'extension a sont obligatoires).

V - Utilisation de la bibliothèque dans un programme C++

V-A - Création du fichier C++ du programme

Nous allons créer un fichier exec.cpp qui contiendra le point d'entrée du programme hôte de la bibliothèque.

Il contiendra donc :

1. #include "mylibtest.h"

3. int main()

4. {

5. helloWorld();

6. return 1;

7. }

V-B - Compilation du programme

Nous le compilerons avec la commande :

1. g++ exec.cpp -L. -ltest -o exec_cpp

Remarque importante : les fichiers qui en utilisent d'autres doivent être spécifiés (dans l'ordre de liaison) avant les fichiers qu'ils utilisent ! Dans notre cas nous devrons donc spécifier le fichier exec.cpp (qui utilise la bibliothèque) avant la bibliothèque elle-même.

L'option -L spécifie les chemins où se trouvent les bibliothèques à inclure.

L'option -l spécifie les bibliothèques à utiliser, ici test, puisque notre bibliothèque s'appelle libtest.a

Notre répertoire contiendra donc :

1. mylibtest.cpp

2. mylibtest.h

3. mylibtest.o

4. exec.cpp

5. libtest.a

6. exec_cpp

V-C - Lancement du programme

Le lancement du fichier exec_cpp doit nous afficher « hello » sur l'écran.

VI - Modification de la bibliothèque C++ pour être utilisée en C

Afin qu'une fonction définie dans un fichier C++ puisse être vue dans un programme compilé en C, il est nécessaire de la déclarer à l'intérieur d'un bloc extern « C » , en revanche, étant donné que ce bloc est défini dans le fichier « .h », il sera utilisé aussi bien lors de la compilation de la bibliothèque que de la compilation du programme C, mais dans le cas de la bibliothèque, la fonction ne doit pas être déclarée dans un bloc extern, pour cela il convient d'utiliser une directive préprocesseur qui inclura ou non un bloc extern suivant que le compilateur a positionné la variable __cplusplus ou non.

Le fichier mylib.h devient donc :

1. #ifndef MYLIBTEST_H

2. #define MYLIBTEST_H

4. #ifdef __cplusplus

5. extern "C"

6. {

7. #endif

9. void helloWorld();

11. #ifdef __cplusplus

12. }

13. #endif

15. #endif /* MYLIBTEST_H */

Répéter les étapes des chapitres 2 et 3.

VII - Utilisation de la bibliothèque dans un programme C

VII-A - Création du fichier C du programme

Nous allons créer un fichier exec.c qui contiendra le point d'entrée du programme hôte de la bibliothèque.

Il contiendra donc :

1. #include "mylibtest.h"

3. int main()

4. {

5. helloWorld();

6. return 1;

7. }

VII-B - Compilation du programme

Nous le compilerons avec la commande :

1. gcc exec.c -L. -ltest -lstdc++ -o exec_c

Remarque importante : les composants utilisateur (fichiers, bibliothèques) doivent être spécifiés (dans l'ordre de liaison) avant ceux dont ils ont besoin. Dans notre cas nous devrons donc spécifier le fichier exec.c (qui utilise la bibliothèque) avant la bibliothèque elle-même.

L'option -lstdc++, spécifie au compilateur C qu'il devra devoir lier en plus la bibliothèque standard du C++ (libstdc++.a), puisque nous utilisons dans notre bibliothèque des fonctionnalités de la STL (Standard Type Library), dans notre cas, cette fonctionnalité est le cout.

Remarque : si la bibliothèque a été compilée avec le mot clef -static, il ne devrait pas être nécessaire de spécifier l'option -lstdc++, celle-ci devant alors se trouver directement greffée dans la bibliothèque que nous sommes en train de construire.

Notre répertoire contiendra donc :

1. mylibtest.cpp

2. mylibtest.h

3. mylibtest.o

4. exec.cpp

5. exec.c

6. libtest.a

7. exec_cpp

8. exec_c

VII-C - Lancement du programme

Le lancement du fichier exec_c doit nous afficher « hello » sur l'écran.

VIII - Utilisation des classes dans un programme C, via la bibliothèque C++

Le langage C ne comprenant pas, de base, la notion de classe, il est nécessaire d'encapsuler la création des classes et leur utilisation dans autant de fonctions, et de créer des instances globales des classes visibles depuis l'extérieur de la bibliothèque.

Ainsi, si nous souhaitons créer une classe HelloWorld chargée d'afficher « Hello », nous devrons modifier les fichiers de la façon suivante :

- mylibtest.h

1. #ifndef MYLIBTEST_H

2. #define MYLIBTEST_H

4. #ifdef __cplusplus

6. class HelloWorld

7. {

8. public:

9. void display();

10. };

13. extern "C"

14. {

15. #endif

17. void HelloWorldInit();

18. void HelloWorldDeinit();

19. void HelloWorldDisplay();

22. #ifdef __cplusplus

23. }

24. #endif

26. #endif

- mylibtest.cpp

1. #include "mylibtest.h"

3. #include <iostream>

5. HelloWorld * g_helloWorld;

7. void HelloWorld::display()

8. {

9. std::cout << "hello" << std::endl;

10. }

12. void HelloWorldInit()

13. {

14. g_helloWorld = new HelloWorld;

15. }

17. void HelloWorldDeinit()

18. {

19. if (g_helloWorld)   delete g_helloWorld;

20. }

22. void HelloWorldDisplay()

23. {

24. g_helloWorld->display();

25. }

- exec.c

1. #include "mylibtest.h"

3. int main()

4. {

5. HelloWorldInit();

6. HelloWorldDisplay();

7. HelloWorldDeinit();

9. return 1;

10. }

IX - Utilisation de plusieurs instances de classes dans un programme C, via la bibliothèque C++

Nous venons de voir comment créer et utiliser une instance de la classe HelloWorld dans un fichier C, maintenant voyons comment il est possible de créer et utiliser plusieurs instances de cette classe.

Au lieu d'utiliser un pointeur global sur une seule instance de HelloWorld, il faudra créer une map globale qui associera un identifiant à une instance.

Il faudra donc modifier les fonctions HelloWorldInit() et HelloWorldDeinit() qui permettront d'initialiser cette map.

Nous créerons les fonctions HelloWorldCreate() et HelloWorldRelease() permettant à partir d'un identifiant de créer des instances de HelloWorld.

La fonction HelloWorldDisplay() prendra donc elle aussi un argument qui sera l'identifiant permettant de retrouver l'instance depuis le fichier C.

Voici à quoi ressembleront les différents fichiers :

- mylibtest.h

1. #ifndef MYLIBTEST_H

2. #define MYLIBTEST_H

4. #ifdef __cplusplus

6. class HelloWorld

7. {

8. public:

9. void display();

10. };

13. extern "C"

14. {

15. #endif

17. void HelloWorldInit();

18. void HelloWorldDeinit();

19. void HelloWorldCreate(int _num);

20. void HelloWorldRelease(int _num);

21. void HelloWorldDisplay(int _num);

24. #ifdef __cplusplus

25. }

26. #endif

28. #endif

- mylibtest.cpp

1. #include "mylibtest.h"

3. #include <iostream>

4. #include <map>

6. std::map<int, HelloWorld *, std::less<int, HelloWorld *> > * g_helloWorlds;

8. void HelloWorld::display()

9. {

10. std::cout << "hello" << std::endl;

11. }

13. void HelloWorldInit()

14. {

15. g_helloWorlds = new std::map<int, HelloWorld *, std::less<int, HelloWorld *> >;

16. }

18. void HelloWorldDeinit()

19. {

20. if (g_helloWorlds)   delete g_helloWorlds;

21. }

23. void HelloWorldCreate(int _num)

24. {

25. if (!g_helloWorlds)    return;

27. std::map<int, HelloWorld *, std::less<int, HelloWorld *> >::iterator p = g_helloWorlds->find(_num);

29. if (p == datas.end())

30. g_helloWorlds->insert(std::pair<int, HelloWorld *>(_num, new HelloWorld))

31. }

33. void HelloWorldRelease(int _num)

34. {

35. if (!g_helloWorlds)    return;

37. std::map<int, HelloWorld *, std::less<int, HelloWorld *> >::iterator p = g_helloWorlds->find(_num);

39. if (p != datas.end())

40. delete (*p).second;

41. }

43. void HelloWorldDisplay(int _num)

44. {

45. if (!g_helloWorlds)    return;

47. std::map<int, HelloWorld *, std::less<int, HelloWorld *> >::iterator p = g_helloWorlds->find(_num);

49. if (p != datas.end())

50. (*p).second->display();

51. }

- exec.c

1. #include "mylibtest.h"

3. int main()

4. {

5. HelloWorldInit();

7. HelloWorldCreate(12);

8. HelloWorldCreate(4);

9. HelloWorldCreate(25);

10. HelloWorldDisplay(12);

11. HelloWorldDisplay(4);

12. HelloWorldRelease(12);

13. HelloWorldDisplay(25);

14. HelloWorldRelease(4);

15. HelloWorldRelease(25);

17. HelloWorldDeinit();

19. return 1;

20. }