Behandlung von Delphi-Generics in C++
Nach oben zu Behandlung von Delphi-Features in C++Builder - Index
In diesem Thema werden einige Programmierfragen beschrieben, die bei der Arbeit mit Generics, einem von Delphi unterstützten Feature, auftreten können.
Delphi-Generics werden für C++ als Templates bereitgestellt. Die Instantiierungen finden jedoch in Delphi und nicht in C++ statt. Daher können Sie diese Template nur für Typen verwenden, die explizit im Delphi-Code instantiiert wurden. Im folgenden Beispiel wird ein einfaches Generic, TList<T>
, in Delphi deklariert:
unit MyUnit;
interface
uses System.Generics.Collections;
type
MyTList<T> = class(TList<T>)
public
// Anchors constructor/destructor
constructor Create;
destructor Destroy; override;
class procedure Cleanup(var L: MyTList<T>); static;
end;
// DoubleList: instantiates MyTList<double>
DoubleList = class(MyTList<double>)
end;
// StringList: instantiates MyTList<string>
StringList = class(MyTList<string>)
end;
implementation
class procedure MyTList<T>.Cleanup(var L: MyTList<T>);
begin
L.Free;
end;
constructor MyTList<T>.Create;
begin
inherited;
end;
destructor MyTList<T>.Destroy;
begin
inherited;
end;
end.
Das obige Interface wird folgendermaßen für C++ bereitgestellt:
#ifndef DelphiunitHPP
#define DelphiunitHPP
#pragma delphiheader begin
#pragma option push
#pragma option -w // Display all warnings
#pragma option -w-inl // Functions %s are not expanded inline
#pragma option -w-8111 // Accessing deprecated entity
#pragma option -Vx // Zero-length empty class member
#pragma pack(push,8)
#include <System.hpp> // Pascal unit
#include <SysInit.hpp> // Pascal unit
#include <System.Generics.Collections.hpp> // Pascal unit
#include <System.Generics.Defaults.hpp> // Pascal unit
#include <System.Types.hpp> // Pascal unit
//-- user supplied -----------------------------------------------------------
namespace Delphiunit
{
//-- type declarations -------------------------------------------------------
template<typename T> class DELPHICLASS MyTList__1;
// Template declaration generated by Delphi parameterized types is
// used only for accessing Delphi variables and fields.
// Don't instantiate with new type parameters in user code.
template<typename T> class PASCALIMPLEMENTATION MyTList__1 : public System::Generics::Collections::TList__1<T>
{
typedef System::Generics::Collections::TList__1<T> inherited;
public:
__fastcall MyTList__1(void);
__fastcall virtual ~MyTList__1(void);
static void __fastcall Cleanup(MyTList__1<T>* &L);
};
class DELPHICLASS DoubleList;
class PASCALIMPLEMENTATION DoubleList : public MyTList__1<double>
{
typedef MyTList__1<double> inherited;
public:
/* {MyUnit}MyTList<System_Double>.Create */ inline __fastcall DoubleList(void) : MyTList__1<double>() { }
/* {MyUnit}MyTList<System_Double>.Destroy */ inline __fastcall virtual ~DoubleList(void) { }
};
class DELPHICLASS StringList;
class PASCALIMPLEMENTATION StringList : public MyTList__1<System::UnicodeString>
{
typedef MyTList__1<System::UnicodeString> inherited;
public:
/* {MyUnit}MyTList<System_string>.Create */ inline __fastcall StringList(void) : MyTList__1<System::UnicodeString>() { }
/* {MyUnit}MyTList<System_string>.Destroy */ inline __fastcall virtual ~StringList(void) { }
};
//-- var, const, procedure ---------------------------------------------------
} /* namespace Delphiunit */
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_DELPHIUNIT)
using namespace Delphiunit;
#endif
#pragma pack(pop)
#pragma option pop
#pragma delphiheader end.
//-- end unit ----------------------------------------------------------------
#endif // DelphiunitHPP
C++-Code, der die .obj
-Datei linkt, die aus der obigen Delphi-Unit erstellt wurde, kann Instanzen von MyTList__1<double>
oder MyTList__1<System::String>
verwenden.
C++-Code kann die in Delphi definierten Generics direkt verwenden, solange der C++-Code auf Typen beschränkt ist, für die die Generics in Delphi instantiiert wurden. Beispielsweise können hier MyTList<String>
und MyTList<double>
verwendet werden, weil die Delphi-Unit beide instantiiert. Wenn jedoch versucht wird, MyTList__1<char>
zu verwenden, werden Fehler ausgelöst, da im Delphi-Code MyTList<AnsiChar>
nicht instantiiert wurde.
void UseDLists()
{
MyTList__1<double>* dblList = new MyTList__1<double>();
dblList->Add(1.0);
dblList->Add(1.5);
double d = dblList->Items[1];
#ifdef _WIN64
delete dblList
#else
MyTList__1<double>::Cleanup(dblList);
#endif
MyTList__1<System::String> *strList = new MyTList__1<System::String>();
strList->Add("First");
strList->Add("Second");
strList->Add("Third");
assert(strList->Count == 3);
System::String str = strList->Items[0];
assert(str == "First");
assert(strList->Items[1] == "Second");
assert(strList->Items[2] == "Third");
strList->Insert(0, "Inserted");
assert(strList->Count == 4);
assert(strList->Items[0] == "Inserted");
assert(strList->Items[1] == "First");
strList->Reverse();
assert(strList->Items[0] == "Third");
assert(strList->Items[1] == "Second");
assert(strList->Items[2] == "First");
assert(strList->Items[3] == "Inserted");
assert(strList->Contains("Inserted"));
assert(!strList->Contains("Not Inserted"));
strList->Sort();
strList->Remove("Inserted");
assert(strList->Items[0] == "First");
assert(strList->Items[1] == "Second");
assert(strList->Items[2] == "Third");
#ifdef _WIN64
delete strList;
#else
MyTList__1<System::String>::Cleanup(strList);
#endif
}
Wenn C++-Code versucht, ein Delphi-Generic für Typen zu verwenden, die nicht in Delphi instantiiert wurden, erhalten Sie beim Linken Fehler. Der folgende Code versucht beispielsweise, MyTList__1<char>
zu verwenden, obwohl im Delphi-Code MyTList<AnsiChar>
nicht explizit instantiiert wurde:
void UseListOfChar()
{
MyTList__1<char>* charList = new MyTList__1<char>();
charList->Add('a');
// …
}
Der obige Quelltext kann compiliert werden, erzeugt aber beim Linken die folgenden Fehler:
[ilink32 Fehler] Fehler: Nicht auflösbares externes 'Delphiunit::MyTList__1<char>::' referenziert von CPPUNIT.OBJ
[ilink32 Fehler] Fehler: Nicht auflösbares externes '__fastcall Delphiunit::MyTList__1<char>::MyTList__1<char>()' referenziert von CPPUNIT.OBJ
[ilink32 Fehler] Fehler: Nicht auflösbares externes '__fastcall System::Generics::Collections::TList__1<char>::Add(const const char)' referenziert von CPPUNIT.OBJ
[ilink32 Fehler] Fehler: Linken kann nicht ausgeführt werden
Um diesen Fehler auszuschließen, müssen Sie sicherstellen, dass im Delphi-Code der Typ MyTList<AnsiChar>
verwendet wird.