Behandlung von Object Pascal-Generics in C++

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Behandlung von Object Pascal-Features in Appmethod C++ - Index

Dieses Thema beschreibt einige Programmierfragen, die bei der Arbeit mit Generics - ein von Object Pascal unterstütztes Feature - auftreten können.

Object Pascal-Generics werden für C++ als Templates bereitgestellt. Es ist aber wichtig, zu wissen, dass die Instantiierungen in Object Pascal und nicht in C++ stattfinden. Daher können Sie diese Template nur für Typen verwenden, die explizit im Object Pascal-Code instantiiert wurden. Im folgenden Beispiel wird ein einfaches Generic, TList<T>, in Object Pascal deklariert:

 
unit DelphiUnit;

interface
  uses System.Generics.Collections;

type
  MyTList<T> = class(TList<T>)
  public
    // Ankerkonstruktor/-destruktor
    constructor Create;
    destructor Destroy; override;

    class procedure Cleanup(var L: MyTList<T>); static;
  end;

  // DoubleList: instantiiert MyTList<double>
  DoubleList = class(MyTList<double>)
  end;

  // StringList: instantiiert 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:

 
// CodeGear C++Builder
// Copyright (c) 1995, 2012 von Embarcadero Technologies, Inc.
// Alle Rechte vorbehalten

// (NICHT BEARBEITEN: vom Computer erzeugter Header) 'DelphiUnit.pas' rev: 24.00 (Windows)

#ifndef DelphiunitHPP
#define DelphiunitHPP

#pragma delphiheader begin
#pragma option push
#pragma option -w       // Alle Warnungen anzeigen
#pragma option -w-inl   // Funktionen %s werden nicht als Inline expandiert
#pragma option -w-8111  // Zugriff auf veraltete Entität 
#pragma option -Vx      // Leere Klassenelemente auf 0 setzen
#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

//-- vom Benutzer bereitgestellt -----------------------------------------------------------

namespace Delphiunit
{
//-- Typdeklarationen -------------------------------------------------------
template<typename T> class DELPHICLASS MyTList__1;
 // Template-Deklaration, die von parametrisierten Delphi-Typen erzeugt wurde,
 // wird nur für den Zugriff auf Delphi-Variablen und -Felder verwendet.
 // Instantiieren Sie sie nicht mit neuen Typparametern im Benutzercode.
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:
	/* {DelphiUnit}MyTList<System_Double>.Create */ inline __fastcall DoubleList(void) : MyTList__1<double>() { }
	/* {DelphiUnit}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:
	/* {DelphiUnit}MyTList<System_string>.Create */ inline __fastcall StringList(void) : MyTList__1<System::UnicodeString>() { }
	/* {DelphiUnit}MyTList<System_string>.Destroy */ inline __fastcall virtual ~StringList(void) { }

};


//-- var, const, Prozedur ---------------------------------------------------
}	/* 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.
//-- Ende-Unit----------------------------------------------------------------
#endif	// DelphiunitHPP

C++-Code, der die .obj-Datei linkt, die aus der obigen Object Pascal-Unit erstellt wurde, kann Instanzen von MyTList__1<double> oder MyTList__1<System::String> verwenden.

 
void UseDLists()
{
   // C++-Code kann die in Delphi definierten Generics direkt verwenden,
   // solange der C++-Code auf Typen beschränkt ist, für die
   // das Generic in Delphi instantiiert wurde. Zum Beispiel:
   // Weil die Delphi-Unit MyTList<String> und MyTList<double> instantiiert, können diese
   // hier verwendet werden. Wenn aber versucht wird, MyTList__1<char> zu verwenden, werden
   // Fehler ausgegeben, weil in Delphi
   // MyTList<AnsiChar> nicht instantiiert wurde.
  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 Object Pascal-Generic für Typen zu verwenden, die nicht in Object Pascal instantiiert wurden, erhalten Sie beim Linken Fehler. Der folgende Code versucht beispielsweise, MyTList__1<char> zu verwenden, obwohl der Object Pascal-Code MyTList<AnsiChar> nicht explizit instantiiert hat:

 
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 der Object Pascal-Code den Typ MyTList<AnsiChar> verwendet.

Siehe auch