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++Builder Discussion :

Pattern Strategy et Constructeurs Virtuels VCL


Sujet :

C++Builder

  1. #1
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 073
    Par défaut Pattern Strategy et Constructeurs Virtuels VCL
    Je continue mes recherches sur le C++, j'ai passé le stade constructeurs pour passer au "constructeurs virtuels" (uniquement VCL)

    Je l'utilise souvent pour le Pattern Strategy des mes développements, en fait souvent couplé avec le Pattern RegisterClass (oui ça c'est typique Borland ce n'est pas un Pattern reconnu ailleurs, quoi qu'il y a la Registry assez utilisé en PHP sous Zend par exemple mais qui s'implique aux instances et non aux classes)

    L'utilisation la plus connue c'est finalement la DFM, effectivement, le TComponent introduit le premier constructeur virtuel, ensuite tous les TControl on aussi un constructeur virtuel

    le problème c'est que l'on a pas le code qui créé les composants par le parsage du DFM (il en codé en Delphi dans TStream::ReadComponent, TReader::ReadData ...) et utilise FindClass et utilise la Réference de Class (équivalent du MetaClass) pour instancier n'importe quel objet qui hérite du TComponent sans savoir ce qu'il est vraiment, ensuite via les RTTI, il modifie les propriétés publiées de l'objet

    J'aimerais trouvé une traduction C++, pour ce genre d'utilisation

    Delphi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    type
      TClassVCLDelphiClass = class of TClassVCLDelphi; 
    ...
    var
      ClassVCLType: TClassVCLDelphiClass;
      Obj: TClassVCLDelphi;
    ...
      ClassVCLType := FindClass(ChaineNomDeClasse);
      Obj := ClassVCLType.Create();
      Obj.MethodeVirtuelle(); // lance la méthode de la bonne classe !
    C++
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
      typedef TMetaClass* TClassVCLDelphiClass;
     
      TClassVCLDelphiClass  ClassVCLType = FindClass(ChaineNomDeClasse);
      TClassVCLDelphi *Obj = new ClassVCLType(); // ça Compile pas 
      Obj->MethodeVirtuelle();
    évidemment ce n'est pas la bonne syntaxe, si quelqu'un a déjà tenté cela !

    Sinon, je serais réduit à faire des séries if if if ou des cases ...
    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

  2. #2
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2005
    Messages
    401
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2005
    Messages : 401
    Par défaut
    Hello,

    Je ne sais pas comment le faire en C++ pur, mais pourquoi ne pas utiliser un bout de Delphi juste pour la création:
    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
    unit Unit1;
     
    interface
     
    uses Classes, Controls;
     
    procedure CreateControl(InstanceClass: TComponentClass; var Reference);
     
    implementation
     
    procedure CreateControl(InstanceClass: TComponentClass; var Reference);
    var
      Instance: TComponent;
    begin
      Instance := TComponent(InstanceClass.NewInstance);
      TComponent(Reference) := Instance;
      try
        Instance.Create(nil);
      except
        TComponent(Reference) := nil;
        raise;
      end;
    end;
     
    end.
    C'est honteusement pompé sur le code de TApplication.CreateForm.
    Puis ensuite, dans ton code C++:
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #include <vcl.h>
    #pragma hdrstop
     
    #include "Unit6.h"
     
    #include "Unit1.hpp"
     
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm6 *Form6;
     
    class T1 : public TComponent
    {
    public:
      int i;
      __fastcall T1(TComponent* p) : TComponent(p), i(10) { }
      virtual __fastcall ~T1(void) {}
      virtual void __fastcall Test(void) { i = 20; }
    };
     
    class T2 : public T1
    {
    public:
      int j;
      __fastcall T2(TComponent* p) : T1(p), j(30) { }
      virtual __fastcall ~T2(void) {}
      virtual void __fastcall Test(void) { j = 40; }
    };
     
    __fastcall TForm6::TForm6(TComponent* Owner)
      : TForm(Owner)
    {
     
      TMetaClass* classType = __classid(T2);
      RegisterClasses(&classType, 0);
     
      T1* p;
      CreateControl(FindClass("T2"), &p);
      p->Test();
    }
    J'ai fait un test rapide, ça a l'air de virtualiser comme il faut (testé sous 2010)

  3. #3
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 073
    Par défaut
    Merci totoche76, utiliser une fonction codée en Delphi pour contourner les limitations du C++, oui, c'est logique avec BSD !
    J'avais honteusement eu cette idée, mais comme toi, il m'a fallu partir d'un TComponent, je n'ai pas peu le faire depuis un TObject ou TPersistent

    je vais regarder si c'est lié au "constructeur par défaut ()" ou les directives comme __fastcall

    Et en C++, ce n'est donc pas possible ?
    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

  4. #4
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2005
    Messages
    401
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2005
    Messages : 401
    Par défaut
    Ah....

    Je viens d'essayer (sous XE) en remplaçant partout TComponent par TPersistent (et en tenant compte du fait que le constructeur de TPersistent n'a pas d'argument), et ça continue à gazer...(edit: sauf que le constructeur par défaut n'est effectivement pas appelé ????)

    En C++, faut plutôt regarder du côté du pattern Fabrique...

    Sinon il doit bien y avoir qq'un sur les forums Embarcadero avec une idée là-dessus...

  5. #5
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 073
    Par défaut
    le constructeur par défaut n'est effectivement pas appelé
    D'où mon problème, puisque mon constructeur de mon objet instancie un Objet et j'ai évidemmnet une violation d'accès lors que j'essaye d'accèder à cet objet interne !
    Tu es d'accord que cela pert tout son intéret si le constructeur n'est pas appelé !

    Oui, au passage ma Strategy utlise une Factory pour lancer le bon algorithme !

    En fait, tous les exemples C++ qui je vais vu utilise un switch\case ou un série de if if if, ce n'est pas très élégant, et très "statique".

    J'utilise souvent une TClassList ou une constante array of TClass, et dans le cas de ma propre couche de peristance les classes des objets étaient stockées dans les Tables (gestion de l'héritage)


    Tient, j'avais fait ceci en Delphi, ma méthode de création étant un peu plus courte que celle de la CreateForm mais lorsque l'on voit le proto CPP de CreateInstanceCPP on y voit l'ajout d'un paramètre VMT cela m'a donné ce genre d'idée !

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    unit BeginningBaseClassesDelphiImpl;
     
    interface
     
    uses
      Classes;
     
    type
      TClassVCLDelphiVirtualizer = class helper for TObject
        constructor CreateVirtual(Dummy: Integer = 0); virtual;
      end;
     
      TClassVCLDelphiFactory = class(TObject)
        class procedure CreateInstanceCPP(var Reference; AOwner: TComponent = nil);
      end;
     
     
    implementation
     
    constructor TClassVCLDelphiVirtualizer.CreateVirtual(Dummy: Integer = 0);
    begin
      inherited Create();
    end;
     
     
    class procedure TClassVCLDelphiFactory.CreateInstanceCPP(var Reference; AOwner: TComponent = nil);
    var
      Instance: TObject absolute Reference;
    begin
      if Self.InheritsFrom(TComponent) then
        Instance := TComponentClass(Self).Create(AOwner)
      else
        Instance := Self.CreateVirtual();
    end;
     
    end.
    HPP Généré
    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
    typedef void *TClassVCLDelphiVirtualizer;
     
    class DELPHICLASS TClassVCLDelphiFactory;
    class PASCALIMPLEMENTATION TClassVCLDelphiFactory : public System::TObject 
    {
    	typedef System::TObject inherited;
     
    public:
    	/*         class method */ static void __fastcall CreateInstanceCPP(TMetaClass* vmt, void *Reference, Classes::TComponent* AOwner = (Classes::TComponent*)(0x0));
    public:
    	#pragma option push -w-inl
    	/* TObject.Create */ inline __fastcall TClassVCLDelphiFactory(void) : System::TObject() { }
    	#pragma option pop
    	#pragma option push -w-inl
    	/* TObject.Destroy */ inline __fastcall virtual ~TClassVCLDelphiFactory(void) { }
    	#pragma option pop
     
    };
    le Paramètre Dummy permet de simuler un overload pour que C++ comprenne que ce constructeur existe, et c'est le __fastcall virtual () qui utilisé comme constructeur par défaut

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    //---------------------------------------------------------------------------
    // TClassVCLDelphi
    //---------------------------------------------------------------------------
    /*constructor virtual*/ __fastcall TClassVCLDelphi::TClassVCLDelphi()
    {
      InternalConstructor(); // Je n'ai pas trouvé comment appelé un constructeur de la même classe pour partager une Initialisation commune
      FMessages->Add(" () ");
     
      this->PublicValue = -2;
    }
     
    ... Plein de Variante de TClassVCLDelphi(xxx) avec bool, int, char*, String ...
     
    //---------------------------------------------------------------------------
    /*constructor*/ void TClassVCLDelphi::InternalConstructor()
    {
      FMessages = new TStringList();
      FMessages->Add(String().sprintf("%s [%s]", String(this->ClassName()), typeid(this).name()));
    }
    //---------------------------------------------------------------------------
    // TClassVCLDelphiHerited
    //---------------------------------------------------------------------------
    /*constructor virtual*/ __fastcall TClassVCLDelphiHerited::TClassVCLDelphiHerited()
    {
      FMessages->Add(String().sprintf("%s [%s]", String(this->ClassName()), typeid(this).name()));
    };
    //---------------------------------------------------------------------------
    // TComponentHerited
    //---------------------------------------------------------------------------
    /*constructor*/ __fastcall TComponentHerited::TComponentHerited(TComponent* AOwner)
      : TComponent(AOwner)
    {
      if (AOwner && AOwner->InheritsFrom(__classid(TMemo)))
        ((TMemo*)AOwner)->Lines->Add(String().sprintf("%s [%s]", String(this->ClassName()), typeid(this).name()));
    }
    J'ai bien la bonne chaine d'appel des constructeurs avec CreateInstanceCPP !
    Le class helper est invisible pour le C++ et permet d'avoir ce constructeur virtuel depuis TObject et TPersistent !
    Et on peut partir d'un TComponent et son constructeur Virtuel !
    Si un jour, j'ai vraiment envie de faire cela dans un projet, je saurais le faire !
    Mais au final, c'est bien plus long qu'un case\switch ou if if if alors qu'en Delphi cela donnait un code plus concis !


    Pour projet dont j'ai la maintenance, j'ai utilise les __property pour que mes accesseurs allouent les objets à la volée au lieu de le faire dans le constructeur, la plupart étant des TSQLQuery ou autre hérité de TComponent, un petit Owner pas besoin de libérer explicitement ...
    D'ailleurs ces objets avec __property sont eux même des TComponent (pour stocker des données relatives aux Cells d'une TStringGrid)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
      TClassVCLDelphi *ObjVCLDelphiByMeta;
      TClassVCLDelphiFactory::CreateInstanceCPP(__classid(TClassVCLDelphi), &ObjVCLDelphiByMeta);
      MemoTrace->Lines->Add(Format("Class VCL Delphi Meta -2 : %d (Other %d) - %s", ARRAYOFCONST((ObjVCLDelphiByMeta->PublicValue, ObjVCLDelphiByMeta->OtherPublicValue, ObjVCLDelphiByMeta->MessageText))));
     
      TClassVCLDelphi *ObjVCLDelphiByMeta2;
      TClassVCLDelphiFactory::CreateInstanceCPP(__classid(TClassVCLDelphiHerited), &ObjVCLDelphiByMeta2);
      MemoTrace->Lines->Add(Format("Class VCL Delphi Herited Meta -2 : %d (Other %d) - %s", ARRAYOFCONST((ObjVCLDelphiByMeta2->PublicValue, ObjVCLDelphiByMeta2->OtherPublicValue, ObjVCLDelphiByMeta2->MessageText))));
     
      TComponentHerited *ObjComponentByMeta;
      MemoTrace->Lines->Add("Class VCL Delphi Component Herited Meta : ");
      TClassVCLDelphiFactory::CreateInstanceCPP(__classid(TComponentHerited), &ObjComponentByMeta, MemoTrace);
    MessageText étant un accesseur à FMessages->Text


    J'ai lu pas mal de sujet sur cette histoire, on voit que les Développeurs C++ ne comprenne pas l'idée d'avoir des méthodes "virtual static" et qu'il y ait un héritage des MetaClass !
    Si par hazard, j'arrive à trouver tout de même un vrai code C++ !
    En fait, la conception objet de la VCL Borland est tout simplement incompatible avec C++, c'est assez instructif tout ça !
    Je comprend maintenant pourquoi dans les Exemples C++ de Pattern, il y a toujours autant de Classes, même certains exemples Delphi traduit sont inutilement verbeux tout simplement parce qu'ils n'utilisent pas de méthodes "virtual static"
    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

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

Discussions similaires

  1. Constructeur virtuel quel utilité ?
    Par helmis dans le forum Langage
    Réponses: 4
    Dernier message: 26/09/2008, 14h31
  2. [Stratégie] Pattern Strategie avec parametres variables
    Par new.proger dans le forum Design Patterns
    Réponses: 3
    Dernier message: 10/06/2007, 20h48
  3. [POO]Probléme de constructeur virtuel surchargé
    Par Laurent Dardenne dans le forum Delphi
    Réponses: 10
    Dernier message: 15/08/2006, 12h19
  4. Réponses: 4
    Dernier message: 22/12/2004, 14h28

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