Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence
FORUMS C FAQs C TUTORIELS C LIVRES C COMPILATEURS C SOURCES GTK+

Technique : Travailler avec les Interfaces en C++Builder Partie 1

Date de publication : 13/03/2006

Par DjmSoftware
 

niveau : moyen

durée : 30 minutes

Ce tutorial démontre l'utilisation des Interfaces en C++ Builder avec un exemple d'application


Sources du Programme disponible ici.


Définition
1.1. Interface IUnknow
1.2. IInterface
1.3. DelphiInterface
2. Définition d'une Interface
2.1. Utilisation d'une interface
2.2. Déclaration d'une Interface
3. La Classe d'implémentation
3.1. Classe TInterfacedObject
3.2. Définition d'une classe d'implémentation
4. Une Application Exemple
4.1. Marche à suivre
4.2. Interface ICalc
4.3. Interface IAgenda
4.4. La Classe d'implémentation
5. L'application
6. Conclusion


Définition

Le Pascal Objet n'est pas conçu pour profiter de l'héritage multiple. Pour pallier ce manque, Borland a implémenté à partir de la version 4 de la VCL l'utilisation des interfaces basées sur COM.


1.1. Interface IUnknow

Définition tirée de l'aide Borland
IUnknown est considérée comme l'interface de base pour toutes les autres interfaces, y compris les interfaces définies en Pascal Objet.
IUnknown présente la méthode QueryInterface, qui est utile pour découvrir et utiliser d'autres interfaces implémentées par le même objet.
IUnknown introduit également les méthodes de comptage de références AddRef et Release. Un code qui obtient un pointeur d'interface doit appeler AddRef dès qu''il acquiert le pointeur, et appeler Release lorsque'il en a terminé avec lui.


1.2. IInterface

IInterface est l'interface de base pour les interfaces définies en code Pascal Objet.
IInterface est considérée comme une interface fille de IUnknown, et IUnknown est considérée
comme l'interface de base pour toutes les interfaces.


1.3. DelphiInterface

DelphiInterface est l'implémentation C++Builder du type intrinsèque Pascal Objet interface.


2. Définition d'une Interface

Une interface en C++ Builder est une classe abstraite.

Elle contient :

  • uniquement une partie public
  • des méthodes virtuelles pures
  • des property
  • des Events
Elle ne contient pas :

  • de données membres

2.1. Utilisation d'une interface

Appellation :
On précède le nom d'une interface avec la lettre I

Ex. : IDesigner Interface IDesigner

Déclaration:
Une interface se déclare avec la macro __interface

Composition:
Une interface se compose d'un GUID indispensable pour rechercher cette interface simplement
Ce GUID peut être généré automatiquement avec c++ Builder avec la combinaison de touche <CTRL+Shift+G>

La chaîne générée est au format Pascal.
['{B208D697-3B41-42A2-9129-82F30184BADB}']
Pour la rendre compatible avec C++ Builder il faut la modifier de la manière suivante :

("{B208D697-3B41-42A2-9129-82F30184BADB}")
Ce GUID s'utilise avec la macro INTERFACE_UUID.

Une interface descend toujours de la classe IInterface.
Pour les méthodes relatives à la classe IInterface veuillez consulter l'aide de Borland.
Une méthode intéressante s'appelle Supports.
Elle permet de retourner une autre DelphiInterface prise en charge à partir de la classe d'implémentation.
Nous verrons plus tard son utilisation.
Pour obtenir des facilités d'utilisation des interfaces proches de celle de Delphi, Borland met a disposition un Template :

<class T> class RTL_DELPHIRETURN DelphiInterface;



2.2. Déclaration d'une Interface

La déclaration d'une interface se fait toujours dans le Header(.h) et nécessite l'inclusion du fichier system.hpp
Exemple d'interface
  __interface  INTERFACE_UUID ("{B208D697-3B41-42A2-9129-82F30184BADB}") IMapremiereInterface public :  IInterface 
  {
   public:
   	virtual void __stdcall DitBonjour(void) = 0 ;// Méthode virtuelle pure
  };
  typedef System::DelphiInterface< IMapremiereInterface >  _di_IMapremiereInterface; // DelphiInterface

3. La Classe d'implémentation

Utilité : définir dans cette classe l'implémentation de toutes les méthodes exposées dans l'interface.


