librairie dynamique et instanciation

librairie dynamique et instanciation - C++ - Programmation

Marsh Posté le 16-01-2011 à 00:34:12    

Bonsoir, après avoir lu le tutorial http://hiko-seijuro.developpez.com/a...que-dynamique/ sur la création de librairie dynamique en c++ sous linux et testé son exemple, je m'interroge sur la manière d'instancier un objet dans le code de la librairie
 
mettons la classe "circle" (issue du tuto):
 

Code :
  1. circle.h
  2. #ifndef CIRCLE_H_
  3. #define CIRCLE_H_
  4. #include <iostream>
  5. #include test.h
  6. class circle
  7. {
  8. public:
  9.     virtual void draw();
  10. };
  11. typedef circle *(*maker_circle)();
  12. #endif
  13. circle.cpp
  14. #include "circle.h"
  15. using namespace std;
  16. void circle::draw()
  17. {
  18.     cout << "   ###   " << endl;
  19.     cout << "  #   #  " << endl;
  20.     cout << " #     # " << endl;
  21.     cout << " #     # " << endl;
  22.     cout << "  #   #  " << endl;
  23.     cout << "   ###   " << endl;
  24. }
  25. extern "C"
  26. {
  27.     circle *make_circle()
  28.     {
  29.  return new circle();
  30.     }
  31. }


 
j'ai voulu ajouter une autre classe "test" :
 

Code :
  1. test.h
  2. #ifndef TEST_H_
  3. #define TEST_H_
  4. #include <iostream>
  5. class test
  6. {
  7. public:
  8.    
  9. Test();
  10. ~Test();
  11.    void print();
  12. };
  13. #endif
  14. test.cpp
  15. #include "test.h"
  16. using namespace std;
  17. Test::Test(){}
  18. Test::~Test(){}
  19. void Test::print(){
  20.     cout << "   TEST  " << endl;
  21. }


 
j'ai voulu l'instancier dans la méthode "draw" de "circle" :
 

Code :
  1. void circle::draw()
  2. {
  3.     cout << "   ###   " << endl;
  4.     cout << "  #   #  " << endl;
  5.     cout << " #     # " << endl;
  6.     cout << " #     # " << endl;
  7.     cout << "  #   #  " << endl;
  8.     cout << "   ###   " << endl;
  9.     Test* test = new Test();
  10.     test->print();
  11. }


 
voici le main.cpp:
 

Code :
  1. #include "circle.h"
  2. #include "test.h"
  3. #include <cstdlib>
  4. #include <iostream>
  5. #include <dlfcn.h>
  6. using namespace std;
  7. int main(int argc, char **argv)
  8. {
  9.     void *hndl;
  10.     maker_circle pMaker;
  11.     // Ouverture de la librairie
  12.     hndl = dlopen("./libcircle.so", RTLD_LAZY);
  13.     if(hndl == NULL)    {
  14.  cerr << "dlopen : " << dlerror() << endl;
  15.  exit(EXIT_FAILURE);
  16.     }
  17.     // Chargement du créateur
  18.     void *mkr = dlsym(hndl, "make_circle" );
  19.     if (mkr == NULL)
  20.     {
  21.  cerr << "dlsym : " << dlerror() << endl;
  22.  exit(EXIT_FAILURE);
  23.     }
  24.     pMaker = (maker_circle)mkr;
  25.     // Création, affichage puis destruction du cercle
  26.     circle *my_circle = pMaker();
  27.     my_circle->draw();
  28.     dlclose(hndl);
  29.     return EXIT_SUCCESS;
  30. }


 
je compile en librairie dynamique sans problème avec :
 

Code :
  1. libcircle.so: circle.cpp circle.h test.h test.cpp
  2. g++  -shared -o libcircle.so circle.cpp
  3. example: main.cpp libcircle.so
  4. g++  -o example main.cpp -ldl


 
mais à l'exécution, j'obtiens un "undefined symbol: _ZN4TestC1Ev"
 
