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 :

[DLL] Membre privé d'une classe exportée en DLL


Sujet :

C++

  1. #1
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut [DLL] Membre privé d'une classe exportée en DLL
    Salut,

    J'ai codé une classe 'MyClass' dans une DLL qui expose un certain nombre de méthodes publiques. Lorsque j'ajoute un membre privé de la classe (par exemple vector<long> _test) le compilateur VC++ 2005 me sort un warning qui dit :
    class 'std::vector<_Ty>' needs to have dll_interface to be used by clients of class 'MyClass'.

    Je ne comprends pas quel est le problème...

  2. #2
    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
    Ce warning peut être ignoré si tu es sûr d'utiliser exactement la même version de Visual (service pack compris) pour les programmes qui utilisent la DLL.

    Dans le cas contraire, aucune classe exportée ne doit avoir de membre template, car cela peut causer des incompatibilités entre la DLL et le programme utilisateur.
    C'est pourquoi je suggère la même approche que COM quand il s'agit de faire de l'Orienté Objet avec les DLLs : Exporter uniquement des objets instanciés sur le tas, d'une classe dérivant d'une classe abstraite sans variables dont toutes les méthodes sont virtuelles pures. En gros, une "interface".

    Il suffit d'exporter une fonction extern "C" qui crée un tel objet sur le tas...
    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.

  3. #3
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    Merci,

    Je ne suis pas sûr de comprendre l'histoire d'utiliser la même version de visual C++.

    Vu que mon objet "vector" est véritablement compilé dans la DLL, et que je l'utilise exclusivement en interne de la classe exportée (il n'y a pas d'accesseurs sur lui dans l'interface publique) je ne vois pas pourquoi le code généré pourrait avoir une incompatibilité avec un code externe.

  4. #4
    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
    Parce que visual n'a aucune preuve qu'un vector<int> fait la même taille dans les deux versions.
    Et il a besoin de connaître cette taille pour avoir le sizeof(ta classe).
    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.

  5. #5
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Parce que visual n'a aucune preuve qu'un vector<int> fait la même taille dans les deux versions.
    Et il a besoin de connaître cette taille pour avoir le sizeof(ta classe).
    Mmmmouais...d'accord alors dans ce cas est-ce que je peux considérer que ma DLL est compatible avec n'importe quelle version à condition de se limiter à l'instanciation de la classe et l'utilisation de son interface publique (pas de copie notamment) ?
    Ou bien est ce que ça va au delà - c'est à dire qu'il est besoin de connaître à l'avance la taille du type du "vector" - dans ce cas je ne comprends pas pourquoi dans un programme non DLL une classe peut très bien s'accommoder d'avoir un membre vector dont elle ne connait pas la taille à l'avance (le sizeof de la classe peut varier avec le nombre d'éléments du vector) et une classe DLL ne le pourrait pas.

  6. #6
    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
    Citation Envoyé par tnarol Voir le message
    Mmmmouais...d'accord alors dans ce cas est-ce que je peux considérer que ma DLL est compatible avec n'importe quelle version à condition de se limiter à l'instanciation de la classe et l'utilisation de son interface publique (pas de copie notamment) ?
    Tu peux, mais dans ce cas, tu en es plus sûr si tu passes par une classe abstraite avec des fonctions virtuelles. Ici, la virtualité n'aura aucun coût en performance, car un compilo intelligent peut fusionner l'overhead de virtualité à celui de la DLL.
    Ou bien est ce que ça va au delà - c'est à dire qu'il est besoin de connaître à l'avance la taille du type du "vector" - dans ce cas je ne comprends pas pourquoi dans un programme non DLL une classe peut très bien s'accommoder d'avoir un membre vector dont elle ne connait pas la taille à l'avance (le sizeof de la classe peut varier avec le nombre d'éléments du vector) et une classe DLL ne le pourrait pas.
    Si tu fais des manipulations de pointeur, le compilo a en effet besoin de connaître à l'avance la taille. Ça marche quand tu ne fais pas de DLL, car à ce moment le compilo sait que c'est toujours SA version de vector qui va être utilisée, car on n'utilise jamais deux versions différentes de la STL dans le même projet: Pour ça, il faudrait compiler différentes parties du projet avec des versions différentes du compilo!
    Mais pour une DLL, le compilo n'a pas cette garantie.

    ...Un truc intéressant que je ne sais pas, c'est comment il réagit avec les bibliothèques statiques, par contre.
    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.

  7. #7
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    En fait si j'ai bien compris un problème peut se poser *uniquement* si le programme utilisateur a la mauvaise idée de faire une manipulation qui implique que ma classe va devoir faire une opération non interne sur son vecteur (par exemple une copie) - car dans ce cas c'est la méthode de la classe vector de l'appelant qui est utilisée.

    Sinon pour la solution de l'exportation d'objets instanciés sur le tas, je vois bien comment écrire les fonctions extern "c" qui font un new et un delete mais je ne vois pas quelle(s) classe(s) exporter : l'interface avec les méthodes virtuelles pures, la classe qui en dérive, ou les deux ? Tu pourrais donner un squelette de code pour illustrer ça ?

  8. #8
    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
    Citation Envoyé par tnarol Voir le message
    En fait si j'ai bien compris un problème peut se poser *uniquement* si le programme utilisateur a la mauvaise idée de faire une manipulation qui implique que ma classe va devoir faire une opération non interne sur son vecteur (par exemple une copie) - car dans ce cas c'est la méthode de la classe vector de l'appelant qui est utilisée.
    Pas seulement. Tu peux avoir un problème de mémoire si la classe vector d'une version n'a pas la même taille que l'autre. Imagine le carnage si tu fais un héritage, ou même simplement si tu dois accéder à une variable déclarée après le vector!

    Sinon pour la solution de l'exportation d'objets instanciés sur le tas, je vois bien comment écrire les fonctions extern "c" qui font un new et un delete mais je ne vois pas quelle(s) classe(s) exporter : l'interface avec les méthodes virtuelles pures, la classe qui en dérive, ou les deux ? Tu pourrais donner un squelette de code pour illustrer ça ?
    Dans l'interface, il n'y a rien à exporter, seulement la déclarer et la définir entièrement dans son header.
    Et tu exportes juste les fonctions extern "c".

    Exemple:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //MaDLL.h
     
    class IUneInterface
    {
    public:
    	virtual void UneMethode(void) = 0;
    };
     
    extern "C" MADLL_API IUneInterface * __stdcall CreerUnObjet( parametres );
    extern "C" MADLL_API void __stdcall DetruireUnObjet( IUneInterface * );
    Une autre méthode pour la destruction est d'avoir une fonction membre virtuelle pure DeleteThis(), qui permet de ne pas avoir à exporter la fonction DetruireUnObjet().

    Sous Windows, pour une plus grande compatibilité, on peut se servir des macros COM pour définir ses propres interfaces, même des interfaces non-COM. Avec ces macros, on est sûr que ça marche pour tous les compilos C++, et c'est utilisable même en C:
    Code C/C++ : 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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /* MaDLL.h */
    #ifndef H_MADLL_20081202_1103
    #define H_MADLL_20081202_1103
     
    #include <objbase.h>
     
    #ifndef EXTERN_C
    #ifdef __cplusplus
    #define EXTERN_C extern "C"
    #else
    #define EXTERN_C extern
    #endif
    #endif
     
    /*Définition de build pour MADLL_API.
      Le define MADLL_EXPORTS (ou BUILDING_MADLL) doit être
      dans les options du projet de la DLL.*/
    #ifdef MADLL_EXPORTS
    #define MADLL_API __declspec(dllexport)
    #else
    #define MADLL_API __declspec(dllimport)
    #endif
     
    DECLARE_INTERFACE(IUneInterface)
    {
    	STDMETHOD_(void, UneMethode)(void) PURE;
    };
     
    EXTERN_C MADLL_API IUneInterface * __stdcall CreerUnObjet( parametres );
    EXTERN_C MADLL_API void __stdcall DetruireUnObjet( IUneInterface * );
     
    #endif /* H_MADLL_20081202_1103 */
    Code C++ : 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
    24
    25
    26
    27
    28
    29
    30
    31
    //UneClasse.cpp
     
    #include "MaDLL.h"
     
    class UneClasse : public IUneInterface
    {
    public:
    	UneClasse( parametres );
     
    	STDMETHOD_(void, UneMethode)(void); //Note l'absence de PURE ici
    };
     
    UneClasse::UneClasse( parametres )
    {
    	...
    }
     
    STDMETHODIMP_(void) UneClasse::UneMethode(void)
    {
    	...
    }
     
    EXTERN_C MADLL_API IUneInterface * __stdcall CreerUnObjet( parametres )
    {
    	return new UneClasse(parametres);
    }
     
    EXTERN_C MADLL_API void __stdcall DetruireUnObjet( IUneInterface * pThis )
    {
    	delete pThis;
    }
    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.

  9. #9
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    Merci énormément !!! Ca fonctionne très bien.

    Juste une dernière question qui me taraude... n'y a t-il pas le même genre de restrictions concernant les arguments des méthodes de l'interface ?

    C'est à dire y a t-il un problème à ce qu'une des méthodes virtuelle pures de l'interface reçoive une référence sur un vecteur par exemple ? En effet là aussi on transporte la classe vector entre le monde de l'appelant et le monde de la DLL.

  10. #10
    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
    En effet, il ne faut pas qu'il y ait un vector en paramètre, ni à aucun endroit dans l'interface publique de la DLL.

    Par contre, rien ne t'empêche de passer un pointeur vers une classe parfaitement définie, ou un pointeur d'interface.
    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.

  11. #11
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Par contre, rien ne t'empêche de passer une interface.
    Ah c'est embêtant ça... Quand tu parle de passer une interface cela veut dire que je dois "encapsuler" le vecteur dans une classe côté appelant qui implémenterait une interface comme celle-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class IVectString
    {
        virtual string getNextString() = 0;
    }
    D'ailleurs est-ce que "string" est valide dans une interface ou bien il souffre des mêmes restrictions que vector ??? auquel cas il ne reste guère plus que les bons vieux "char*"

  12. #12
    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
    string souffre des mêmes restrictions.
    Par contre, je ne connais pas les types d'autres bibliothèques, comme wxString.

    Sous COM, on utilise des BSTR, qui sont des chaînes UTF-16 au format de Visual Basic. On utilise aussi des SAFEARRAY pour les tableaux, ainsi que des types particuliers pour les dates et les sommes d'argent (Currency).

    L'inconvénient des BSTR, c'est qu'elles sont PITA à utiliser si le reste de ton programme n'est pas en Unicode.
    Enfin, tu peux toujours utiliser un tableau de char pour tes chaînes, du moment qu'ils sont tous alloués avec une fonction de la même DLL (dans le cas de COM, on utilise alors CoTaskMemAlloc()).
    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.

  13. #13
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Enfin, tu peux toujours utiliser un tableau de char pour tes chaînes
    Est ce que c'est bon si j'utilise l'interface suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class ITabString {
    public :
       virtual unsigned int size() const = 0;
       virtual const char* getString(unsigned int idx) const = 0;
    }
    Implémentée côté appelant de la DLL comme ça :
    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
    24
    25
    26
    27
     
    class TabStringImpl : public ITabString 
    {
    public :
    TabStringImpl(const vector<string>& initTabString);
    virtual ~TabStringImpl();
    virtual size_t size() const;
    virtual const char* getString(unsigned int idx) const;
    protected:
    vector<string> _myVectString;
    }
     
    TabStringImpl::TabStringImpl(const vector<string>& initTabString) : _myVectString(initTabString) { }
     
    size_t TabStringImpl::size() const { return _myVectString.size() }
     
    const char* TabStringImpl::getString(unsigned int idx) const {
     
       if (idx < _myVectString.size())
       {
           return myVectString[idx].data();
       }
       else
       {
          return NULL;
       }
    }

  14. #14
    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
    Ta classe étant apparemment immuable, je dirais que c'est bon, puisque les strings ne peuvent être modifiées après un appel à getString().

    Par contre, j'utiliserais c_str() plutôt que data(), mais c'est mineur.
    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.

  15. #15
    Membre régulier
    Inscrit en
    Mai 2006
    Messages
    330
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 330
    Points : 85
    Points
    85
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Par contre, j'utiliserais c_str() plutôt que data(), mais c'est mineur.
    Je n'ai jamais saisi la différence entre les deux...

  16. #16
    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
    Citation Envoyé par tnarol Voir le message
    Je n'ai jamais saisi la différence entre les deux...
    pour te répondre

Discussions similaires

  1. Réponses: 3
    Dernier message: 02/01/2015, 02h30
  2. Modifier un membre privé d'une classe
    Par Haythem17 dans le forum Débuter
    Réponses: 3
    Dernier message: 22/03/2014, 22h29
  3. Réponses: 2
    Dernier message: 18/04/2012, 17h47
  4. reccupérer un membre privé d'une classe
    Par ouinih dans le forum C++
    Réponses: 10
    Dernier message: 16/08/2007, 11h37
  5. Réponses: 8
    Dernier message: 22/12/2004, 22h57

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