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

Langage Delphi Discussion :

Problème d'héritage avec les génériques.


Sujet :

Langage Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut Problème d'héritage avec les génériques.
    Bonjour,

    Je bute sur un problème que je ne comprend pas bien.

    J'ai déclaré une première classe "TDiaDBTableRecord qui hérite de TCustomDiaDBTableRecord" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TCustomDiaDBTableRecord = class
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TDiaDBTableRecord = class ( TCustomDiaDBTableRecord )
    J'ai ensuite déclaré une classe générique, ainsi qu'une classe dérivée non générique (donc en fermant le type) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    TCustomDiaDBTableDataServ<T: TCustomDiaDBTableRecord> = class( TDiaDataServ )
       private
          // Liste de "liste d'enregistrements fils"
          FTableRecords: TObjectList<TDiaDBTableRecords<T>>;

    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TDiaDBTableDataServ = class ( TCustomDiaDBTableDataServ<TDiaDBTableRecord> )

    Là, le compilateur me sort une erreur
    "[DCC Erreur] U_DiaFieldServ.pas(6660): E2010 Types incompatibles : 'TDiaDBTableRecord' et 'TCustomDiaDBTableRecord'"

    la ligne indiquée correspond à la dernière ligne du fichier source correspondant. Cette erreur disparait si je retire la déclaration de la classe dérivée "TDiaDBTableDataServ".

    Je n'y comprend rien, "TDiaDBTableRecord" hérite bien de "TCustomDiaDBTableRecord" et est donc compatible.

    J'ai bien vérifié que dans l'implémentation de ma classe générique je n'ai aucune référence à un type fermé de "T".

    J'ai essayé de déclarer
    "TDiaDBTacleDataServ" de la façon suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TDiaDBTableDataServ<T: TDiaDBTableRecord> = class ( TCustomDiaDBTableDataServ<T> )
    cela compile, mais alors dès que j'essaie d'en hériter en fermant le type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TServCfgListe = class( TDiaDBTableDataServ<TDiaDBTableRecord> )
    j'ai de nouveau l'erreur à la fin du fichier source correspondant.


    J'ai essayé un test rapide dans un autre fichier source :

    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
        TMyClass = class
        private
        protected
    //       procedure MyClassTest;
        public
        end;
     
        TMyDerivedClass = class(TMyClass)
        private
        protected
        public
        end;
     
        TMyGenericClass<T: TMyClass> = class(TObjectList<T>)
        private
        protected
        public
        end;
     
        TMyDerivedGenericClass = class ( TMyGenericClass<TMyDerivedClass> )
        private
           Grut: TMyDerivedClass;
        end;

    qui compile sans problème ??


    Cela doit donc provenir de l'implémentation, mais je ne vois pas en quoi elle peut influencer sur le fait qu'une classe soit compatible avec une autre... pour moi seule la déclaration/prototype détermine cela.

    Je pense que j'ai du zapper quelque chose avec les génériques, je sais qu'il y a des contraintes avec les types ouverts mais là je ne vois pas ce qui cloche.

    Quelqu'un peut-il m'aider/me donner une piste ? Je n'ai pas inséré le code source, si nécessaire je le ferai mais il est relativement long...

  2. #2
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    je ne suis pas très à l'aise avec les generics mais je suppose que cette déclaration n'est pas possible TObjectList<TDiaDBTableRecords<T>>, pourquoi n'utilises-tu pas simplement T ?
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    A priori ce type de déclaration ne pose pas de problème, mais pour ton information à la base ma classe "TCustomDiaDBTableDataServ" était déclarée comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
       TCustomDiaDBTableDataServ<T: TCustomDiaDBTableRecord; U: TCustomDiaDBTableRecords<T>> = class( TDiaDataServ )
       private
          // Liste de "liste d'enregistrements fils"
          FTableRecords: TObjectList<U>;
    Et là j'avais une erreur supplémentaire à la compilation :
    "[DCC Erreur] U_CfgListeServ.pas(1): E2010 Types incompatibles : 'U_DiaFieldServ.TDiaDBTableRecords<U_DiaFieldServ.TDiaDBTableRecord>' et 'U_DiaFieldServ.TCustomDiaDBTableRecords<U_DiaFieldServ.TDiaDBTableRecord>'"
    mais je pense qu'en réalité celle-ci est toujours du à la première erreur (qui apparait avant : "[DCC Erreur] U_CfgListeServ.pas(1): E2010 Types incompatibles : 'TDiaDBTableRecord' et 'TCustomDiaDBTableRecord'" Sauf que je ne vois pas en quoi ces types sont incompatibles...

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    je ne comprend pas, dans le code que tu donnes TCustomDiaDBTableRecords n'est pas un type générique ?! alors que signifie TCustomDiaDBTableRecordsw<T> ?
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Est-ce prudent de pousser à ce point les templates ?
    Il faut penser que cela génère un code très lourd au final (redondance du code compilé pour chaque spécification)
    Sur des types simples comme integer, double ... je comprends mais sur des objets !

    Les génériques n'ont pas comme vocation de remplacer le Polymorphisme dynamique à la Delphi (virtual\override), cela peut en reduire le code (surtout les transtypages un peu pénible de la TObjectList)
    les templates c'est une autre forme de polymorphisme que l'on pourrait de qualifier de "statique"
    Mélanger les deux, ça fait beaucoup !
    En plus tu tombes en plein "héritage d'implémentation", typique du Delphi mais déconseillé en POO préférant la délégation et l'utilisation d'interface !
    D'ailleurs utiliser des Interfaces obligent une certaine structure du code (justement dans la gestion de l'héritage) et facilite le respect des Patterns

    Même si en théorie, je suis aussi curieux pour ce type de délire : C++Builder - [Langage/Algorithme] Template et Héritage
    Parfois, c'est peut-être un peu trop !

    Découpe tes objets !
    Au lieu d'avoir property U Records[] fait un truc plus simple comme property TCustomDiaDBTableRecords Records.
    Comme le DataSet et ses TFields !
    Ensuite, un bon vieux as\is pour les cas particulier (mais c'est peut-être cette ancienne pratique que tu veux bannir, c'est ma principal utilisation des templates, juste rendre quelques transtypages implicites )

    Quel est ton but ?
    Pousser ta couche DB jusqu'à une sorte de couche de persistance permettant de gérer la couche métier entièrement avec des objets ?

    Sinon, cela en plus venir de la façon de gérer les templates, en C++, il y a le pré-processeur qui s'occupe des macros et templates avant la compilation, en Delphi, est-ce fait dans la phase de compilation ?
    La procédure de compilation est différente entre ces deux langages, les génériques c'est un pompage des templates, pour être à la mode, c'est bien utile pour des trucs comme la TIntegerList plus conviviale qu'un TIntegerDynArray

    En C++Builder 2007, j'utilise la STL comme le std::map !
    je peux te dire qu'a un moment donné la complétion de code plante : "Erreur interne du compilateur", il n'arrive plus à comprendre (le mélange STL + VCL ça lui fait mal)
    Cela dépend dans quel ordre sont déclarés les objets (les forward déclarations ne suffisent semble-t-il pas)
    Du coup, soit j'utilise le TObject* au lieu du vrai type et transtype par la suite (du coup, perte de l'intérêt des templates) ou alors je refais mon code en RTL normal !
    J'ai même créer ma propre template héritant de TObjectList, l'une étant une simple List et une autre étant un Map

    Je testerais en C++XE2, la version bêta plantait bien avant et j'ai pas réessayé depuis !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    je ne comprend pas, dans le code que tu donnes TCustomDiaDBTableRecords n'est pas un type générique ?! alors que signifie TCustomDiaDBTableRecordsw<T> ?
    Non c'est "TCustomDiaDBTableRecord" (sans le "s") qui n'est pas générique. Alors que "TCustomDiaDBTableRecords" est déclaré comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TCustomDiaDBTableRecords<T: TCustomDiaDBTableRecord> = class
    C'est une liste d'enregistrements...

    ShaiLeTroll> Je sais que les "génériques" en Delphi sont en réalité des templates, il y a des avantages (possiblité d'utiliser les types de base comme Integer, array, string. Performance: chaque classe à type spécifié est redéfinie complètement donc vitesse d'exécution du code optimal) et des inconvénients : quelques contraintes liés au fait que justement deux classes à types spécifiées différentes sont considérés comme justement deux classes différentes et consommation mémoire lié à la redéfinition sous-jacente de chaque nouvelle classe instanciée.

    La finalité est bien entendu d'éviter les casts, qui sont "quand même d'un autre âge" (même si c'est très pratique). C'est vrai qu'au final on peut se demander si le jeu en vaut la chandelle, à l'époque de Delphi 7 j'utilisais une métaclasse conjointement au cast. Tu a tout à fait compris ce que je voulais faire et pourquoi j'utilisais des génériques (templates).

    Enfin pour finir le problème est résolu, j'ai viré tout l'ancien code que j'avais commenté dans le fichier source et cela a suffit. Je pense à un bug du parser ou compilo qui s'emmêlait les pinceaux. Heureusement que j'ai pu trouver une solution car j'avais quand même passé pas ma de temps à faire ces classes.

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Tu as modifié le titre du sujet pour mettre [Résolu] mais savais-tu qu'il existe un bouton en bas qu'il le fait automatiquement !?


    Citation Envoyé par ZZZzzz2 Voir le message
    d'éviter les casts, qui sont "quand même d'un autre âge"
    Ah bon ? Mince, je programme comme un vieux alors
    Depuis un an, je suis en C++Builder, et j'ai eu besoin de mettre en place des DelphiInterface que j'échange entre EXE et DLL, la méthode Supports (la méthode implicitement utilisé par as en Delphi) est justement basé sur un type générique bool Supports(DelphiInterface<T>& smartIntf).
    Hors Supports c'est un juste un cast sécurisé utilisant les GUID !

    Ce n'est pas parce que les génériques sont une nouveauté en Delphi (ça existe depuis le début en C++[Builder] ) qu'il faut considérer les pratiques existantes comme forcément obsolète !
    [Mode TROLL² ON]
    Ce n'est pas parce que certains langages veulent déresponsabiliser le développeur (considéré comme un incompétent qui fait n'importe quoi et qui ne maîtrise rien) que l'on doit faire pareil en Delphi !
    [Mode TROLL² OFF]

    Sinon, attention avec toutes ces templates, c'est vrai qu'en Delphi, le couplage fort des objets est habituel, tu pense avant tout à la simplicité d'utilisation des objets, ça je le comprends, je pense aussi des cette manière, un code système un peu sioux mais très facile à utiliser
    Tu sembles oublier l'architecture POO et les Design Patterns qui dans un tel développement seraient intéressantes à utiliser !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Tu as modifié le titre du sujet pour mettre [Résolu] mais savais-tu qu'il existe un bouton en bas qu'il le fait automatiquement !?



    Ah bon ? Mince, je programme comme un vieux alors
    Depuis un an, je suis en C++Builder, et j'ai eu besoin de mettre en place des DelphiInterface que j'échange entre EXE et DLL, la méthode Supports (la méthode implicitement utilisé par as en Delphi) est justement basé sur un type générique bool Supports(DelphiInterface<T>& smartIntf).
    Hors Supports c'est un juste un cast sécurisé utilisant les GUID !

    Ce n'est pas parce que les génériques sont une nouveauté en Delphi (ça existe depuis le début en C++[Builder] ) qu'il faut considérer les pratiques existantes comme forcément obsolète !
    [Mode TROLL² ON]
    Ce n'est pas parce que certains langages veulent déresponsabiliser le développeur (considéré comme un incompétent qui fait n'importe quoi et qui ne maîtrise rien) que l'on doit faire pareil en Delphi !
    [Mode TROLL² OFF]

    Sinon, attention avec toutes ces templates, c'est vrai qu'en Delphi, le couplage fort des objets est habituel, tu pense avant tout à la simplicité d'utilisation des objets, ça je le comprends, je pense aussi des cette manière, un code système un peu sioux mais très facile à utiliser
    Tu sembles oublier l'architecture POO et les Design Patterns qui dans un tel développement seraient intéressantes à utiliser !
    ah oui effectivement j'avais zappé ce bouton Résolu, merci !

    Ne le prends pas mal mais admets que le cast ça reste "sale". C'est sûr que c'est très pratique et un bon développeur n'aura pas de problèmes à en utiliser (et gagnera souvent beaucoup de temps). Mais je me force à adopter autant que possible la logique des langages "managés", et là le cast ou les pointeurs, "c'est le mal", car en cas de code pourri d'erreurs, tu galères pour débogger (le safe cast "as" est "moins pire" certes). Comme je ne suis pas le seul sur le projet cela m'assure que d'autres devs qui n'auraient pas forcément la rigueur suffisante pour caster/utiliser des pointeurs à outrance ne va pas générer des bugs chiants à trouver...

    Après j'avoue que c'est un peu pour la beauté de la chose, j'aime faire des choses "clean". A cet égard je ne vois pas en quoi j'oublie l'architecture POO (d'ailleurs le simple nom de mes classes, débutant par "TCustomXXX" va en ce sens) ni les interfaces et implémenteurs que j'utilise dès que je peux...
    D'une manière générale je "descend" autant que possible mes méthodes/propriétés/variables afin d'avoir la structure POO la "plus optimale"...

  9. #9
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par ZZZzzz2 Voir le message
    Ne le prends pas mal mais admets que le cast ça reste "sale".
    ça dépend qui le fait
    Mais oui, normalement, un code bien écrit, on doit pouvoir s'en passer à 99.9% du temps, une petite strategy ou une factory permet de le faire sans douleur voire même sans s'en rendre compte !

    Citation Envoyé par ZZZzzz2 Voir le message
    adopter autant que possible la logique des langages "managés", et là le cast ou les pointeurs, "c'est le mal", car en cas de code pourri d'erreurs, tu galères pour débogger (le safe cast "as" est "moins pire" certes). Comme je ne suis pas le seul sur le projet cela m'assure que d'autres devs qui n'auraient pas forcément la rigueur
    lol, j'espère que tes collègues ne lisent pas le forum, tu viens juste de sous-entendre de leur incompétence ... je compatis ... après on va dire que certains programmeurs sont intransigeants et manque d'humilité parce qu'ils anticipe la médiocrité des autres

    Personnellement, j'ai pondu, ces derniers mois, un code vachement générique pour la gestion de périphérique matériel, un peu comme un système de driver utilisant des DLL avec des interfaces, des events pour la supervision ...
    J'hésite à savoir si j'ai un truc POO bien architecturé ou un plat de lasagne dont chaque couche est rempli de spaghetti
    car j'ai pondu le truc au fur et à mesure, disons que mon cahier des charges tient une phrase : "Faut pourvoir faire ça avec n'importe quel marque de bidule", et je ne plaisante pas, j'ai juste censuré le ça et le bidule
    J'ai du m'adapter à l'existant pas du tout architecturé et faire rentrer les évolutions au forceps !
    Cela fonctionne super bien, cela à l'air tout bien mais avec le recul, c'est super compliqué !
    Justement, je pensais mettre faire des délégation et des templates pour factoriser certains éléments

    Sinon quand je parle de cast pour un TObject, je parle strictement du as\is ou InheritsFrom jamais d'un cast sauvage !
    Le cast non protégé, ça c'est extrème, à part les hack de protected, je ne l'utilisais jamais pour les TObject !
    En C++Builder, hack de protected ne passent pas, donc finalement, je n'en fait plus et j'utilise Supports pour mes interfaces ou static_cast<>, dynamic_cast<> ou encore InheritsFrom lors d'un cast sauvage ! tout ça revient au as\is
    Et finalement, le cast reste assez rare car je conçois une interface ou une classe abstraite qui fourni un comportement standard, l'appelant ne doit se préoccuper que de cela ! l'implémentation n'est pas de son ressort !
    J’encapsule beaucoup, justement au plus haut niveau, j'ai des objets métier, il suffit de les utiliser, il n'y a aucune raison de déborder du cadre qu'ils fournissent !
    En interne, ces objets métiers utilisent des objets systèmes génériques.


    Citation Envoyé par ZZZzzz2 Voir le message
    caster/utiliser des pointeurs à outrance
    Tu confonds le cast de type simple genre utilise le Tag pour stocker un pointeur, ou une TList avec des PRecord et le cast d'objet qui peut rester propre !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  10. #10
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    ça dépend qui le fait


    lol, j'espère que tes collègues ne lisent pas le forum, tu viens juste de sous-entendre de leur incompétence ... je compatis ... après on va dire que certains programmeurs sont intransigeants et manque d'humilité parce qu'ils anticipe la médiocrité des autres
    Personnellement, j'ai pondu un code vachement générique pour la gestion de périphérique matériel, un peu comme un système de driver, avec des interfaces, des events pour la supervision ... j'hésite à savoir si j'ai un truc POO bien architecturé ou un plat de lasagne dont chaque couche est rempli de spaghetti
    , ça marche super bien, cela à l'air tout bien mais avec le recul, c'est super compliqué !
    Justement, je pensais mettre faire des délégation et des templates pour factoriser certains éléments

    Sinon quand je parle de cast pour un TObject, je parle strictement du as\is ou InheritsFrom jamais d'un cast sauvage !
    Le cast non protégé, ça c'est extrème, à part les hack de protected, je ne l'utilisais jamais pour les TObject !
    En C++Builder, hack de protected ne passent pas, donc finalement, je n'en fait plus et j'utilise Supports pour mes interfaces ou static_cast<>, dynamic_cast<> ou encore InheritsFrom lors d'un cast sauvage ! tout ça revient au as\is


    Tu confonds le cast de type simple genre utilise le Tag pour stocker un pointeur, ou une TList avec des PRecord et le cast d'objet qui peut rester propre !

    Arf, je ne m'en cache pas, on a eu des perles... mais ce n'est pas forcément la raison principale. J'aime avoir un code robuste aussi, qui demandera le moins de modification possible tout en étant simple à modifier (à lire donc). Mais il est vrai qu'il est toujours compliqué de trouver l'équilibre entre "bon code" et "beau code". En gros et comme un collègue dit toujours "le mieux est l'ennemi du bien". On peut toujours améliorer un code et le rendre plus propre mais ce n'est pas toujours (sinon rarement) le meilleur choix, car on a toujours des contraintes (de temps, de souplesse, etc...)

    Mais bon le principe "on part d'une classe la plus simple à exploiter", c'est-à-dire qui ne nécessite pas de cast ni rien ne me semble pas dénué de sens tout de même.

    Ah le bon vieux cast "TCustomXXX" pour accéder aux membres protégés, ça c'est du bourrin (mais j'avoue l'avoir déjà fait aussi).

    Pour revenir aux casts, quand je dis que c'est le mal c'est juste parce-que pour parler des casts "non objet" cela passe outre le typage du compilateur, et donc outre ses mécanismes de gestion de la mémoire (comtpage de référence, tout ça). Concernant les casts sur des "TObject",je ne trouve ça pas plus propre que les autres. Je parle bien sûr des "hardcasts", le "is" et le "as" restent "safe" et sont par ailleurs très pratiques.


    J'ai vu que tu as édité ton post, par rapport à ton logiciel de gestion de périphérique matériel que tu as développé. Combien de fois cela m'est-il arrivé de regarder du code en me disant "merde avec une bonne approche POO" (ou avec des génériques depuis qu'on utilise XE) le code pourrait être bien plus simple. Mais bon voilà à quoi ça sert si ça marche bien et qu'on ne prévoit pas de changement notable dans ces portions de code à l'avenir ? Mais j'avoue que ça me démange à chaque fois de tout changer, mais il faut admettre que ça serait une perte de temps.

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par ZZZzzz2 Voir le message
    Pour revenir aux casts, quand je dis que c'est le mal c'est juste parce-que pour parler des casts "non objet".
    Ah, oui, ça je n'utilise plus volontairement depuis des années !
    Pour être honnête, le besoin ne sait pas fait sentir, je serais capable d'en refaire mais toujours encapsuler !
    J'en ai abusé, j'ai même basé la lecture de fichier à structure fixe exclusivement sur un jeu ce cast et case of !
    Résultat, le parsage du fichier était 100 fois plus rapide que le découpage par succession de Copy !
    Je triche avec les union, le Variant \ TVarData en est un parfait exemple !

    Et quand j'en arrive a de tel extrémité, souvent justement avec des DLL fournis par les fabricants de matériel avec tout plein de CallBack, c'est encaspulé dans un objet !
    Là, tu ne peux pas faire autrement, le coup classique, un code d'event, un pointeur a caster selon le code d'event et une la longueur du pointeur pour être sûr que ton sizeof(TRecordEventType1) correspond bien à ce que tu reçois, histoire que l'alignement ne soit pas le même

    Une fois l'encapsulation, on en revient à des pratiques plus sages !

    Idem, a un moment donné, il est plus simple d'utiliser un record et de l'écrire\lire sauvagement depuis un objet TCP, RS-232 ... je réserve ces pratiques à ce genre de cas !
    Je l'ai fait récemment, pour un petit programme de test pour une collègue qui codait la EPROM d'une carte que l'on a fabriqué pour un client, j'utilisais partout un record composé d'un Header, Data et Footer,
    Header donnant le type du Data (implictement sa longueur) ...
    Footer contenant un Checksum du Header + Data !

    Citation Envoyé par ZZZzzz2 Voir le message
    cela passe outre le typage du compilateur
    Tient justement, dans le sujet Déclaration de pointeur de fonction et structure, c'était justement du cast sauvage non protégé !
    Et j'ai fini par réussir une variante sans cast !
    Preuve que je le considère comme dangereux quand même !



    Citation Envoyé par ZZZzzz2 Voir le message
    Concernant les casts sur des "TObject",je ne trouve ça pas plus propre que les autres. Je parle bien sûr des "hardcasts", le "is" et le "as" restent "safe" et sont par ailleurs très pratiques.
    Entièrement d'accord, il faut bannir le "hardcast", je ne pratique que is\as, c'est un peu le but de l'héritage quand même !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  12. #12
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Ah, oui, ça je n'utilise plus volontairement depuis des années !
    Pour être honnête, le besoin ne sait pas fait sentir, je serais capable d'en refaire mais toujours encapsuler !
    J'en ai abusé, j'ai même basé la lecture de fichier à structure fixe exclusivement sur un jeu ce cast et case of !
    Résultat, le parsage du fichier était 100 fois plus rapide que le découpage par succession de Copy !
    Je triche avec les union, le Variant \ TVarData en est un parfait exemple !

    Et quand j'en arrive a de tel extrémité, souvent justement avec des DLL fournis par les fabricants de matériel avec tout plein de CallBack, c'est encaspulé dans un objet !
    Là, tu ne peux pas faire autrement, le coup classique, un code d'event, un pointeur a caster selon le code d'event et une la longueur du pointeur pour être sûr que ton sizeof(TRecordEventType1) correspond bien à ce que tu reçois, histoire que l'alignement ne soit pas le même

    Une fois l'encapsulation, on en revient à des pratiques plus sages !

    Idem, a un moment donné, il est plus simple d'utiliser un record et de l'écrire\lire sauvagement depuis un objet TCP, RS-232 ... je réserve ces pratiques à ce genre de cas !
    Je l'ai fait récemment, pour un petit programme de test pour une collègue qui codait la EPROM d'une carte que l'on a fabriqué pour un client, j'utilisais partout un record composé d'un Header, Data et Footer,
    Header donnant le type du Data (implictement sa longueur) ...
    Footer contenant un Checksum du Header + Data !


    Tient justement, dans le sujet Déclaration de pointeur de fonction et structure, c'était justement du cast sauvage non protégé !
    Et j'ai fini par réussir une variante sans cast !
    Preuve que je le considère comme dangereux quand même !




    Entièrement d'accord, il faut bannir le "hardcast", je ne pratique que is\as, c'est un peu le but de l'héritage quand même !

    C'est vrai que pour lire/écrire dans un fichier le plus performant je pense reste d'utiliser un type "file of" associé à un record. De plus cette méthode t'assure que ton fichier ne pourra pas contenir un enregistrement tronqué. L'inconvénient est que celui-ci ne peux pas contenir de type dynamique, forcément la taille du record doit-être connu à l'avance (donc obligation d'utiliser des chaînes courtes, etc.). Donc ce n'est pas toujours possible.

    Concernant les cast sur les types de base, on peut aussi arriver à un comportement similaire tout en restant "clean" en utilisant les "enregistrements variables" (record avec des cases, l'équivalent des unions en C/C++), j'avoue m'en servir assez souvent car là aussi c'est très pratique. Notamment lorsqu'on a des types ordinaux personnalisés (des enums) qu'on souhaite pouvoir stocker dans un fichier ou une base de données par exemple. Ou encore lorsqu'on stocke dans un Integer plusieurs sous-champs d'un octet par exemple (et qu'un sous-champ a une signification qui dépend du sous-champ de poids supérieur notamment).

    Après les cast sur les "string"/"array" peuvent être nécessaires quand on veut explicitement court-circuiter le comptage de référence du delphi, mais à utiliser en connaissance de cause (moi je m'en sers principalement pour insérer rapidement dans un tableau).

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

Discussions similaires

  1. Problème de type avec les génériques
    Par othebault dans le forum Langage
    Réponses: 5
    Dernier message: 17/09/2009, 14h59
  2. Problèmes de pointeurs avec les arbres
    Par thierry57 dans le forum C
    Réponses: 17
    Dernier message: 22/12/2005, 23h35
  3. probléme d'enregistrement avec les chexkbox.
    Par pmboutteau dans le forum ASP
    Réponses: 16
    Dernier message: 18/10/2005, 15h05
  4. Réponses: 6
    Dernier message: 19/05/2005, 11h06

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