ai-je mal instancié ?  :(  
 
si je retire  
 

Code :
  1. Test* test = new Test();
  2.     test->print();


 
ça marche nickel et j'ai le résultat attendu

Reply

Marsh Posté le 16-01-2011 à 00:34:12   

Reply

Marsh Posté le 16-01-2011 à 07:59:48    

il te manque un -lcircle pour que ton exécutable soit linké avec libcircle. Même pour un link sur une bibliothèque dynamique, il faut ce genre de chose.
 
 

Code :
  1. g++  -o example main.cpp -ldl -lcircle


 
 
 
Il faut également avoir précisé le chemin de la bibliothèque avec -L si celle ci ne se trouve pas dans un chemin connu de g++.
 
 

Code :
  1. g++  -o example main.cpp -ldl -lcircle -L/path/to/library

Message cité 1 fois
Message édité par xilebo le 16-01-2011 à 08:02:49
Reply

Marsh Posté le 16-01-2011 à 08:46:11    

codablank a écrit :


mais à l'exécution, j'obtiens un "undefined symbol: _ZN4TestC1Ev"


 
Pour qu'une lib chargée avec dlopen puisse utiliser des symboles du programme principal, il y a des choses à faire (et je ne les retiens pas d'une fois sur l'autre).  Regarde les différentes options de gcc, ça me suffit généralement pour retrouver ce qu'il faut.
 

xilebo a écrit :

il te manque un -lcircle pour que ton exécutable soit linké avec libcircle.


 
Il utilise dlopen()...


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 16-01-2011 à 09:08:05    

Un Programmeur a écrit :


 
Pour qu'une lib chargée avec dlopen puisse utiliser des symboles du programme principal, il y a des choses à faire (et je ne les retiens pas d'une fois sur l'autre).  Regarde les différentes options de gcc, ça me suffit généralement pour retrouver ce qu'il faut.
 


 

Un Programmeur a écrit :


 
Il utilise dlopen()...


 
 
Désolé, je n'avais pas fait gaffe  :jap:

Reply

Marsh Posté le 16-01-2011 à 12:51:06    

Citation :

g++  -shared -o libcircle.so circle.cpp


Et avec:
g++ -fPIC -shared -o libcircle.so circle.cpp test.cpp
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 16-01-2011 à 13:57:50    

C'est vraisemblablement la solution.  J'avais vu qu'il ne mettais pas test.cpp dans libcircle.so et j'avais supposé sans vérifié qu'il le mettais avec main.cpp (ce qui a posteriori est doublement stupide considérant les dépendances déclarées pour libcircle.so).


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 16-01-2011 à 15:31:54    

Codablank, si j'avais eu a écrire ce code, j'aurais plus ou moins fait comme suit:

Code :
  1. /////////////////////////////////////////////////////////////////////////////
  2. // circle.h
  3. #ifndef CIRCLE_H_
  4. #define CIRCLE_H_ 
  5. #include "test.h"  // à commenter si l'on veut tester sans la classe test
  6. class Circle {
  7. #ifdef TEST_H_
  8.   friend class Test;
  9. #endif
  10.   public: virtual void draw();
  11. };
  12. #endif
  13. /////////////////////////////////////////////////////////////////////////////
  14. //test.h
  15. #ifndef TEST_H_
  16. #define TEST_H_
  17. class Test {
  18.   void print();
  19. };
  20. #endif
  21. /////////////////////////////////////////////////////////////////////////////
  22. // circle.cpp
  23. #include "circle.h"
  24. #include <iostream>
  25. using std::cout;
  26. extern "C" Circle* create() {
  27.   return new Circle;
  28. }
  29. extern "C" void destroy(Circle* c) {
  30.   delete c;
  31. }
  32. void Circle::draw()
  33. {
  34.     cout << "   ###   " << endl;
  35.     cout << "  #   #  " << endl;
  36.     cout << " #     # " << endl;
  37.     cout << " #     # " << endl;
  38.     cout << "  #   #  " << endl;
  39.     cout << "   ###   " << endl;
  40. #ifdef TEST_H_
  41.     Test* test = new Test;
  42.     test->print();
  43.     delete test;
  44. #endif
  45. }
  46. /////////////////////////////////////////////////////////////////////////////
  47. // test.cpp
  48. #include <iostream>
  49. #include "test.h"
  50. using std::cout;
  51. void Test::print() {
  52.     cout << "   TEST  " << endl;
  53. }
  54. /////////////////////////////////////////////////////////////////////////////
  55. // main.cpp
  56. #include <cstdlib>
  57. #include <iostream>
  58. #include <dlfcn.h>
  59. #include "circle.h"
  60. using namespace std;
  61. int main(int argc, char **argv) {
  62.   int exit_code = EXIT_SUCCESS;
  63.   void *hndl = dlopen("./libcircle.so", RTLD_LAZY); // Ouverture de la librairie
  64.  
  65.   if (hndl == NULL) {
  66.     cerr << "dlopen : " << dlerror() << endl;
  67.     exit_code = EXIT_FAILURE;
  68.   }
  69.   else {
  70.     Circle* (*create_circle)();
  71.     void (*destroy_circle)(Circle*);
  72.    
  73.     create_circle = (Circle* (*)()) dlsym(hndl, "create" );
  74.     destroy_circle = (void (*)(Circle*)) dlsym(hndl, "destroy" );
  75.     if (create_circle == NULL || destroy_circle == NULL) {
  76.       cerr << "dlsym : " << dlerror() << endl;
  77.       exit_code = EXIT_FAILURE;
  78.     }
  79.     else {
  80.       Circle* my_circle = create_circle();
  81.       my_circle->draw();
  82.       destroy_circle(my_circle);
  83.     }
  84.     dlclose(hndl);
  85.   }
  86.   return exit_code;
  87. }
  88. /////////////////////////////////////////////////////////////////////////////


(Bon, j'ai pas de compilo sous la main, donc pas testé)
Il y a pas de grosses diffs, mais l'emploi des directives préprocesseur permet de n'avoir qu'un endroit à commenter pour utiliser test ou non
Et plutôt que d'avoir un constructeur et un destructeur publics sans paramètres, l'emploi d'une classe friend me parait mieux coller.
Par contre, le déport de
#include <iostream>
dans cercle.h au lieu de cercle.cpp alors que rien dans cercle.h n'utilise iostream, je suis tout à fait contre.
Et dans le même genre de remarque, il n'y a aucune raison de faire figurer test.h dans main.cpp vu qu'on ne fait pas appel à Test dans son code.

 

A+,


Message édité par gilou le 16-01-2011 à 15:55:58

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 16-01-2011 à 15:38:11    

Un dernier point:

Code :
  1. class test
  2. {
  3. public:
  4.  
  5. Test();
  6. ~Test();
  7.    void print();
  8. };


Si le nom de ta classe est test, et non pas Test, il va y avoir des problèmes...
Dans le même genre, nommer une classe circle sans majuscule, pourquoi pas, mais utiliser une convention de nommage de base, ça peut faciliter le repérage des erreurs.

 

A+,


Message édité par gilou le 16-01-2011 à 15:39:26

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 16-01-2011 à 15:57:44    

Merci pour toutes vos  réponses  [:dawa]  
 
effectivement j'avais omis test.cpp dans la compilation de la librairie, donc binaire de Test impossible à trouver pour le main
 
gilou, merci pour tes corrections, c'était à la base juste un code de test, mais tes conseils vont me permettre de bâtir un chargeur de .so un peu plus propre
 
à propos existe-il une api standard pour ce genre de chose en C++ ?

Reply

Marsh Posté le 16-01-2011 à 16:00:22    

Citation :

à propos existe-il une api standard pour ce genre de chose en C++

Non, ça dépend de l'OS, les librairies partagées/dlls.
Sous unix, selon les unix, ça a toujours été le bordel au niveau des options pour le compilo, les librairies partagées (AIX / SunOS ou Solaris par exemple).
A+,


Message édité par gilou le 16-01-2011 à 16:11:08

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed