[C++] Problème d'interface (JAVA)
Répondre à la discussion
Affichage des résultats 1 à 10 sur 10

[C++] Problème d'interface (JAVA)



  1. #1
    Aenonis

    [C++] Problème d'interface (JAVA)


    ------

    Bonjour tout le monde !

    J'ouvre cette discussion pour vous informer d'un problème que j'ai en C++.

    En fait, j'ai écrit une application en JAVA qui tourne super et j'ai voulu la retranscrire en C++.

    Normalement, un simple copier-coller avec les modifications qui s'imposent fonctionne.

    Mais, c'est lorsque un cycle de trois headers s'includent mutuellement. Il faut que je fasse de la déclaration préventive mais j'ai toujours eu du mal avec ce concept vu qu'en JAVA, il ne faut rien inclure, avec le système de package, ça se fait automatiquement.

    Et c'est là que se situe le problème à mon humble avis.

    Je vais d'abord vous donner la version JAVA du programme qui fonctionne parfaitement et puis la version C++ rigoureusement identique qui foire complètement.

    Le programme a été simplifié au maximum pour éviter tout effet de bord (le programme initial comptait au moins 50 classes), ce que je fais pourrait vous paraitre complètement stupide, mais c'est comme ça, il faut que ça fonctionne ainsi.

    Les codes JAVA:
    Code:
    package tests;
    
    public abstract class T
    {
        protected final int t;
        
        protected T(int t)
        {
            this.t=t;
        }
    }
    Code:
    package tests;
    
    public interface O
    {
        public abstract N getN();
    }
    Code:
    package tests;
    
    public abstract class C extends T implements O
    {
        protected C(int t)
        {
            super(t);
        }
        
        protected abstract int get();
    }
    Code:
    package tests;
    
    public class N extends C
    {
        private N(int t)
        {
            super(t);
        }
    
        public static N getInstance(int t)
        {
            return new N(t);
        }
        
        @Override
        public final N getN()
        {
            System.out.println("N.getN()");
            return this;
        }
        
        @Override
        public final int get()
        {
            return t;
        }
    }
    Code:
    package tests;
    
    public abstract class S extends C
    {
        protected S(int t)
        {
            super(t);
        }
        
        @Override
        public final N getN()
        {
            System.out.println("S.getN()");
            return N.getInstance(get());
        }
    }
    Code:
    package tests;
    
    public class P extends S
    {
        private P()
        {
            super(42);
        }
        
        public static P getInstance()
        {
            return new P();
        }
    
        @Override
        public final int get()
        {
            return t;
        }
    }
    Et la fonction main qui teste le tout:
    Code:
    package tests;
    
    public class Main
    {
        public static void main(String[] args)
        {
            T t=P.getInstance();
            System.out.println("t "+t.getClass().getName()+" adress="+t);
            O o=(O)t;
            System.out.println("o "+o.getClass().getName()+" adress="+o);
            System.out.println("Call o.getN()");
            N n=o.getN(); //affiche S.getN()
            System.out.println("End call o.getN()");
            System.out.println("n "+n.getClass().getName()+" adress="+n);
            System.out.println("Call n.get()");
            int i=n.get();
            System.out.println("End call n.get()");
            System.out.println("i = "+i+" (should be 42)");
        }
    }
    La sortie du programme est:
    Code:
    t tests.P adress=tests.P@308f5944
    o tests.P adress=tests.P@308f5944
    Call o.getN()
    S.getN()
    End call o.getN()
    n tests.N adress=tests.N@6dc98c1b
    Call n.get()
    End call n.get()
    i = 42 (should be 42)
    (les adresses varient évidemment d'une exécution à l'autre, les deux premières doivent cependant être identiques)

    C'est beau c'est propre, on fait la fête tellement ça fonctionne bien!

    Attaquons la partie C++ maintenant, je sens qu'on va se marrer...

    Les headers:
    Code:
    #ifndef T_H
    #define T_H
    
    class T
    {
    protected:
        const int t;
    protected:
        T(int t);
    public:
        virtual ~T();
    };
    
    #endif // T_H
    Code:
    #ifndef O_H
    #define O_H
    
    class N;
    
    class O
    {
    public:
        virtual N *getN() const=0;
    protected:
        O()
        {}
        virtual ~O()
        {}
    };
    
    #endif // O_H
    Code:
    #ifndef C_H
    #define C_H
    
    #include "T.h"
    #include "O.h"
    
    class C : public T, public O //AAAAAAAAAAAAAHHHHHHHHHHHHHHHH, ça fait mal :(
    {
    protected:
        C(int t);
        virtual ~C();
    protected:
        virtual int get() const=0;
    };
    
    #endif // C_H
    Code:
    #ifndef N_H
    #define N_H
    
    #include "C.h"
    
    class N : public C
    {
    private:
        N(int t);
    public:
        virtual ~N();
    public:
        static N *getInstance(int t);
    public:
        virtual N *getN() const;
        virtual int get() const;
    };
    
    #endif // N_H
    Code:
    #ifndef S_H
    #define S_H
    
    #include "C.h"
    #include "N.h"
    
    class S : public C
    {
    protected:
        S(int t);
        virtual ~S();
    public:
        virtual N *getN() const;
    };
    
    #endif // S_H
    Code:
    #ifndef P_H
    #define P_H
    
    #include "S.h"
    
    class P : public S
    {
    private:
        P();
        virtual ~P();
    public:
        static P *getInstance();
    public:
        virtual int get() const;
    };
    
    #endif // P_H
    Les sources:
    Code:
    #include "T.h"
    
    T::T(int t)
    : t(t)
    {
    }
    
    T::~T()
    {
    }
    Code:
    #include "O.h"
    
    #include <iostream>
    using namespace std;
    
    #include "N.h"
    
    N *O::getN() const //=0 je sais mais au cas où
    {
        cout << "O::getN()=0" << endl;
        return NULL;
    }
    Code:
    #include "C.h"
    
    C::C(int t)
    : T(t)
    {
    }
    
    C::~C()
    {
    }
    Code:
    #include "N.h"
    
    #include <iostream>
    using namespace std;
    
    N::N(int t)
    : C(t)
    {
    }
    
    N::~N()
    {
    }
    
    N *N::getInstance(int t)
    {
        return new N(t);
    }
    
    N *N::getN() const
    {
        cout << "N::getN()" << endl;
        return const_cast<N *>(this);
    }
    
    int N::get() const
    {
        return t;
    }
    Code:
    #include "S.h"
    
    #include <iostream>
    using namespace std;
    
    S::S(int t)
    : C(t)
    {
    }
    
    S::~S()
    {
    }
    
    N *S::getN() const
    {
        cout << "S::getN()" << endl;
        return N::getInstance(get());
    }
    Code:
    #include "P.h"
    
    P::P()
    : S(42)
    {
    }
    
    P::~P()
    {
    }
    
    P *P::getInstance()
    {
        return new P();
    }
    
    int P::get() const
    {
        return t;
    }
    Et la fonction main:
    Code:
    #include <iostream>
    using namespace std;
    
    #include <typeinfo>
    
    #include "T.h"
    #include "O.h"
    
    #include "P.h"
    
    int main()
    {
        T *t=P::getInstance();
        cout << "t " << typeid(t).name() << " adress=" << t << endl;
        O *o=(O *)t;
        cout << "o " << typeid(o).name() << " adress=" << o << endl;
        cout << "Call o->getN()" << endl;
        N *n=o->getN(); //devrait afficher S::getN() mais n'affiche rien du tout
        cout << "End call o->getN()" << endl;
        cout << "n " << typeid(n).name() << " adress=" << n << endl;
        cout << "Call n->get()" << endl;
        int i=n->get(); //plante royalement
        cout << "End call n->get()" << endl;
        cout << "i = " << i << " (should be 42)" << endl;
    
        delete n;
        delete t;
        return 0;
    }
    Voilà la sortie du programme:
    Code:
    t P1T adress=0x10c1468
    o P1O adress=0x10c1468
    Call o->getN()
    End call o->getN()
    n P1N adress=0x4042e8
    Call n->get()
    Et là le programme plante méchamment.

    D'abord, lors de l'appel de o->getN(), il n'appelle rien du tout, même pas la fonction virtuelle pure dont j'ai défini un corps (juste pour tester), donc, n pointe sur un zone mémoire aléatoire et donc lors de l'appel de n->get(), ben, le programme il ne sait plus où aller et il me dit merde.

    Voilà tout le problème.

    Si une âme sensible pouvait trouver miraculeusement la solution, je lui en serai infiniment reconnaissant.

    Ah oui, une dernière chose, pour compiler en C++, j'utilise Code::Blocks avec MinGW, peut-être que ça entre en compte, je ne sais pas!

    En vous remerciant d'avance,

    Aenonis

    -----

  2. #2
    Chanur

    Re : [C++] Problème d'interface (JAVA)

    Bonjour,

    dans le main :
    Citation Envoyé par Aenonis Voir le message
    Code:
         O *o=(O *)t;
         (...)
         N *n=o->getN(); //devrait afficher S::getN() mais n'affiche rien du tout [non : indéfini.]
         (...)
         int i=n.get() [chez moi, n est nul => segmentation fault, core dump !]

    Citation Envoyé par Aenonis Voir le message
    Et là le programme plante méchamment.
    ben oui.
    Le cast O *o=(O *)t n'a aucun sens. comment veut-tu que le compilateur devine comment transformer un objet quelconque en un autre objet quelconque ? (il n'y a pas de lien entre O et T)

    En fait, à la première ligne du main, tu devrait déclarer ton objet comme un P (puisque c'est un P). En déclarant comme public le destructeur de P, il n'y a même pas de warning à la compilation et tout s'exécute bien.

    Le compilateur peut considérer un P comme un T, il peut considérer un P comme un O, mais il ne sait pas comment transformer un T en O. Et comme tu a forcé le cast, il n'y a pas de warning. Il ne faut écrire explicitement (O *) que si on sait que le cast est possible, pas pour masquer un warning ...
    Ce qui se conçoit bien s'énonce clairement ; et les mots pour le dire arrivent aisément.

  3. #3
    Aenonis

    Re : [C++] Problème d'interface (JAVA)

    Tu es un génie

    En fait, dans mon application JAVA d'origine, dans mon esprit, toutes mes classes héritent de T (la classe mère) sauf O qui est une interface mais qui dans mon esprit, est un T (dans l'application finale, toutes les classes commencent par T pour bien dire que TO (donc O) est un T) donc le cast problématique me semblait naturel.

    Donc, j'ai juste modifié le code ainsi (grâce à ta précieuse aide):
    J'ai fait hérité O de T et j'ai supprimé le double héritage de C (T et O) au profit de O.

    Et là, ça a fonctionné comme sur des roulettes sans modifier une ligne du main (le main ne devant pas être modifié).

    Merci de m'avoir aidé, je t'en suis infiniment reconnaissant et merci d'avoir passé du temps à décortiquer mon code afin de me donner une réponse claire et précise

    Aenonis

  4. #4
    Aenonis

    Re : [C++] Problème d'interface (JAVA)

    En réfléchissant d'avantage, je me suis rappelé que le double héritage de C (T et O) doit exister, en effet, je dois garder cette relation (je ne peux pas supprimer T), vu que dans une autre partie de la hiérarchie, on aura une classe F (héritant de T), une classe FO (héritant de F et "interfaçant" O donc hérite de O), une interface B qui est similaire à O et une classe FB qui hérite de F et "interfaçant" B. Le double héritage est donc obligatoire si FO et FB doivent garder les caractéristiques de F.

    Le truc aussi, c'est que le cast problématique, le cast doit exister (vu que dans le code JAVA, ça fonctionne), l'héritage de O de T est donc obligaroire.

    Enfin, le double héritage de C de T et O, vu que O hérite de T, aura deux fois l'attribut t, ce qui pourrait poser problème en mémoire: on double l'espace mémoire réservé (en sachant que la classe T complète contient beaucoup plus d'attributs que l'entier t). De plus, dans C, FO et FB, pour accéder à t, il y a ambiguïté.

    Ta solution de remplacer T t; par P t; n'est pas à concevoir vu que je dois passer par un T (en fait, le P::getInstance() dans le code est en fait un T::getInstance() qui renvoie, dans le code simplifié un P, sachant que P représente une feuille de l'arbre qui en contient plusieurs, donc t peut être n'importe quoi).

    À propos du cast problématique, comment je fais pour ajouter à mon objet t (qui est un T) la fonction getN() dans le cas où O n'hériterait pas de T? Je ne peux pas passer par C vu que t pourrait être très bien un FO...

    Comme dis plus haut, le fait d'hériter O de T est une solution à court terme mais doublerait les attributs de T.

    Donc, je suis un peu perdu.

    Je te remercie encore de m'avoir mis sur la voie et de tes précieux conseils!

    Aenonis
    Dernière modification par Aenonis ; 01/01/2013 à 03h15.

  5. A voir en vidéo sur Futura
  6. #5
    Aenonis

    Re : [C++] Problème d'interface (JAVA)

    Re,

    J'ai encore réfléchi au problème posé et j'ai finalement trouvé une solution.

    Je ne modifie pas ma hiérarchie de classes (donc O n'hérite pas de T et C hérite de O et de T).

    Mais je modifie mon main en conséquence alors mais toujours en utilisant un objet T:
    Voilà le nouveau main (avec V définie juste après):

    Code:
    #include <iostream>
    using namespace std;
    
    #include "T.h"
    #include "N.h"
    #include "C.h"
    #include "V.h"
    
    #include "P.h"
    
    void test(T *t)
    {
        N *n=NULL;
        if (dynamic_cast<C *>(t))
        {
            cout << "P:: utilise" << endl;
            n=((C *)t)->getN();
        }
        else if (dynamic_cast<V *>(t))
        {
            cout << "V:: utilise" << endl;
            n=((V *)t)->getN();
        }
        int i=n->get();
        cout << "i = " << i << " (should be 42)" << endl;
        delete n;
    }
    
    int main()
    {
        T *t;
    
        t=P::getInstance();
        test(t);
        delete t;
    
        t=V::getInstance();
        test(t);
        delete t;
    
        return 0;
    }
    Et V:
    Code:
    #ifndef V_H
    #define V_H
    
    #include "T.h"
    #include "O.h"
    
    class V : public T, public O
    {
    protected:
        V(int t);
    public:
        virtual ~V();
    public:
        static V *getInstance();
    public:
        virtual N *getN() const;
    };
    
    #endif // V_H
    Code:
    #include "V.h"
    #include "N.h"
    
    V::V(int t)
    : T(t)
    {
    }
    
    V::~V()
    {
    }
    
    V *V::getInstance()
    {
        return new V(42);
    }
    
    N *V::getN() const
    {
        return N::getInstance(t);
    }
    La sortie du programme est magnifique:
    Code:
    P:: utilise
    i = 42 (should be 42)
    V:: utilise
    i = 42 (should be 42)
    Voilà.

    Je suppose que cette solution acceptable clôt la discussion que j'ai ouverte, vu que j'ai vu mon erreur et que j'ai agit en conséquence.

    Je te remercie encore.

    Aenonis

    PS: si tu as une solution plus jolie que le dynamic_cast, je suis preneur.
    Dernière modification par Aenonis ; 01/01/2013 à 06h47.

  7. #6
    Aenonis

    Re : [C++] Problème d'interface (JAVA)

    j'ai trouvé la solution ultime, qui ne modifie qu'une seule ligne de l'exemple de départ.

    J'ai lu un tuto du site du zéro (http://www.siteduzero.com/tutoriel-3...-de-types.html) qui parle des _cast dont le dynamic_cast.

    Et comme dernier exemple, le tuto nous parle du cross-casting, je lis, je lis, et là, j'ai l'illumination, la première de 2013 .

    Je peux caster un T* en O* avec dynamic_cast si et seulement si l'objet pointé est un O ET un T, hors, c'est ce que j'ai exactement dans mon soucis.

    Voilà le main de départ avec la seule et unique ligne modifiée:

    Code:
    #include <iostream>
    using namespace std;
    
    #include <typeinfo>
    
    #include "T.h"
    #include "O.h"
    
    #include "P.h"
    
    int main()
    {
        T *t=P::getInstance();
        cout << "t " << typeid(t).name() << " adress=" << t << endl;
        O *o=dynamic_cast<O *>(t); //anciennement O *o=(O *)t;
        cout << "o " << typeid(o).name() << " adress=" << o << endl;
        cout << "Call o->getN()" << endl;
        N *n=o->getN(); //affiche S::getN()
        cout << "End call o->getN()" << endl;
        cout << "n " << typeid(n).name() << " adress=" << n << endl;
        cout << "Call n->get()" << endl;
        int i=n->get(); //ne plante plus royalement
        cout << "End call n->get()" << endl;
        cout << "i = " << i << " (should be 42)" << endl;
    
        delete n;
        delete t;
        return 0;
    }
    Mais c'est quand même toi qui m'a donné le coup de pouce pour comprendre

    En te remerciant une dernière fois,

    Aenonis
    Dernière modification par Aenonis ; 01/01/2013 à 07h53.

  8. #7
    Chanur

    Re : [C++] Problème d'interface (JAVA)

    Bonjour,
    Pour trouver où ton programme commençait à dérailler, j'ai utilisé un débugger (gdb, en l’occurrence, mais il y en a généralement un avec chaque compilateur). Je ne saurait trop t'encourager à en faire autant.
    Quand à la solution, tu as raison, évidemment, de ne pas prendre la mienne au pied de la lettre et d'étudier en détail ce qui se passe avent de choisir la correction adéquate.
    Bon courage et bonne année.
    Ce qui se conçoit bien s'énonce clairement ; et les mots pour le dire arrivent aisément.

  9. #8
    Aenonis

    Re : [C++] Problème d'interface (JAVA)

    Je te remercie Chanur mais je n'ai jamais utilisé de débugueur, je débugue toujours à coups de printf (ou cout en C++)...

    Comment ça fonctionne en fait un débugueur?

    Je te réciproque mes vœux de bonne année et de bonne santé !

    Aenonis

  10. #9
    Chanur

    Re : [C++] Problème d'interface (JAVA)

    Citation Envoyé par Aenonis Voir le message
    je n'ai jamais utilisé de débugueur
    Essaie, tu verra ça change la vie.

    Le défaut, quand on debugue avec des printf, c'est qu'on modifie le programme qu'on debugue, et il y a parfois des effets de bord, par exemple le bug qui disparaît quand on ajoute un printf. Et en plus c'est long.

    Un debuguer permet d'exécuter un programme pas à pas en examinant la valeur des variables à chaque étape.

    Dans ton cas, j'ai lancé le programme et il a planté avec le message "Segmentation fault, core dump" (je suis sous linux, mais le docteur Watson correspond au même mécanisme (moins lisible) sous windows).
    Ça signifie que le système a enregistré un fichier core qui contient l'état du programme au moment ou il s'est planté. J'ai regardé le fichier core avec gdb, et j'ai vu qu'il s'était planté là où tu avais mis en commentaire "//plante royalement"
    J'ai affiché la valeur du pointeur n : il était nul
    J'ai relancé le programme sous débugueur en m'arrêtant à la ligne ou tu faisait "n=o->getN()" et en rentrant dans la fonction appelée, pour m'apercevoir que c'était le destructeur de P ou de T (je ne sais plus) et donc que le compilateur interprétait mal le type d'objet utilisé.

    Le tout ne m'a pas pris 3 minutes.

    gdb est un debugueur en ligne de commande, très puissant mais assez rébarbatif, mais les compilateurs intégrés avec un éditeur incluent en général un debugueur graphique où on peut placer un point d'arrêt ou surveiller une variable avec un simple clic. Franchement, ça gagne énormément de temps d'apprendre à s'en servir.
    Ce qui se conçoit bien s'énonce clairement ; et les mots pour le dire arrivent aisément.

  11. #10
    Aenonis

    Re : [C++] Problème d'interface (JAVA)

    Je te remercie, je vais regarder la documentation du débuggueur sous code::blocks et comment on l'utilise, vu que j'ai un ami qui a fait les mêmes études que moi, il va sûrement pouvoir m'aider à utiliser cet outil.

    Pendant mes études d'informatique, je détestais utiliser le débugueur vu que je ne savais pas comment l'utiliser, donc, j'ai pris la fâcheuse habitude de débuguer à coups de printf...

    Mais, maintenant que tu me dis que c'est primordial de savoir l'utiliser et que ça permet de faire de grandes choses, je vais m'y attaquer.

    Je vais garder ton message en tête afin de comprendre ce que tu as fait avec le debugueur quand j'aurai compris le fonctionnement afin de refaire ta manip.

    Merci encore.

    Aenonis

Discussions similaires

  1. Problème Java
    Par krasnaya dans le forum Internet - Réseau - Sécurité générale
    Réponses: 3
    Dernier message: 26/09/2012, 12h23
  2. probleme java
    Par GECKO00 dans le forum Programmation et langages, Algorithmique
    Réponses: 4
    Dernier message: 03/10/2011, 17h37
  3. Problème Java
    Par invitee32ad308 dans le forum Programmation et langages, Algorithmique
    Réponses: 32
    Dernier message: 23/09/2011, 18h36
  4. Probléme de protocole d'interface de scanner
    Par invite8189240b dans le forum Logiciel - Software - Open Source
    Réponses: 0
    Dernier message: 03/05/2011, 05h06
  5. Probleme java
    Par BIGFOOT176 dans le forum Logiciel - Software - Open Source
    Réponses: 1
    Dernier message: 09/11/2009, 17h36