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 :

[C++ / DLL] Quelques questions concernant les templates


Sujet :

C++

  1. #1
    Membre habitué Avatar de poussinphp
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    428
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 428
    Points : 176
    Points
    176
    Par défaut [C++ / DLL] Quelques questions concernant les templates
    Bonjour à tous,

    J'ouvre ce topic pour vous demander quelques précisions concernant l'utilisation de template en c++ avec des librairies dynamiques (dll). J'ai rencontré différents problèmes et je en sais pas si c'est un souci de configuration, de méconnaissance sur le language ou une limite technique (je suis novice en c++ ).

    J'ai comme projet de faire un petit moteur de jeu c++. Celui-ci ce défini par une dll qui contient les classes de mon moteur (gestionnaire de ressource, gestionnaire d'événements, classe de base de sprite...) et j'ai un petit executable qui va utiliser et enrichir les bases du moteur pour ses besoin (spécialisation de sprite, regles pour le jeu etc...).

    1) Premier cas : J'ai tenté de faire un service locator avec un template de façon à accèder à mes services comme j'ai l'habitude de le faire (je viens du monde C#).
    J'avais donc une classe ServiceLocator qui contient un membre statique std::map<string, void*> avec 2 methodes statiques Register et GetService. Lorsque je l'utilisais dans mon executable, aucun soucis. Dans la DLL.... L'opérateur [] plantais avec un "violation access memory", j'ai donc changé par la création d'un std::pair puis de faire un insert. ça fonctionnais sauf que la, j'ai l'impression que ma map statique était instancié 2 fois ! (une fois dans l'exe, une fois dans la dll et je le voyait car le nombre d'éléments dans la map était différents). Pourtant l'exe utilise la dll, donc devrais être dans le même processus, je trouve ça bizarre... J'ai donc laissé tombé car ça me prennais la tête et fais autrement mais j'aimerais bien comprendre pourquoi et utiliser une façon plus générique de faire les choses...

    2) Second cas : Je souhaitais utiliser LUA pour scripter le comportement de mon jeu de façon dynamique. Après quelques recherches, j'ai trouvé une librairie (luabind) qui me permet d'utiliser mes classes c++ dans mes scripts LUA. Cette librairie statique est incluse dans ma dll. Et là, même souffrance. Le code marche niquel dans l'exe mais dans la DLL ça plante avec une erreur R6010 (spécifique à LUA peut-être) au code c++ :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    luabind::module(state) [
        luabind::class_<MyClass>("MyClass")
          .def(luabind::constructor<int>())
          .def("print", &MyClass::print)
    Encore une histoire de template semblerait...

    Dans tout ça, j'ai l'impression que l'utilisation de template est réservé uniquement aux executables mais ça me gêne car j'aimerais encapsuler certains comportements et découpler la partie jeu (executable) de la partie métier (librairie). Je ne souhaite pas faire un gros executable de 50Mo et pouvoir découper mes parties comme j'aurais pu le faire en C# et j'aimeria spouvoir réutiliser mon moteur pour faire un editeur de niveau par exemple. Ma question est de savoir si mon approche est bonne en c++ et s'il est interdit d'utiliser les templates dans les librairies, si c'est mon compilateur vc++ 2013 qui est bugué ou si il y a quelque chose de particulier pour utiliser les templates dans les librairies...

    Je trouve beaucoup de documentation sur les templates mais pas vraiment grand chose sur leur utilisation dans des librairies.


    Merci par avance pour vos réponses

  2. #2
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 031
    Points : 11 477
    Points
    11 477
    Billets dans le blog
    11
    Par défaut
    membre statique std::map<string, void*>


    Déjà, les statiques c'est le mal, et tu en rencontres une des causes : les DLL sont dans leur propre espace mémoire donc tes statiques sont bel et bien instanciées 2 fois : dans l'appli et dans la DLL.
    Ensuite les void* c'est le mal, en C++ il y a toujours moyen de faire mieux (avec le polymorphisme, par exemple).

    Par rapport à ton problème, évite les variables statiques et ça devrait mieux se passer.
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  3. #3
    Membre habitué Avatar de poussinphp
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    428
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 428
    Points : 176
    Points
    176
    Par défaut
    Merci pour ta réponse

    Si je comprend bien, il faut pas utiliser les statiques mais comment implémenter correctement un servicelocator ? Le pattern (comme je l'utilise en c#) est constitué d'une map (dictionnary) statique qui stocke les objets que l'ont à enregistré et qui permet de les recuperer facilement avec un getter. Du coup, si je souhaite exposer une classe abstraite (interface) sur mon getter dans mon executable IElement et définir le service avec mon implémentation MyElement. de façon que :

    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
     
    DLL :
    // Methode init du moteur...
    {
    ...
    Locator::Register<IElement>(new MyElement());
    ...
    }
     
    EXE :
    {
    ...
    IElement *tata = Locator::Resolve<IElement>();
    ...
    }
    on peux pas le faire ?

    Autre element, tu me dis qu'il ne faut pas utiliser les void*, cependant, imaginont le cas suivant :
    - Ma librairie utilise une autre librairie X.
    - X contient une classe Y.
    Dans ma dll, j'ai un .h tel que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class Test
    {
    public:
    inline Test() { m_tata = new Y(); }
    virtual ~Test() {}
    private:
    Y *m_tata;
    }
    - Dans mon exe j'ai besoin de Test, donc je met à dispo test.hpp.
    - Dans l'exe j'aurais donc besoin de Y.hpp car test.hpp l'utilise.

    Comment puis-je encapsuler m_tata de façon à ce que l'executable n'ai pas besoin de Y ? à part le void* et un static_cast<> dans le .cpp je ne vois pas comment faire autrement

    Merci !

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Pourquoi le locator aurait besoin d'être global alors qu'il suffirait que l'application crée le sien ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Membre habitué Avatar de poussinphp
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    428
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 428
    Points : 176
    Points
    176
    Par défaut
    Oui effectivement mais ce que je fais (possible que ça soit pas bon ) :

    - main crée Engine puis l'initialize.
    - Dans la méthode d'initialisation, Engine va enregistrer ses différents services qui vont êtres utilisé un peu partout (ResourceManager ec...). Je dois pouvoir réutiliser les resources déjà existante.

    Dans l'application, j'en ai aussi besoin car si je veux charger une ressource (une image par exemple), j'utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Resource *res = Locator::Resolve<ResourceManager>().Load("myPicture.png");
    Si je crée un text dans la dll (affichage du fps), je dois pouvoir réutiliser la ressource dans un autre objet dans l'exe (afficher un "coucou"). Dans mon cas, si je dois utiliser 2 fois "myPicture.png", je devrais avoir 2 reference sur le pointeur de ma ressource et pas charger 2 fois la ressource même si elle a une référence dan sla dll et une autre dans l'exe.

    Si je devais l'instancier, et avoir qu'une seule instance, j'aurais 2 choix :
    - Créer un singleton de Locator => Locator::getInstance()-><ResourceManager>().Load("myPicture.png"); => pas pratique en écriture et ça retire l'interet du servicelocator. autant faire des getter pour chaque service.
    - Passer Engine partout. => lourd à gerer et pas forcément utile dans toute les méthodes...

    Après, comme je disais, si je souhaite que le moteur m'initialize les services et que je puisse les utiliser aussi bien dans la dll que dans l'exe. ça peux vous paraitre bizarre mais je fais comme ça en C# quand j'en ai besoin. Après, comme je le disais, je suis novice en c++ et j'essaye peut-être d'adapter une façon de faire d'un autre langage qui n'est pas adapté en c++.

    Comment vous y prennez-vous dans ce cas ? Vous créer tout dans l'exe puis vous le passer à chaque fois? j'ai pas envie de devoir fournir une api ou on dois renseigner 50 lignes avant de pouvoir utiliser quelque chose... enfin... c'ets mon souhait, après faudra que je m'adapte au langage

    Merci !

  6. #6
    Membre habitué Avatar de poussinphp
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    428
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 428
    Points : 176
    Points
    176
    Par défaut
    J'ai bien réfléchit et je me pose une question :

    ça vous semblerait pertinent de créer une classe Locator qui contiendrais la map le register et le resolve mais qui serais un membre de engine ? Du coup, il faudrait que je passe l'instance de engine à chaque component pour avoir accès aux services. Ca ne dois pas être couteux en mémoire d'avoir un pointeur / classe... (Je suis toujours dans une recherche de solution pour arriver à utiliser mes templates et éviter les erreurs en retirant les static car vous m'avez dit que c'est pas bien de les utiliser... ).

    Du coup, pour mes singletons (qui ont l'air de marcher) je fais comment si j'ai plus de statique? ça deviens prise de tête tout ça

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    A priori, tu ne fais pas de singleton: tu utilises l'instance de Engine.

    l'utilisateur va créer une Engine. Tu le sais, vu que tu l'exiges. Du coup, sers-t'en
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  8. #8
    Membre habitué Avatar de poussinphp
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    428
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 428
    Points : 176
    Points
    176
    Par défaut
    Tu as parfaitement raison. C'est juste mon petit côté "découpage" qui me fais défaut.

    Je trouvais ça curieux de devoir passer ma classe engine à chaque component pour récuperer le service d'affichage ou de chargement de ressources.

    Pour le module LUA parcontre, va falloir que je planche un peu plus car ça m'embetterais beaucoup de devoir écrire mes definitions d'export lua dans l'executable. C'est une fonctionnalité qui dois être intégré dans ma dll.

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Il faut bien prendre conscience des différences entre les génériques .NET et les templates C++.
    Bien que syntaxiquement assez proches, ils sont quand même assez différents.
    J'ai comme projet de faire un petit moteur de jeu c++. Celui-ci ce défini par une dll qui contient les classes de mon moteur
    Attention, contrairement à .NET, l'utilisation de classes entre modules (exe et dll) est assez problématique. Et pour ce qui est des templates, c'est même pas la peine s'essayer.
    Le seul moyen de faire ce type de partage, c'est d'utiliser exactement la même chaine de compilation, configurée exactement de la même manière, pour générer à la fois les exe et les dll (les modules qui se partageront ces classes). Cela rend le découpage en exe et dlls bien peu utile du coup.
    Moi, pour ce type de découpage, je préconise l'utilisation d'une API C, donc procédurale, quit à enrober cette API avec des classes/templates de wrapping qui seront compilés avec chaque module.
    Cela change radicalement le mode de penser l'API entre l'exe et la dll, au moins au niveau de l'implémentation.

    classe ServiceLocator qui contient un membre statique
    Attention, votre manière d'implémenter un ServiceLocator n'est pas correct/sûr même en .NET.
    Les statics .NET ne sont unique que dans un AppDomain. Une application .NET peut avoir plusieurs AppDomain (gestion de version multiple d'assemblies, continuité de service lors de mise à jour de source en ASP.NET non déployé, etc...)

    j'ai l'impression que ma map statique était instancié 2 fois
    Bonne analyses, chaque fichier PE dispose d'une section avec les variables globales (une statique, c'est un variable globale carrosée). Quand le loader de l'OS charge la Dll, il mappe chaque section du module(dll) dans une zone particulière de l'espace d'adressage et patche les adresses dans le code exécutable pour qu'elles pointent sur les zones mappées car elles changent pour chaque processus. Chaque Dll dispose donc des données bien à elle.

    Pourtant l'exe utilise la dll, donc devrais être dans le même processus, je trouve ça bizarre
    Oui, mais c'est chacun s'occupe de ses données et les moutons seront bien gardés.

    J'ai donc laissé tombé car ça me prennais la tête et fais autrement mais j'aimerais bien comprendre pourquoi et utiliser une façon plus générique de faire les choses...
    J'espère avoir été assez clair sur le comment. Pour le pourquoi, bin en code natif, on éviter de faire des couplages forts entre modules pour pouvoir les faire évolués séparément sans trop de problème.

    j'aimerais encapsuler certains comportements et découpler la partie jeu (executable) de la partie métier (librairie).
    Pour être vraiment bien indépendant, ill vous faudra passer par une API C, au moins au niveau échange entre les 2 modules.

    Ma question est de savoir si mon approche est bonne en c++ et s'il est interdit d'utiliser les templates dans les librairies
    Vous ne pouvez pas facilement utiliser de template pour une API de Dll. Mais vous pouvez les utiliser facilement dans chaque module.
    Il faut bien faire la différence entre générique et template.
    Les génériques c'est au runtime, les templates c'est au compile-time.

    les DLL sont dans leur propre espace mémoire
    Qu'entendez-vous par "espace mémoire" ?
    Les Dll et l'exécutable partage exactement le même espace d'adressage mémoire, c'est un seul et même processus.
    Le "segment" de données statiques pour la Dll et pour l'exécutable peuvent même être le même si leurs caractéristiques (Read/Write, Partageable/NonPartageable, Paged...) sont identiques. Mais le loader ne va pas fusionner par une action du saint esprit les variables globales (déjà que le linker le fait pas toujours pour un même module).

    j'ai pas envie de devoir fournir une api ou on dois renseigner 50 lignes avant de pouvoir utiliser quelque chose
    Je vous propose de concevoir des templates comme des classes de wrapping de l'API C, plutôt.

    Je suis toujours dans une recherche de solution pour arriver à utiliser mes templates et éviter les erreurs en retirant les static car vous m'avez dit que c'est pas bien de les utiliser
    Commencez par concevoir une API C, donc sans templates ni classes.

  10. #10
    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 bacelar Voir le message
    Commencez par concevoir une API C, donc sans templates ni classes.
    Je ne suis que partiellement d'accord avec ça. Les API en C, c'est bien quand on veut proposer une interface multi-langage et/ou distribuer une bibliothèque propriétaire sans l'application. Si ce n'est pas nécessaire, c'est dommage de se passer des références et des structures de données standards, c'est vraiment très contraignant. Garantir pour la livraison que l'exécutable et la DLL sont compilés en même temps avec la même chaîne et sur la même machine, ce n'est pas une contrainte énorme. La question se posera si on veut distribuer la DLL sans son code source à des personnes tierces qui vont créer des exécutables s'en servant. Si on n'a pas ce besoin, dommage de se casser les reins avec une API en C.

    Pour les statiques, il faut bien comprendre les mécanismes de C++ sur les bibliothèques dynamiques, et comment sont stockées et partagées les données statiques. Dire que les statiques c'est mal, c'est correct, mais ça n'aide pas forcément : si on utilise une bibliothèque tierce qui utilise des statiques, et qu'on veut l'embarquer dans une seule DLL, comment fait-on ? On ré-écrit tout ? Sûrement pas.

    Il est important qu'un symbole statique ne soient pas présent "deux fois". Par conséquent, si pour une quelconque raison l'exécutable doit accéder à une donnée statique instanciée par la DLL, elle doit le faire en utiliser une API qui ne contient pas la déclaration de cette donnée statique (sous peine de créer une seconde version), mais seulement un moyen de récupérer une référence dessus. Si tu utilises une bibliothèque tierce qui est codée avec des éléments statiques, débrouille toi pour que aucun header qui ne contienne la déclaration d'un tel élément ne soit utilisé dans le code de l'exécutable.

    Pour ton problème de Lua, je ne crois pas que ça aie à voir avec les templates, mais plutôt avec un manque de maîtrise du flot d'exécution ton code Lua. Si tu utilises des threads, tu peux rencontrer ce type de problèmes. Les problèmes de templates se manifestent à la compilation ou à l'édition de lien, rarement (si ce n'est jamais) à l'exécution.
    Find me on github

  11. #11
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Sinon, entre les problèmes de l'API C++ et les complications de l'API C, on peut faire une API COM ou un truc similaire. C'est fait pour être utilisable d'un langage à l'autre, et il y a des ponts entre COM et .Net...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Membre habitué Avatar de poussinphp
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    428
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 428
    Points : 176
    Points
    176
    Par défaut
    Bonjour,

    Avec quelques expériences dans un projet de test, j'ai pu comprendre la mécanique des statiques. On peut utiliser des statiques dans une dll à condition que l'initialisation soit en Lazy. J'ai donc modifié ma notion de ServiceLocator et celui-ci contient des getters vers des singletons qui sont initialisés par l'application au premier appel et non pas au chargement de la dll, ça a réoslu tout mes problèmes

    Pour ce qui est du couplage plus faible, j'ai opté pour la forward déclaration et j'ai donc l'include de l'objet qui ets membre privé dans le .cpp. ça me permet de fournir mes headers sans pour autant imposer à celui qui utilisera ma librairie d'inclure les headers des autres librairies que j'utilise en interne.

    Pour ce qui est de LUA, j'ai trouvé un bug dans la librairie que j'utilise, j'ai corrigé l'anomalie et ça fonctionne bien maintenant. J'ai juste besoin d'une mini class de wrapping lorsque j'ai des méthodes virtuel pour qu'il puisse appeller la bonne classe (il se perdait et savais pas s'il devais appeller la méthode de la classe mère ou celui de la classe fille).


    Merci pour vos conseils !

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

Discussions similaires

  1. question concernant les fichier.bin
    Par argon dans le forum Autres Logiciels
    Réponses: 10
    Dernier message: 27/08/2005, 17h44
  2. Questions concernant les études supérieures et travails
    Par Vivian Pennel dans le forum Etudes
    Réponses: 25
    Dernier message: 21/06/2005, 15h23
  3. Réponses: 11
    Dernier message: 21/06/2005, 10h16
  4. [Débutant] Deux questions concernants les vues
    Par 13obscur dans le forum Eclipse Platform
    Réponses: 1
    Dernier message: 19/04/2005, 14h29
  5. Réponses: 7
    Dernier message: 10/09/2004, 14h28

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