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 :
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
};
typedef System::DelphiInterface< IMapremiereInterface > _di_IMapremiereInterface; |
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:
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);
public:
virtual TDateTime __stdcall GetDate();
virtual void __stdcall SetDate(TDateTime Value);
virtual AnsiString __stdcall GetNote();
virtual void __stdcall SetNote(AnsiString Value);
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:
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:
_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();
Fagenda= mCalc->Supports(mAgenda);
}
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(); |
A partir de cette Delphinterface on peut retrouver toutes les interfaces implémentés par la classe d'implémentation :
Fagenda= mCalc->Supports(mAgenda); |
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.
 
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.
|