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 :

Pimpl, Opaque pointers, Cheshire cat .... sans alloc ! :)


Sujet :

C++

  1. #1
    Membre averti
    Avatar de bigquick
    Profil pro
    Inscrit en
    Août 2002
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 356
    Points : 353
    Points
    353
    Par défaut Pimpl, Opaque pointers, Cheshire cat .... sans alloc ! :)
    Salut !

    J'ai une petite question sur le "pimpl idiom" (ou peu importe comment vous l'appelez ) : est-il possible de l'implémenter sans faire d'allocation à chaque instanciation de la classe ?

    Je voudrais l'utiliser pour proposer 2 implémentations d'une classe (avec une chaine ou un CRC). Cette classe est utilisée partout, donc j'aurais voulu un flag dans le fichier CPP pour changer d'implémentation sans avoir à recompiler le reste du projet.

    Est-ce que vous voyez une autre solution que le pimpl pour implémenter ça, ou alors est-il possible d'éviter l'allocation dynamique à chaque construction (les performances sont très importantes) ?

    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
    [H]
    class Key
    {
        ....   //  interface publique qui ne change pas
    };
     
     
    [CPP]
    #define   USE_STRINGS    1   //  a modifier
     
    #if USE_STRINGS
       ...   // implementation avec des strings
    #else
       ...   // implementation avec des CRCs
    #end
    Vous voyez le truc ...

    Merci !
    And still we will be here, standing like statues ...

  2. #2
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 669
    Points
    10 669
    Billets dans le blog
    3
    Par défaut
    Tu dois manipuler un pointer dans la classe publique. Ce pointeur est traditionnelement initialisé via une alloc dynamique (auto_ptr se prête bien dans le cas du pimpl), mais tu peux gérer ça autrement avec une gestion de pool à toi.
    Sinon, vu que tu as 2 implémentations possibles, peut être que l'héritage serait la solution (2 classes filles KeyWithStrings et JeyWithCRC).

  3. #3
    Membre averti
    Avatar de bigquick
    Profil pro
    Inscrit en
    Août 2002
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 356
    Points : 353
    Points
    353
    Par défaut
    Merci pour ta réponse !

    Je voudrais juste certaines précisions
    Pour l'allocation dynamique, je crois que je vais vraiment éviter (même avec un pool), puisque c'est une classe utilisée vraiment très souvent, et que les performances à ce niveau sont critiques.

    Pour l'héritage par contre, pourquoi pas, mais comment faire pour que le code appelant soit identique dans tous les cas ? En effet, je ne peux pas trop changer les milliers d'occurence du mot "Key" par "KeyString" dans le projet à chaque fois que je veux basculer d'implémentation, et par la même occasion tout recompiler... Un typedef de Key vers l'une des classes dérivées ne marcherait pas non plus, car il devrait être défini dans Key.h, qui ne doit pas changer....


    Une petite illustration du problème sera plus parlante

    On a beaucoup de classes qui utilisent cette classe "Key":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // exemple completement fictif, mais l'idée y est :)
     
    include <Key.h>
     
    Key k("test");
    arbre.add( k );
    cout << "On vient d'ajouter la clé" << k << endl;
    bool isPresent = arbre.contains( k );
    Et je voudrais donc pouvoir basculer de l'implémentation performante, qui stocke et compare les CRC de la chaine en question, vers l'implémentation avec des chaines, plus lente mais teeeeellement plus lisible pour débugger

    Mais bien sur sans changer le corps de Key.h, sinon on va devoir recompiler la 1/2 du projet à chaque fois.... Et sans allocation dynamique non plus, car ça ralentirait beaucoup l'application ....

    Est-ce que tu coup vous avez une idée ?
    Merci encore
    And still we will be here, standing like statues ...

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 394
    Points : 473
    Points
    473
    Par défaut
    Il me semble difficile de se passer du pimpl tout en n'ayant pas à recompiler tous les clients de Key.h
    D'ailleurs le petit nom du pimpl c'est 'compilation firewall', en gros c'est fait pour et c'est marqué dessus.
    Y'a bien un truc qui s'appelle le fast pimpl, dans un Guru of the Week
    http://www.gotw.ca/gotw/028.htm
    Mais il y a bien sûr allocation dynamique. Sans alloc, pas d'indirection possible, donc recompile en chaine.

    Une question néanmoins, à part au moment de la création tes clefs CRC, et tes clefs simple ont-elles un comportement différent ?
    Si ce n'est pas le cas, tu peux déléguer leur construction à un builder et garder une classe unique. En terme de perf, tu ne perdra qu'à la creation, mais pas à l'utilisation.

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Tu ne veux pas toute la généricité d'un pimpl, puisque tu connais déjà tes n ipmplémentations, alors que le pimpl est là pour gérer un jeu indéterminé d'implémentations. Par exemple, si la mémoire n'est pas un problème, une solution à base de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Key
    {
       // ...
    private:
       String data1;
       CRC data2;
    };
    Où tu n'utilises qu'un champ selon ton implémentation peut marcher.
    Si tes types String et CRC sont des POD (ou, plus probable, si tu peux remplacer ces types par des POD localement) tu peux les mettre dans une union pour regagner de la mémoire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Key
    {
       // ...
    private:
      union Data;
      {
         String data1;
         CRC data2;
      };
      Data data;
    };
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 669
    Points
    10 669
    Billets dans le blog
    3
    Par défaut
    Puisuq'il y avait allocation dynamique du pimpl, je pensais que tant qu'à faire utiliser l'alloc dynamique pour manipuler Key:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // exemple completement fictif, mais l'idée y est :) 
     
    include <Key.h> 
     
    KeyPtr *k = Key::New("test"); // smart ptr
    arbre.add( k ); 
    cout << "On vient d'ajouter la clé" << k << endl; 
    bool isPresent = arbre.contains( k );
    Key::New fait office de factory:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    KeyPtr Key::New( std::string S )
    {
    #ifdef USE_STRINGS
        return new KeyStrings( S );
    #else
        return new KeyCRC( S );
    #endif
    }
    mais Key doit être abstraite, et donc y'a des appels virtuels, c'est un peu plus lent. Mais bon à priori c'est insignifiant, et sûrement plsu performant car tu noteras que tu économises des recopies par rapport à ton exemple alloué sur la pile (+ une alloc dynamique par arbre.add).

  7. #7
    Membre averti
    Avatar de bigquick
    Profil pro
    Inscrit en
    Août 2002
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 356
    Points : 353
    Points
    353
    Par défaut
    Merci pour toutes vos réponses.

    Il me semble difficile de se passer du pimpl tout en n'ayant pas à recompiler tous les clients de Key.h. D'ailleurs le petit nom du pimpl c'est 'compilation firewall', en gros c'est fait pour et c'est marqué dessus.
    Oui, le pimpl c'est pour (entre autres) réduire le temps de compilation, mais je me suis dit que c'était peut-être pas la seule technique ....
    Sinon, en effet, à part le constructeur, la classe reste identique peu importe l'implémentation; donc j'ai reflechi à la construction déleguée, mais ne vois pas comment l'implémenter sans allocation dynamique.

    -

    Loic Joly, par rapport à ta réponse, la mémoire est aussi importante, donc je ne vais pas vraiment pouvoir utiliser la solution avec les 2 membres séparés. La solution avec l'union est dejà plus envisageable si je remplace le std::string par un char[...]. Il faudrait que j'investigue un peu plus: si je choisis une union { int / char[32] } par exemple, chaque clé utilisera 4x fois plus de place que le CRC seul. A voir si c'est vraiment critique

    -

    Et enfin Aurélien (j'en ai eu des réponses dis donc ! Merci beaucoup !), ta solution marcherait bien, mais il y a allocation dynamique. Tu dis que ça ne changerait pas grand chose niveau performances, est-ce que tu pourrais détailler s'il te plait ?

    En fait on fait une utilisation intensive de cette classe (environ 10.000 constructions avec une recherche rapide sous Visual), et les performances sont critiques dans ce programme.

    Autre problème, la plupart des appels se font avec un constructeur non explicite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Arbre::add( Key _k );
    Key::Key( string _str );
     
    monArbre.add( "test" );
    Donc j'imagine que cette implémentation impliquerait de toute recoder

    -

    Enfin merci pour vos réponses, je continue à chercher en parallèle...
    J'espère pouvoir trouver la méthode la plus efficace, l'idée étant de

    rajouter la possibilité de débugger avec des strings si on le souhaite (moyennant une recompilation du fichier Key.cpp)
    sans perdre en performances si on compile avec l'implémentation en CRC

    J'en demande peut-être beaucoup
    And still we will be here, standing like statues ...

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 394
    Points : 473
    Points
    473
    Par défaut
    Le pb, c'est que tu souhaites modifier le type des variables d'instance de Key, sans modifier les clients de Key. Ca, je pense que c'est théoriquement impossible.
    Maintenant, si tu acceptes de recompiler les clients de Key, alors des solutions y'en a plein.

  9. #9
    Membre averti
    Avatar de bigquick
    Profil pro
    Inscrit en
    Août 2002
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 356
    Points : 353
    Points
    353
    Par défaut
    Aie c'est ce que je craignais ....

    Le problème c'est que des clients de Key il y en a nombre incalculable (presque chaque classe du projet l'utilise durant sa création). Et on ne peut pas se permettre de recompiler le projet entier dès que l'on veut bénéficier des strings de debug au lieu des CRC.

    Ca serait bien dommage :/

    A part pour des raisons techniques au niveau compilation, est-ce qu'il y a une raison qui fait que la partie privée d'une classe ne pourrait pas être déclarée dans le CPP ? Ca aurait été plutot pratique de déclarer ce qui est publique dans le header (nom de la classe, méthodes et attributs publiques), et tout ce qui est implémentation (méthodes et attributs privés , et corps de toutes les méthodes) dans le CPP....

    J'imagine que durant la compilation d'une classe X, le compilateur doit reserver l'espace pour chaque Key utilisée sur la pile, et donc que sa taille doit être connue .... en fait c'est peut être suffisant comme raison ...
    And still we will be here, standing like statues ...

Discussions similaires

  1. Inversion de control sans allocation dynamique?
    Par mahorais dans le forum Langage
    Réponses: 14
    Dernier message: 08/07/2013, 10h53
  2. pointer un checkbox sans id
    Par alpanko dans le forum Général JavaScript
    Réponses: 6
    Dernier message: 27/10/2010, 16h36
  3. Allocation d'un grand tableau sans boucle
    Par hei lan dans le forum Débuter
    Réponses: 2
    Dernier message: 09/06/2008, 11h52
  4. Pointer, Allocation et C
    Par Pikwik dans le forum C
    Réponses: 3
    Dernier message: 10/05/2006, 11h42
  5. Lecture dans un fichier sans cat
    Par gandalfar dans le forum Linux
    Réponses: 5
    Dernier message: 27/12/2005, 11h35

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