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 :

Classe virtuelle et casting après un échange socket


Sujet :

C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    410
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2004
    Messages : 410
    Points : 361
    Points
    361
    Par défaut Classe virtuelle et casting après un échange socket
    Bonjour,
    Voila mon problème. J'ai une application qui se baserait sur une classe Client/Server qui fait transiter des tickets (une structure de donnée) au travers de sockets (plus un broadcast mpi mais ça c'est un détails).


    Voici mon pseudo code.
    Je définie une classe abstraite ticket:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    class Ticket
    {
    protected:
        int type;
        double duration;
    public:
        Ticket();
     
        virtual std::string create_display() const=0;
     
        // Plus autres methodes virtuelles
        // ...
    };
    Exemple coté client:
    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
     
    void ClientServer::client_loop()
    {
        void* buf_ticket=malloc(max_size_ticket());
        memset(buf_ticket,0,max_size_ticket());
     
        while (true)
        {
            socket_mgr_recv_server(handle,buf_ticket,max_size_ticket());
     
            // Quel est le bon cast qui me permet de récupérer le vtable
            // et appeler la bonne méthode (ex: create_display)
            Ticket* ticket=CAST?(buf_ticket);
     
            printf("Display ticket: %s\n",ticket->create_display().c_str());
     
            process_ticket(ticket);
     
            // ...
        }
    }
    Tout est dans le commentaire.
    Quel cast me permet de donner la bonne forme à mon objet qui a transité par socket?
    Sachant qu'en plus il y a un mécanisme d'échange MPI qui n'est pas écrit dans le pseudo code. Mais l'idée est là. On fait transiter une classe comme un void* et de l'autre coté (dans un autre process) on veut retrouver la bonne classe fille.
    Est ce que c'est possible?
    Si oui quel cast (ou autre mécanisme) me permet le faire?

    Merci d'avance

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    410
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2004
    Messages : 410
    Points : 361
    Points
    361
    Par défaut
    Déjà en lisant un peu des infos sur le vtable, je me rends compte qu'une première contrainte à tout mon problèmes est qu'il va falloir que client et server soit la même application et non deux applications séparées. Sinon les pointeurs vtable seront différents.

  3. #3
    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
    C'est pour ça que généralement on ne transmet rien sous une forme qui contient des pointeurs. Il convient de sérialiser les données qu'on veut transmettre.
    Ici vu qu'on transmet des données de type variable, il faut envoyer en plus une donnée indiquant leur type (index dans une table, nom en ASCII, etc.)
    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.

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    410
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2004
    Messages : 410
    Points : 361
    Points
    361
    Par défaut
    Salut Médinoc,
    merci pour ta réponse.
    En ce qui concerne la contrainte d'avoir la même appli pour faire client et server ce n'est pas un problème car c'est acquis de fait dans la façon d'implémenter le code de calcul.
    Dans ce cas : Est ce que un static_cast<Ticket*>(buf_ticket) me permettrait d'obtenir le résultat escompté?

    EDIT: je vais laisser le ticket comme étant une structure très basique (conteneur de type primitifs) avec un "int type" pour le définir.
    Merci encore.

  5. #5
    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
    Tu as le choix entre "structure très basique" ou carrément "écriture à la main dans un buffer, puis envoi du buffer". Sachant que le premier choix s'expose à des incompatibilités si tu transmets entre des machines qui ont différentes endianness et/ou contraintes d'alignement.
    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.

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par reptils Voir le message
    Bonjour,
    Voila mon problème. J'ai une application qui se baserait sur une classe Client/Server qui fait transiter des tickets (une structure de donnée) au travers de sockets (plus un broadcast mpi mais ça c'est un détails).


    Voici mon pseudo code.
    Je définie une classe abstraite ticket:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    class Ticket
    {
    protected:
        int type;
        double duration;
    public:
        Ticket();
     
        virtual std::string create_display() const=0;
     
        // Plus autres methodes virtuelles
        // ...
    };
    Humm... Rien qu'un tel code me fait affreusement peur du point de vue conceptuel

    A la lecture de ce code, je vois le fait "gros comme une maison" que tu vas finir ton projet avec un tas de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if(ptr->type() == XXX){
        TiketDerivee * temp = dynamic_cast<TiketDerivee *>(ptr);
    }else if(ptr->type()== YYY){
        AutreTiketDerivee * temp = dynamic_cast<AutreTiketDerivee *>(ptr);
    }
    Laisses moi dire que tu t'engages là dans une bien mauvaise direction, avant même de commencer

    Comprend mois bien: il est plus que probable qu'il soit, à différents endroits de ton code, intéressant de choisir le type réel d'un ticket sur base d'une valeur numérique quelconque, mais cette valeur numérique ne devrait en aucun cas se retrouver dans le ticket, sous peine de se terminer par une répétition "jusqu'à plus soif" du code que je viens de présenter.

    Je vois, de manière générale, deux cas dans lesquels cette valeur énumérée sera utile:
    1. Au niveau d'une "fabrique", pour décider du type de ticket à créer
    2. Dans un processus de sérialisation/ dé sérialisation des données (par exemple, lorsqu'il faut les transmettre par socket).
    Autrement, laisse le ticket savoir lui-même de quel type il est... Il le saura beaucoup mieux que toi, et cela t'évitera bien tu soucis

    Les deux mots clés à retenir dans ce but sont: polymorphisme et double dispatch
    Exemple coté client:
    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
     
    void ClientServer::client_loop()
    {
        void* buf_ticket=malloc(max_size_ticket());
        memset(buf_ticket,0,max_size_ticket());
     
        while (true)
        {
            socket_mgr_recv_server(handle,buf_ticket,max_size_ticket());
     
            // Quel est le bon cast qui me permet de récupérer le vtable
            // et appeler la bonne méthode (ex: create_display)
            Ticket* ticket=CAST?(buf_ticket);
     
            printf("Display ticket: %s\n",ticket->create_display().c_str());
     
            process_ticket(ticket);
     
            // ...
        }
    }
    Ce code là est presque encore pire...

    Un pointeur sur void, malloc, memcpy... ca me paraît être une approche beaucoup trop C style à mon gout.

    En plus, vu que ta classe Ticket a clairement sémantique d'entité, tu ne peux pas envisager de transmettre le ticket en lui-même sur le réseau.

    Tu ne peux envisager de transmettre que les données qui le composent!!!
    Tout est dans le commentaire.
    Quel cast me permet de donner la bonne forme à mon objet qui a transité par socket?
    Fais moi plaisir, oublie TOUT DE SUITE cette idée!!!

    Dis toi que, même si le code du client et celui du serveur sont très proches et utilisent exactement les même données métiers, même si tu n'as qu'une seule application que tu peux lancer en mode "client" ou en mode "serveur" selon un choix établi le simple fait que tu auras d'office une instance de ton application qui agira comme serveur et une instance de ton application qui agira comme client (sans doute sur des machines séparée!!!) rend toute tentative de récupérer la vtable de tes objets au mieux particulièrement risquée, au pire carrément dangereuse.

    La raison de cette imprécation est simple:

    Tu ne peux envisager le transtypage d'un pointeur sur un type donné vers un pointeur sur char (ou sur void, bien que cela ne me plaise pas du tout) et retour que si le type en question des une classe ou une structure POD (Plain Old Data, l'équivalent des structures C style).

    Une classe ayant sémantique d'entité n'est clairement pas une classe POD.

    A partir de là, tout ce que tu pourras essayer dans le même ordre d'idée t’emmènera droit dans un mur (la seule question en suspend étant de savoir à quelle vitesse tu fonceras dedans )
    Sachant qu'en plus il y a un mécanisme d'échange MPI qui n'est pas écrit dans le pseudo code. Mais l'idée est là. On fait transiter une classe comme un void* et de l'autre coté (dans un autre process) on veut retrouver la bonne classe fille.
    Est ce que c'est possible?
    Après la réponse longue, la réponse courte: non
    Si oui quel cast (ou autre mécanisme) me permet le faire?
    Aucun, parce que ce n'est pas possible.

    Comme Médinoc l'a si bien fait remarquer, l'idée de base est de sérialiser toutes les informations relatives au ticket d'un coté avant de les transmettre et dé sérialiser ces informations afin de "recréer" le ticket de l'autre coté
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    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
    Bonsoir,

    juste pour corroborer les propos ci-dessus : on ne transmet sur le réseau que des données qui permettent de créer si besoin de l'autre côté la structure correspondante.

    Si je veux créer un rectangle bleu de 4*2, j'enverrai "rectangle:bleu:4:2" ou toute autre représentation sérialisée qui permette, en désérialisant le buffer, de créer mon rectangle bleu de 4*2.
    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.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    410
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2004
    Messages : 410
    Points : 361
    Points
    361
    Par défaut
    Bonjour et merci pour vos retours.
    Vu le contexte applicatif (code de simulation numérique sur super calculateur), j'opte pour la la structure basique avec un type embarqué. C'est le plus simple finalement.
    Le server est juste un scheduler de job à réaliser et la variété de type de ticket est assez faible. Enfin, le check du type de ticket se fait qu'à deux endroits.

    Ensuite, comme ça tourne sur un supercalculateur, les nodes sont homogènes donc pas de problème d'endianing mais je vais quant même réfléchir à un mécanisme de swap bytes au cas où.

    Enfin, par rapport à la remarque de koala01 sur le void*. C'est un choix assumé d'écrire des codes qui se rapprochent le plus possible du C, tout en utilisant une couche C++ pour plus de flexibilité et lisibilité. Même si ici ça ne s'y prête pas, mais dans ce genre de contexte applicatif (code de calcul) il faut rester au plus prêt de la machine. ex: Pas de template, des classes/structures simples, pas de iostream (perf affreuses), etc.

    Merci encore pour vos retours très riches.

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

Discussions similaires

  1. Réponses: 9
    Dernier message: 02/07/2008, 15h04
  2. Classe virtuelle detructeur virtuel
    Par Jeane dans le forum C++
    Réponses: 3
    Dernier message: 26/06/2006, 23h06
  3. Réponses: 3
    Dernier message: 27/03/2006, 11h43
  4. Dynamic_cast & Base class virtuelle
    Par taron dans le forum C++
    Réponses: 5
    Dernier message: 11/02/2006, 20h45
  5. Réponses: 2
    Dernier message: 01/02/2006, 15h02

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