3.1. Classe TInterfacedObject

TInterfacedObject implémente l'interface IInterface (IUnknown) et peut être utilisée comme base pour des classes compatibles COM simples bénéficiant de l'implémentation des méthodes IInterface.

Différence avec Delphi:
En Delphi, IInterface et IUnknow sont identiques. En C++ elles diffèrent dans la signature des méthodes : IInterface et IUnknow sont différentes.
La conséquence est que la classe TInterfaceObject n'implémente pas les méthodes suivantes :
  virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
  virtual ULONG __stdcall AddRef()
  virtual ULONG __stdcallRelease();
Il est du devoir du programmeur d'implémenter ces 3 méthodes.


3.2. Définition d'une classe d'implémentation

  class PremiereInterfaceImpl : public TInterfacedObject ,public IMapremiereInterface
  {
   public:
    virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
    {
	   return TInterfacedObject::QueryInterface(IID, (void *)Obj);
    }
    virtual ULONG __stdcall AddRef()
    {
      return TInterfacedObject::_AddRef();
    }
    virtual ULONG __stdcall Release()
    {
	   return TInterfacedObject::_Release();
    }
   public:
    virtual void __stdcall DitBonjour(void);
	
   };
Point important:
La classe TInterfaceObject dispose d'un compteur de références.
Dés que ce compteur est à 0, le destructeur de la classe d'implémentation est automatiquement appelé.
La gestion mémoire est ainsi facilitée.


4. Une Application Exemple

Nous allons programmer sur la base des interfaces une petite application basique stimulant des outils de bureau


  • Calculatrice basique
  • Pense bête
Notre calculatrice doit pouvoir :

  • Additionner 2 nombres
  • Soustraire 2 Nombres
  • Multiplier 2 Nombres
  • Diviser 2 Nombres
  • Etablir la racine Carrée d'un nombre
  • Etablir le carré d'un nombre
Notre pense-bête doit pouvoir :

  • Mémoriser une note
  • Lire une note mémorisée
  • Donner la date et l'heure

4.1. Marche à suivre

Nous allons déclarer 2 interfaces :

  • Calculatrice ICalc
  • Pense Bête IAgenda

4.2. Interface ICalc

 __interface INTERFACE_UUID("{9EB5933E-7556-46FD-A3EB-85163E462809}") ICalc :  public IInterface
 {
  public:
   virtual int    __stdcall   Addition(int a,int b)=0;
   virtual int    __stdcall   Soustraction(int a,int b)=0;
   virtual float  __stdcall Division(int a,int b)=0;
   virtual int __stdcall Multiplication(int a, int b)=0;
   virtual float  __stdcall Racine(int a)=0;
   virtual long  __stdcall Carre(int a)=0;
 };
 typedef System::DelphiInterface <ICalc>  _di_Calc;
Toutes les fonctions désirées de notre calculatrice sont exposées en tant que méthodes virtuelles pures.


4.3. Interface IAgenda

__interface INTERFACE_UUID("{0243615E-7298-4CB4-A567-CAE70CEA1DB6}") IAgenda :  public IInterface
 {
  public:
   virtual TDateTime  __stdcall   GetDate()=0;
   virtual void  __stdcall   SetDate(TDateTime Value)=0;
   virtual AnsiString  __stdcall   GetNote()=0;
   virtual void  __stdcall   SetNote(AnsiString Value)=0;
   __property TDateTime  _Date={read = GetDate,write = SetDate};
   __property AnsiString  _Note={read =GetNote,write = SetNote};
 };
 typedef System::DelphiInterface<IAgenda>  _di_IAgenda;
Il est à noter l'utilisation de property.


4.4. La Classe d'implémentation

Déclaration

  class CalcImplementation : public TInterfacedObject ,public ICalc ,public IAgenda
  {
   public:
    // implementation de ICalc
    virtual int __stdcall  Addition(int a, int b);
    virtual int __stdcall  Soustraction(int a, int b);
    virtual float __stdcall Division(int a, int b);
    virtual int __stdcall Multiplication(int a, int b);
    virtual float __stdcall Racine(int a);
    virtual long __stdcall Carre(int a);
    // implementation de IAgenda
   public:  // utilisé pour clarifié la déclaration de l'interface IAgenda
    virtual TDateTime __stdcall GetDate();
    virtual void __stdcall SetDate(TDateTime Value);
    virtual AnsiString __stdcall GetNote();
    virtual void __stdcall SetNote(AnsiString Value);
   // implementation de TInterfacedObject
   public:
    virtual HRESULT __stdcall QueryInterface(const GUID& IID, void ** Obj);
    virtual ULONG __stdcall AddRef();
    virtual ULONG __stdcall Release();
   private:
     TDateTime Fdate;
     AnsiString Fnote;
	};

