IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Méthode de chargement dynamique de fonctions


Sujet :

C++

  1. #1
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut Méthode de chargement dynamique de fonctions
    Bonjour tout le monde!
    Je vous explique rapidement le but de ce sujet: je dois produire une bibliothèque statique qui contient une classe abstraite A et plusieurs classes filles (B, C, D). Chacune de ces classes filles dépendent de bibliothèques différentes les unes des autres (BLib, CLib, DLib).
    Le problème est le suivant: chez certains utilisateurs, les bibliothèques des classes filles ne seront pas disponibles donc il ne pourront pas compiler ma bibliothèque. Pour parer ce problème j'ai utilisé des instructions préprocesseur pour indiquer si une bibliothèque fille est présente ou non:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #if _BLIB_FOUND
     
    #include "blib.h"
     
    class B : public A
    {
    ...
    };
    #endif
    Avec cette méthode, la bibliothèque peut compiler sans problème. Seulement voilà, maintenant, il faudrait que j'ai une fabrique à laquelle je passe en paramètre le nom de la classe fille et qui m'instancie la classe correspondante.

    Ma première approche fut de faire un "vulgaire" switch de la manière suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    void buildClass(int className, A* returnClass)
    {
    switch (className)
    {
    case 0:
      returnClass = new B();
      break;
    case 1:
      returnClass = new C();
      break;
    case 2:
      returnClass = new D();
      break;
    default:
      cerr << "Class not defined" << endl;
    }
    }
    Seulement voilà... ca ne compilera seulement si l'utilisateur possède toutes les bibliothèques dépendantes...

    Voici ma nouvelle idée: charger les méthodes (à savoir ici les constructeurs de classes) de manière dynamique.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void buildClass(std::string className, A* returnClass)
    {
    //Permet de charger une méthode qui construit un objet.
    void* classMetod = loadClass(className);
     
    if(!returnClass)
    {
    cerr << "Class not defined" << endl;
    }
    else
    {
    //Cree l'objet de type className.
    classMethod(returnClass);
    }
    }
    Mon problème c'est comment instancié cette méthode (loadClass). Est-ce possible?
    Je sais que ce genre de fonction existe notamment en openGL avec la méthode glxGetProcAdress mais je ne trouve pas son implémentation.

    Si vous avez une idée, je suis tout à fait preneur!

    Merci d'avance.

  2. #2
    Membre éprouvé Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Points : 997
    Points
    997
    Par défaut
    Bonjour.
    L'un des deux tutoriels suivants pourra peut-être répondre (au moins partiellement) à tes questions...
    Appels de méthodes déterminés dynamiquement
    Exportation de classes C++ dans une bibliothèque dynamique sous Linux

    Bon courage.

  3. #3
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Merci Stef pour ta réponse.
    Pour la partie chargement dynamique de méthode à partir d'une bibliothèque dynamique, je ne pense pas que ce soit approprié pour mon problème. Je n'ai que des classes statiques. De plus je pense que le chargement dynamique ne se fait pas de la même façon sur un système type Unix que sous Windows. Or ma bibliothèque doit être multi-plateformes.

    Pour le premier lien je vais regarder ça plus en détail.

  4. #4
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Hello

    Pourquoi vouloir passer le nom en paramètre ? L'utilisateur ne peut il pas simplement appeler des méthodes CreerClassB(), CreerClassC() et CreerClassD() ?

    Quelle que soit la réponse à cette question, deux solutions possibles :
    - Utiliser tes flags _XLIB_FOUND dans l'implémentation pour faire un = null au lieu d'un new pour les libs non disponibles.
    - La solution préconisée : configurer ton outil de compilation pour qu'il utilise des *.cpp différents en fonction de la disponibilité ou non des libs. Rien ne t'empêche de mettre CreerClassB, CreerClassC et CreerClassD dans des *.cpp différents, quand bien même ils seraient déclarés dans la même factory.

    Puisque l'utilisateur dispose du code source, l'usage de la spécialisation template peut également répondre à ton problème de manière simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template < typename T > struct Factory
    {
      T* CreerClass();
    };
     
    #if _BLIB_FOUND
    template <> B* Factory<B>::CreerClasse()
    {
      return new B();
    };
    #endif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // Code utilisateur
    A* pMaClass = Factory<B>::CreerClasse();
    PS : Dans ton code, la signature que tu proposes devrait être

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void buildClass(int className, A*& returnClass);
    avec une référence sur le pointeur sinon, ça marche pô .
    Find me on github

  5. #5
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Bonjour,
    Merci pour ta réponse!
    C'est vrai que la proposition que tu proposes est relativement simple.

    Par contre, je pense qu'il faut tout de même rajouter les flags #if _XLIB_FOUND au niveau du code utilisateur non?

    Pourquoi vouloir passer le nom en paramètre ? L'utilisateur ne peut il pas simplement appeler des méthodes CreerClassB(), CreerClassC() et CreerClassD() ?
    Je souhaiterais juste que l'utilisateur n'est pas accès à ces méthodes, il ferait appel à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void buildClass(int className, A*& returnClass);
    où className serait récupérer à partir d'un fichier de conf ce qui permetrer d'éviter de re-compiler à chaque fois si l'on souhaite utiliser tel ou tel classe fille.
    Je fais mon prototype et je vous tiens au courant de la solution que j'aurai choisie.

  6. #6
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par darkman19320 Voir le message
    Par contre, je pense qu'il faut tout de même rajouter les flags #if _XLIB_FOUND au niveau du code utilisateur non?
    Non. Si tu configures ta chaîne de compilation pour choisir les *.cpp adéquats, tu n'en auras pas besoin dans le code. Il suffit de goupiller son implémentation pour que la requête sur une classe non disponible renvoie un pointeur nul.
    Find me on github

  7. #7
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Tu es sûr parce que si tu défini ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A* pMaClass = Factory<B>::CreerClasse();
    Et que B n'est pas définie, il y aura forcément une erreur de compilation.

  8. #8
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par darkman19320 Voir le message
    Tu es sûr parce que si tu défini ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A* pMaClass = Factory<B>::CreerClasse();
    Et que B n'est pas définie, il y aura forcément une erreur de compilation.
    Oui car là tu parles de l'autre solution, celle avec les templates.

    Je vais te filer un exemple plus parlant pour celle que je te propose.
    Find me on github

  9. #9
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Voici un exemple complet, avec CMake comme chaîne de compilation :

    CMakeLists.txt

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    project(Factory)
     
    include_directories(${PROJECT_SOURCE_DIR})
    set(SOURCE_FILES ${PROJECT_SOURCE_DIR}/main.cpp ${PROJECT_SOURCE_DIR}/Factory.cpp)
     
    # A voir chez toi comment générer les bons flags
    set(LIBB_FOUND "LibB-Found")
    set(LIBC_FOUND "LibC-NotFound")
     
    if(LIBB_FOUND)
        set(SOURCE_FILES "${SOURCE_FILES}" ${PROJECT_SOURCE_DIR}/FactoryImpl/FactoryLibBFound.cpp)
    else()
        set(SOURCE_FILES "${SOURCE_FILES}" ${PROJECT_SOURCE_DIR}/FactoryImpl/FactoryLibBMissing.cpp)
    endif(LIBB_FOUND)
     
    if(LIBC_FOUND)
        set(SOURCE_FILES "${SOURCE_FILES}" ${PROJECT_SOURCE_DIR}/FactoryImpl/FactoryLibCFound.cpp)
    else()
        set(SOURCE_FILES "${SOURCE_FILES}" ${PROJECT_SOURCE_DIR}/FactoryImpl/FactoryLibCMissing.cpp)
    endif(LIBC_FOUND)
     
    add_executable(test ${SOURCE_FILES})
    A.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef A_H
    #define A_H
     
    class A
    {
    };
     
    #endif
    B.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #ifndef B_H
    #define B_H
     
    class B : public A
    {};
     
    #endif
    C.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #ifndef C_H
    #define C_H
     
    class C : public A
    {};
     
    #endif
    Factory.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #ifndef Factory_H
    #define Factory_H
     
    class A;
     
    class Factory
    {
        A* CreerClasseB();
        A* CreerClasseC();
     
    public:
        enum ClassName{ LibB, LibC};
        A* CreateClass(ClassName iClassToCreate);
    };
     
    #endif
    main.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <iostream>
    #include "Factory.h"
    #include "A.h"
     
    int main(int argc, char * argv[])
    {
        Factory myFact;
        A* pObjectB = myFact.CreateClass(Factory::LibB);
        A* pObjectC = myFact.CreateClass(Factory::LibC);
        if(pObjectB)
        {
            std::cout << "Usage of lib B Ok !" << std::endl;
            delete pObjectB;
        }
     
        if(pObjectC)
        {
            std::cout << "Usage of lib C Ok !" << std::endl;
            delete pObjectC;
        }
     
        return 0;
    }
    Facory.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include "Factory.h"
    #include "A.h"
     
    A* Factory::CreateClass(ClassName iClassToCreate)
    {
        A* pObject = 0;
        switch(iClassToCreate)
        {
        case LibB:
            pObject = CreerClasseB();
            break;
        case LibC:
            pObject = CreerClasseC();
            break;
        default:;
        }
     
        return pObject;
    }
    FactoryImpl/FactoryLibBFound.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include "Factory.h"
    #include "A.h"
    #include "B.h"
     
    A* Factory::CreerClasseB()
    {
        return new B();
    }
    FactoryImpl/FactoryLibBMissing.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "Factory.h"
    #include "A.h"
     
    A* Factory::CreerClasseB()
    {
        return 0;
    }
    FactoryImpl/FactoryLibCFound.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include "Factory.h"
    #include "A.h"
    #include "C.h"
     
    A* Factory::CreerClasseC()
    {
        return new C();
    }
    FactoryImpl/FactoryLibCMissing.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "Factory.h"
    #include "A.h"
     
    A* Factory::CreerClasseC()
    {
        return 0;
    }
    De cette manière, d'une part tout est résolu à la compilation, et d'autre part, si le code de création se complexifie, tu n'auras pas à te pourrir la vue avec des flags préprocesseurs dans tous les sens.

    Par contre ça demande de maîtriser sa chaîne de compilation.
    Find me on github

  10. #10
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Merci encore pour ta contribution!!
    Je comprends tout à fait l’intérêt et la facilité d'utiliser ta méthode.
    Par contre il serait peut être plus préférable d'avoir un seul fichier FactoryLibMissing.cpp contenant toutes les méthodes qui retournent des pointeurs NULL.

    Je vais tester tout ça, en parler à mes collègues et je vous tiens au courant!

    Merci encore pour tout.

  11. #11
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par darkman19320 Voir le message
    Par contre il serait peut être plus préférable d'avoir un seul fichier FactoryLibMissing.cpp contenant toutes les méthodes qui retournent des pointeurs NULL.
    Ben non, car il ne fonctionnerait que si aucune lib n'est disponible. Après il y a peut être moyen de faire plus malin avec moins de fichiers, mais faut voir avec vos use cases en effet.
    Find me on github

  12. #12
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,

    Il me semble que POCO t'évitera de réinventer la roue

  13. #13
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Bonjour tout le monde!

    Pour vous tenir au courant, nous avons décider d'utiliser la méthode avec les template et non pas avec la méthode par choix de fichier pour plusieurs raisons dont éviter d'avoir trop de fichiers (pas un vrai argument pour ma part...), et surtout, si par le plus grand des hasards et de mal chance, CMake deviendrait payant où du moins que ces sources ne soient plus disponible librement (fortement improbable).

    Je vous remercie à tous pour votre soutien.

    PS: @3DArchi merci pour le lien!! Il me servira certainement pour un autre projet, nous n'utilisons que des bibliothèques statiques dans notre cas.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Tableau et chargement dynamique de fonction
    Par chercheur111 dans le forum C
    Réponses: 1
    Dernier message: 04/07/2012, 23h24
  2. Réponses: 1
    Dernier message: 31/05/2010, 14h38
  3. [jar]chargement dynamique.
    Par Teddy-htk dans le forum API standards et tierces
    Réponses: 10
    Dernier message: 27/04/2006, 15h19
  4. Dll a chargement dynamique
    Par delire8 dans le forum C++Builder
    Réponses: 7
    Dernier message: 05/06/2003, 22h47
  5. Chargement dynamique de DLL sous Unix
    Par Willou dans le forum Autres éditeurs
    Réponses: 7
    Dernier message: 18/12/2002, 18h25

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo