Salut,
Il ne faut pas confondre : Code::blocks, Dev-Cpp, XDev-cpp ou même QtCreator ne sont que ce que l'on appelle des "EDI" (Environnement de Developpement Intégré).
Ce ne sont que des environnements qui permettent d'utiliser une série d'outils (compilateur, éditeur de liens, archiveur,...) dans un ordre bien précis pour permettre de transformer le code que tu as écrit en quelque chose d'utilisable (bibliothèque, statique ou dynamique, ou exécutable).
Si deux EDI utilisent le même compilateur (par exemple gcc), dans la même version, et les mêmes options de compilation, ce qui est généré avec l'un est parfaitement utilisable par l'autre.
Ceci dit, dev-cpp (et ses variations WxDev-cpp) est un projet qui n'est plus maintenu depuis maintenant déjà quelques années.
Il est donc largement préférable de choisir un EDI plus récent, comme Code::blocks
Pour en revenir à ta question initiale :
Il existe deux types de bibliothèques : les bibliothèques statiques et les bibliothèques dynamiques ("dll" sous windows, "so" sous linux).
Les bibliothèques statiques ne sont, pour faire simple, que des archives qui contiennent des fichiers objets (*) dont le contenu utile sera rajouté à ton application au moment de l'édition de liens afin qu'il soit utilisé.
Si tu modifies une bibliothèque statique, les applications qui ont déjà été compilées avec les versions précédentes ne seront pas affectées par les modification, pour la simple et bonne raison que l'application finale est indépendante de la bibliothèque statique.
Si tu veux qu'une application puisse profiter des modifications que tu as apportées à une bibliothèque statique, il faut... recompiler l'application (ou au minimum refaire l'édition de liens)
Le fonctionnement des bibliothèques dynamiques est un peu différent.
Elles contiennent toujours le code objet des fonctions qui en font partie, mais ce code n'est pas ajouté directement dans l'application qui utilise la bibliothèque:
L'application va appeler les fonctions directement dans la bibliothèque.
Comme le code de la bibliothèque n'est pas directement inclus dans l'application, mais que c'est l'application qui va le chercher en cas de besoin, si tu modifies une bibliothèque dynamique, et que tu t'arranges pour que la nouvelle version de cette bibliothèque dynamique se trouve à l'endroit où l'application s'attend à la trouver, toute application utilisant la bibliothèque dynamique sera directement impactée par la modification, y compris les applications les plus anciennes.
A lire ton premier message, j'ai l'impression que ce que tu cherches à faire ressemble beaucoup plus à une bibliothèque dynamique qu'à une bibliothèque statique
(*) pour comprendre ce dont je parle, il faut connaitre, au moins dans les grandes lignes, la "chaine de compilation".
Le compilateur n'est qu'un des outils qui interviennent entre le code que tu écris (quel que soit le langage) et l'exécutable final.
En effet, pour passer du code que tu écris à l'exécutable final, il faut passer par plusieurs étapes :
- La première étape est effectuée par ce que l'on appelle le "préprocesseur". C'est un outils qui se contente de modifier le code en fonction de certaines macros (par exemple #include a pour effet de copier le contenu du fichier indiqué)
- La deuxième étape (maintenant facultative grace aux évolutions) était la génération de code assembleur: l'assembleur est un langage moins évolué que le C ou le C++, qui représente les instructions que le processeur devra effectuer
- La troisième étape est la génération de code binaire : chaque instruction que le processeur doit effectuer est "traduite" au format binaire pour qu'elle soit compréhensible par le processeur. Le résultat de cette traduction fournit ce que l'on appelle un "fichier objet"
- La quatrième étape dépend du résultat que tu veux obtenir : Pour la création d'applications, c'est l'édition de liens qui va remplacer l'appel aux différents symbole représentant les différentes fonction par l'appel aux adresses mémoire auxquelles les fonctions en question se trouvent effectivement. Pour la création de bibliothèques, ce sera plutot la création d'une archive
Bon, bien sur, il n'y a ici que les grandes lignes (on pourrait écrire un bouquin complet ) te j'ai même pris quelques raccourcis qui font que ce n'est "pas tout à fait juste"...
Mais cela te permettra malgré tout de comprendre un peu d'avantage de quoi l'on parle
La génération de bibliothèques statiques est "relativement" simple :
En gros, une fois que l'ensemble des fichiers objets a été généré, il "suffit" de créer une archive qui les contient tous avec l'outil qui permettra au compilateur d'en récupérer le contenu (et en lui donnant un nom qui permettra au compilateur de se rendre compte qu'il s'agit bel et bien d'une bibliothèque ).
Pour les bibliothèques dynamiques, c'est "un peu plus compliqué", car cela dépendra essentiellement du système cible.
Sous linux, il suffit généralement d'utiliser l'option de compilation -fPIC pour indiquer au compilateur qu'il doit générer du code indépendant de la position (Position Independant Code) avant de tout mettre dans une archive
Sous windows, il faut commencer par:
faire en sorte que les symboles utilisés par l'application soit "exportés" de la dll
faire en sorte que l'application sache qu'elle doit "importer" les symboles qui viennent de la dll.
Pour indiquer qu'un symbole doit etre exporté par une dll, il faut utiliser la déclaration de spécification __declspec(dllexport).
Cela donne, pour une déclaration de fonction, un code proche de
type_de_retour __declspec(dllexport) nom_de_la_fonction(/* liste de paramètres */ );
et, si tu veux exporter l'ensemble d'une classe, un code proche de
1 2 3 4
| class __declspec(dllexport) NomDeLaClass
{
/* contenu de la classe */
}; |
Lorsque l'on veut utiliser dans une application quelque chose qui est fournit par une dll, il faut indiquer dans la déclaration de ce "quelque chose" que cela doit etre importé.
A cette fin, la déclaration d'une fonction devra ressembler à quelque chose proche de
type_de_retour __declspec(dllimport) nom_de_la_fonction(/* liste de paramètres */ );
et celle d'une classe à un code proche de
1 2 3 4
| class __declspec(dllimport) NomDeLaClass
{
/* contenu de la classe */
}; |
La solution la plus simple (pour éviter d'avoir à gérer deux versions des fichiers d'en-tête : une pour la compilation et l'autre pour l'utilisation) est de recourir à des directives préprocesseurs proches de
1 2 3 4 5
| #if defined(MYLIB_EXPORT)
#define MYLIB_API __declspec( dllexport )
#else
#define MYLIB_API __declspec( dllimport )
#endif |
et d'ajouter la définition MYLIB_EXPORT à la ligne de commande de compilation ( ex avec g++ : gcc -DMYLIB_EXPORT -DSHARED -fmonFichier.cpp -o../build/monFichier.o )
Les codes correspondant aux déclarations de fonctions et de classes ressemblant alors à quelque chose proche de
type_de_retour MYLIB_API nom_de_la_fonction(/* liste de paramètres */ );
et celle d'une classe à un code proche de
1 2 3 4
| class MYLIB_API NomDeLaClass
{
/* contenu de la classe */
}; |
Évidemment, les directives préprocesseurs peuvent devenir beaucoup plus complexes, si tu veux, par exemple, pouvoir aussi bien créer une bibliothèque statique qu'une bibliothèque dynamique ou si tu veux pouvoir créer une bibliothèque fonctionnant aussi bien sous linux que sous windows.
Tu pourrais ainsi très bien te trouver avec des directives préprocesseurs proches de
1 2 3 4 5 6 7 8 9 10 11 12 13
| #if defined(__WIN32) // la partie utilisée sous windows
#if defined(SHARED) // la version "dll"
#if defined(MYLIB_EXPORT) // lors de la compilation
#define MYLIB_API __declspec(dllexport)
#else // lors de l'utilisation
#define MYLIB_API __declspec(dllimport)
#endif // MYLIB_EXPORT
#else // la version statique (windows)
#define MYLIB_API
#endif // SHARED
#else // sous linux
#define MYLIB_API
#endif // __WIN32 |
Hope it helps
Partager