J'ai séparé dans un but de clarté l'implémentation de chaque interface.
Il est a noter que cette classe dispose de 2 membres privés permettant de stocker les valeurs Date Heure et Note.
Cette classe dispose d'un constructeur pour initialiser les données membres.

Implémentation

__stdcall CalcImplementation::CalcImplementation()
  :Fdate(NULL),
   Fnote("")
	{
	}
   //---------------------------------------------------------------------------
int __stdcall CalcImplementation::Addition(int a, int b) { return a+b; } //--------------------------------------------------------------------------- int __stdcall CalcImplementation::Soustraction(int a, int b) { return a-b; } //--------------------------------------------------------------------------- float __stdcall CalcImplementation::Division(int a, int b) { return static_cast<float>(a)/static_cast<float>(b); } //--------------------------------------------------------------------------- int __stdcall CalcImplementation::Multiplication(int a, int b) { return a*b; } //--------------------------------------------------------------------------- float __stdcall CalcImplementation::Racine(int a) { return sqrt(a); } //--------------------------------------------------------------------------- long __stdcall CalcImplementation::Carre(int a) { return a*a; } //--------------------------------------------------------------------------- HRESULT __stdcall CalcImplementation::QueryInterface(const GUID& IID, void ** Obj) { return TInterfacedObject::QueryInterface(IID, (void *)Obj); } //--------------------------------------------------------------------------- ULONG __stdcall CalcImplementation::AddRef() { return TInterfacedObject::_AddRef(); } //--------------------------------------------------------------------------- ULONG __stdcall CalcImplementation::Release() { return TInterfacedObject::_Release(); } //--------------------------------------------------------------------------- TDateTime __stdcall CalcImplementation::GetDate() { if (static_cast<int>(Fdate)) return Fdate; else return Now(); } //--------------------------------------------------------------------------- void __stdcall CalcImplementation::SetDate(TDateTime Value) { Fdate=Value; } //--------------------------------------------------------------------------- AnsiString __stdcall CalcImplementation::GetNote() { return Fnote; } //--------------------------------------------------------------------------- void __stdcall CalcImplementation::SetNote(AnsiString Value) { Fnote=Value; } //---------------------------------------------------------------------------

5. L'application

Déclaration
  class TForm1 : public TForm
  {
   __published:	// Composants gérés par l'EDI
      TPanel *Panel1;
      TLabeledEdit *lbledtB;
      TStatusBar *Stat1;
      TGroupBox *grp1;
      TLabeledEdit *lbledtResult;
      TLabeledEdit *lbledtA;
      TPanel *pnl1;
      TButton *btnbtminus;
      TButton *btnPlus;
      TButton *btnX;
      TButton *btndiv;
      TButton *btnRacine;
      TButton *btnCarre;
      TGroupBox *grp2;
      TLabel *lbl2;
      TEdit *edtReadNote;
      TButton *btnLireNote;
      TLabel *lbl3;
      TLabel *lbl4;
      TEdit *edtReadHeure;
      TButton *btnReadHeure;
      TEdit *edtWriteNote;
      void __fastcall btnbtminusClick(TObject *Sender);
      void __fastcall btnCarreClick(TObject *Sender);
      void __fastcall btndivClick(TObject *Sender);
      void __fastcall btnPlusClick(TObject *Sender);
      void __fastcall btnRacineClick(TObject *Sender);
      void __fastcall btnXClick(TObject *Sender);
      void __fastcall btnLireNoteClick(TObject *Sender);
      void __fastcall btnReadHeureClick(TObject *Sender);
      void __fastcall btnWriteNoteClick(TObject *Sender);
   private:	// Déclarations de l'utilisateur
       _di_ICalc mCalc;
       _di_IAgenda mAgenda;
       bool Fagenda;
   public:	
       __fastcall TForm1(TComponent* Owner);
  };
