Comment gérer les génériques Delphi dans C++
Remonter à Gestion des fonctionnalités Delphi dans C++Builder 2009 - Index
Cette rubrique décrit quelques problèmes de programmation que vous pourriez rencontrer avec les génériques, une des nouvelles fonctionnalités de Delphi.
Les génériques Delphi sont exposés dans C++ comme des templates. Toutefois, il est important de réaliser que les instanciations surviennent du côté Delphi, pas dans C++. Par conséquent, vous pouvez seulement utiliser ces templates pour les types qui étaient instanciés explicitement dans le code Delphi. Par exemple, déclarons un simple générique, TList<T>, dans Delphi :
interface
type
MyTList<T> = class(TList<T>) // TList est une classe de l'espace
// de nommage Generics.Collections
FItems: array of T;
protected
function GetLength: Integer;
public
function Get(Index: Integer): T;
published
property Len: Integer read GetLength;
end;
{ ScoreList dérivé d'un TList<double>}
ScoreList = class(MyTList<double>)
end;
{ StringList dérivé d'un TList<string>}
StringList = class(MyTList<string>)
end;
implementation
{$R *.dfm}
function MyTList<T>.GetLength: Integer;
begin
Result := Count;
end;
function MyTList<T>.Get(Index: Integer): T;
begin
Result := Items[Index];
end;
L'interface ci-dessus est exposée dans C++ comme suit :
// La déclaration de template générée par les types paramétrés de Delphi
// est utilisée pour accéder aux champs et variables de Delphi.
// N'instanciez pas avec les nouveaux paramètres de type dans le code utilisateur.
template<typename T> class PASCALIMPLEMENTATION
MyTList__1 : public Generics_collections::TList__1<T>
{
typedef Generics_collections::TList__1<T> inherited;
private:
typedef DynamicArray<T> _MyTList__1__1;
public:
_MyTList__1__1 FItems;
protected:
int __fastcall GetLength(void);
public:
T __fastcall Get(int Index);
__published:
__property int Len = {read=GetLength, nodefault};
public:
/* TList<T>.Create */ inline __fastcall MyTList__1(void)
/* overload */ : Generics_collections::TList__1<T>() { }
/* TList<T>.Destroy */ inline __fastcall virtual ~MyTList__1(void) { }
};
class DELPHICLASS ScoreList;
class PASCALIMPLEMENTATION ScoreList : public MyTList__1<double>
{
typedef MyTList__1<double> inherited;
public:
/* TList<Double>.Create */ inline __fastcall ScoreList(void)
/* overload */ : MyTList__1<double>() { }
/* TList<Double>.Destroy */ inline __fastcall virtual ~ScoreList(void) { }
};
class DELPHICLASS StringList;
class PASCALIMPLEMENTATION StringList : public MyTList__1<System::UnicodeString>
{
typedef MyTList__1<System::UnicodeString> inherited;
public:
/* TList<string>.Create */ inline __fastcall StringList(void)
/* overload */ : MyTList__1<System::UnicodeString>() { }
/* TList<string>.Destroy */ inline __fastcall virtual ~StringList(void) { }
};
La liaison du code C++ avec le fichier .obj créé à partir de l'unité Delphi ci-dessus peut utiliser des instances de TList__1<double> ou ScoreList.
void UseScoreList()
{
ScoreList* list = new ScoreList();
list->Add(1.0);
list->Add(2.0);
int len = list->Len;
assert(len == 2);
delete list;
}
void UseTList__1()
{
// Le code C++ peut utiliser les génériques définis directement dans Delphi
// tant que le code C++ se limite lui-même aux types pour lesquels
// le générique était instancié sur la taille Delphi. Par exemple,
// puisque test.pas utilise TList<String> et TList<double>, vous pouvez utiliser
// ceci ici. Toutefois, si nous tentons d'utiliser TList__1>char>, nous obtiendrons
// une erreur car le côté Delphi n'instanciait pas
// TList<AnsiChar>.
TList__1<double>* dblList = new MyTList__1<double>();
dblList—>Add(1.0);
dblList—>Add(1.5);
double d = dblList—>Get(1);
delete dblList;
MyTList__1<UnicodeString> *stringList = new MyTList__1<UnicodeString>();
stringList->Add("hiya");
stringList->Add("there");
stringList->Add("buckeroo");
UnicodeString dstring = stringList->Get(0);
delete stringList;
}
Si le code C++ tente d'utiliser un générique Delphi pour les types qui n'étaient pas instanciés dans Delphi, vous obtiendrez des erreurs lors de la liaison. Par exemple, le code suivant tente d'utiliser TList__1<char> quand le code Delphi n'instanciait pas explicitement TList<AnsiChar> :
void UseListOfChar()
{
TList__1<char>* charList = new TList__1<char>();
charList->Add('a');
char ch = charList->Get(1);
delete charList;
}
Tandis que le code ci-dessus est correctement compilé, les erreurs suivantes sont générées lors de la liaison :
[Erreur ILINK32] Erreur : Unresolved external
'Test::MyTList__1<char>::>::' référencé depuis USETEST.OBJ
[Erreur ILINK32] Erreur : '__fastcall
Test::MyTList__1<char>>::Add(char)' externe non résolu, référencé depuis USETEST.OBJ
[Erreur ILINK32] Erreur : '__fastcall
Test::MyTList__1<char>::Get(int)' externe non résolu, référencé depuis USETEST.OBJ
Pour éliminer l'erreur, assurez-vous que le code Delphi utilise le type MyTList<AnsiChar>.