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 :
2.
3.
4.
5.
6.
7.
8.
#include
"mylibtest.h"
#include
<iostream>
void
helloWorld()
{
std::
cout <<
"hello"
<<
std::
endl;
}
et mylibtest.h contiendra celui-ci :
2.
3.
4.
5.
6.
#ifndef MYLIBTEST_H
#define MYLIBTEST_H
void
helloWorld();
#endif
donc le répertoire mylib contiendra :
2.
mylibtest.cpp
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
g++
-
c mylibtest.cpp
ou bien
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 :
2.
3.
mylibtest.cpp
mylibtest.h
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.
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 :
ar cr libtest.a fichier_obj1.o fichier_obj2.o, etc.
dans notre exemple cela sera :
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
/
bin/
ranlib libtest.a
ou bien
/
usr/
bin/
ranlib libtest.a
suivant la location du fichier ranlib
À la fin de cette phase, le répertoire mylib doit contenir :
2.
3.
4.
mylibtest.cpp
mylibtest.h
mylibtest.o
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 :
2.
3.
4.
5.
6.
7.
#include
"mylibtest.h"
int
main()
{
helloWorld();
return
1
;
}
V-B. Compilation du programme▲
Nous le compilerons avec la commande :
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 :
2.
3.
4.
5.
6.
mylibtest.cpp
mylibtest.h
mylibtest.o
exec.cpp
libtest.a
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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
#ifndef MYLIBTEST_H
#define MYLIBTEST_H
#ifdef __cplusplus
extern
"C"
{
#endif
void
helloWorld();
#ifdef __cplusplus
}
#endif
#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 :
2.
3.
4.
5.
6.
7.
#include
"mylibtest.h"
int
main()
{
helloWorld();
return
1
;
}
VII-B. Compilation du programme▲
Nous le compilerons avec la commande :
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 :
2.
3.
4.
5.
6.
7.
8.
mylibtest.cpp
mylibtest.h
mylibtest.o
exec.cpp
exec.c
libtest.a
exec_cpp
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
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
#ifndef MYLIBTEST_H
#define MYLIBTEST_H
#ifdef __cplusplus
class
HelloWorld
{
public
:
void
display();
}
;
extern
"C"
{
#endif
void
HelloWorldInit();
void
HelloWorldDeinit();
void
HelloWorldDisplay();
#ifdef __cplusplus
}
#endif
#endif
- mylibtest.cpp
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
#include
"mylibtest.h"
#include
<iostream>
HelloWorld *
g_helloWorld;
void
HelloWorld::
display()
{
std::
cout <<
"hello"
<<
std::
endl;
}
void
HelloWorldInit()
{
g_helloWorld =
new
HelloWorld;
}
void
HelloWorldDeinit()
{
if
(g_helloWorld) delete
g_helloWorld;
}
void
HelloWorldDisplay()
{
g_helloWorld->
display();
}
- exec.c
2.
3.
4.
5.
6.
7.
8.
9.
10.
#include
"mylibtest.h"
int
main()
{
HelloWorldInit();
HelloWorldDisplay();
HelloWorldDeinit();
return
1
;
}
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
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
#ifndef MYLIBTEST_H
#define MYLIBTEST_H
#ifdef __cplusplus
class
HelloWorld
{
public
:
void
display();
}
;
extern
"C"
{
#endif
void
HelloWorldInit();
void
HelloWorldDeinit();
void
HelloWorldCreate(int
_num);
void
HelloWorldRelease(int
_num);
void
HelloWorldDisplay(int
_num);
#ifdef __cplusplus
}
#endif
#endif
- mylibtest.cpp
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
#include
"mylibtest.h"
#include
<iostream>
#include
<map>
std::
map<
int
, HelloWorld *
, std::
less<
int
, HelloWorld *>
>
*
g_helloWorlds;
void
HelloWorld::
display()
{
std::
cout <<
"hello"
<<
std::
endl;
}
void
HelloWorldInit()
{
g_helloWorlds =
new
std::
map<
int
, HelloWorld *
, std::
less<
int
, HelloWorld *>
>
;
}
void
HelloWorldDeinit()
{
if
(g_helloWorlds) delete
g_helloWorlds;
}
void
HelloWorldCreate(int
_num)
{
if
(!
g_helloWorlds) return
;
std::
map<
int
, HelloWorld *
, std::
less<
int
, HelloWorld *>
>
::
iterator p =
g_helloWorlds->
find(_num);
if
(p ==
datas.end())
g_helloWorlds->
insert(std::
pair<
int
, HelloWorld *>
(_num, new
HelloWorld))
}
void
HelloWorldRelease(int
_num)
{
if
(!
g_helloWorlds) return
;
std::
map<
int
, HelloWorld *
, std::
less<
int
, HelloWorld *>
>
::
iterator p =
g_helloWorlds->
find(_num);
if
(p !=
datas.end())
delete
(*
p).second;
}
void
HelloWorldDisplay(int
_num)
{
if
(!
g_helloWorlds) return
;
std::
map<
int
, HelloWorld *
, std::
less<
int
, HelloWorld *>
>
::
iterator p =
g_helloWorlds->
find(_num);
if
(p !=
datas.end())
(*
p).second->
display();
}
- exec.c
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
#include
"mylibtest.h"
int
main()
{
HelloWorldInit();
HelloWorldCreate(12
);
HelloWorldCreate(4
);
HelloWorldCreate(25
);
HelloWorldDisplay(12
);
HelloWorldDisplay(4
);
HelloWorldRelease(12
);
HelloWorldDisplay(25
);
HelloWorldRelease(4
);
HelloWorldRelease(25
);
HelloWorldDeinit();
return
1
;
}