On notera comme membres privés les 2 Delphi interfaces :

  • _ di_ICalc mCalc;
  • _di_IAgenda mAgenda;
ainsi qu'une variable bool.

Implémentation

 __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
 {
   mCalc = new CalcImplementation();    // instanciation de l'interface
   Fagenda= mCalc->Supports(mAgenda);   // instanciation et test si interface existante
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnbtminusClick(TObject *Sender)
 {
  try
   {
    lbledtResult->Text=mCalc->Soustraction(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
   {
     Stat1->SimpleText=E.Message;
   }
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnCarreClick(TObject *Sender)
 {
  try
   {
    lbledtResult->Text=mCalc->Carre(StrToInt(lbledtA->Text));
   }
  catch(Exception &E)
   {
    Stat1->SimpleText=E.Message;
   }
  }
//--------------------------------------------------------------------------- 
 void __fastcall TForm1::btndivClick(TObject *Sender)
 {
  try
   {
    lbledtResult->Text=mCalc->Division(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
    {
     Stat1->SimpleText=E.Message;
    }
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnPlusClick(TObject *Sender)
 {
   try
    {
     lbledtResult->Text=mCalc->Addition(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
    }
    catch(Exception &E)
    {
     Stat1->SimpleText=E.Message;
    }
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnRacineClick(TObject *Sender)
 {
   try
    {
      lbledtResult->Text=mCalc->Racine(StrToInt(lbledtA->Text));
    }
    catch(Exception &E)
    {
      Stat1->SimpleText=E.Message;
    }
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnXClick(TObject *Sender)
 {
   try
   {
    lbledtResult->Text=mCalc->Multiplication(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
   {
    Stat1->SimpleText=E.Message;
   }
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnLireNoteClick(TObject *Sender)
 {
  if(Fagenda)
      edtReadNote->Text=mAgenda->_Note;
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnReadHeureClick(TObject *Sender)
 {
  if(Fagenda)
     edtReadHeure->Text=TimeToStr(mAgenda->_Date);
 }
//---------------------------------------------------------------------------
 void __fastcall TForm1::btnWriteNoteClick(TObject *Sender)
 {
   if(Fagenda)
     mAgenda->_Note=edtWriteNote->Text;
 }
//---------------------------------------------------------------------------
Dans le constructeur de la Form on instancie une des interfaces en appelant le constructeur de la classe d'implémentation.
L'interface ICalc est instanciée de la manière suivante :

	mCalc = new CalcImplementation();    // instanciation de l'interface
A partir de cette Delphinterface on peut retrouver toutes les interfaces implémentés par la classe d'implémentation :

	Fagenda= mCalc->Supports(mAgenda);   // instanciation et test si interface existante
Si l'interface passée en paramètre de la méthode Supports est bien implémentée, elle est instanciée et la valeur 0 est retournée.
Pour travailler ensuite avec ces delphiInterface il suffit d'appeler directement les méthodes implémentées.
Ex. :

	lbledtResult->Text = mCalc->Soustraction(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
La classe d'implémentation des DelphiInterfaces est automatiquement libérée à la fin de l'application.

J'ai joint avec le source un fichier d'aide réalisé avec Doxygen à la fin de l'application


6. Conclusion

Dans la partie deux d'utilisation des interfaces (en préparation) je vous propose la création de Plugin basée sur un tutorial Delphi de sjrd.


Voir également mes autres articles :
Utilisation des Files Mapping sous C++Builder C++ Builder
La technique des PatchFiles C++ Builder
Maîtrisez les files d'impression sous windows C++ Builder
Travailler avec les Interfaces en C++Builder Partie 1 C++ Builder
Composant de gestion des ports d'imprimantes C++ Builder
Programme de Test Version ActiveX du composant TDLIoport Delphi
Sans oublier :
la FAQ C++ Builder par Geronimo
la FAQ C
la FAQ C++

Valid XHTML 1.1!Valid CSS!

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur.
La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.
Responsable bénévole de la rubrique C : Arnaud Feltz (buchs) - Contacter par EMail :
Vos questions techniques : forum d'entraide C - Publiez vos articles, tutoriels et cours
et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones
Nous contacter - Copyright © 2000-2008 www.developpez.com - Legal informations.