Vererbung / generische Interfaces

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Joz
Beiträge: 40
Registriert: Mo 27. Mai 2013, 13:16
OS, Lazarus, FPC: Arch Linux, OpenSuse 13.2, Lazarus 1.4
CPU-Target: AMD64
Wohnort: Berlin

Vererbung / generische Interfaces

Beitrag von Joz »

Moin alle zusammen,

Ich habe eine generische Klasse TSmartArray, die ein (ebenso generisches) Interface IList implementiert.
Nun habe ich eine Prozedur, deren Signatur einen Parameter vom Typ IList<QWord> definiert. Ich kann dieser Prozedur aber keinen Parameter vom Typ TSmartArray<QWord> übergeben.

Die Klassenstruktur ist ungefähr folgende:

Code: Alles auswählen

 
 
{Unit Lists.pas }
	generic IList<T> = interface
		function GetLow(): Int64;
		function GetHigh(): Int64;
		function GetLength(): Int64;
		function GetElement(Index: Int64): T;
		procedure SetElement(Index: Int64; Element: T);
		procedure Add(ELement: T);
		function GetEnumerator: specialize IListEnumerator<T>;
		property Element[Index: Int64]: T read GetElement write SetElement; default;
		property Length: Int64 read GetLength;
		property Low: Int64 read GetLow;
		property High: Int64 read GetHigh;
	end;
 
{Unit SmartArrayList.pas }
	generic TSmartArrayList<T> = class(TInterfacedObject, specialize IList<T>)
	type
		TTArray = array of T;
		ITList = specialize IList<T>;
		IEnumerator = specialize IListEnumerator<T>;
		TEnumerator = specialize TAllListsEnumerator<T>;
	public
		constructor Create;
		constructor Create(AArray: TTArray);
		procedure SetLow(ALow: Int64);
		function GetLow: Int64;
		function GetLength: Int64;
		function GetHigh: Int64;
		function GetElement(Index: Int64): T;
		procedure SetElement(Index: Int64; Element: T);
		procedure Add(ELement: T);
		procedure AddMany(Elements: ITList);
		function GetEnumerator: IEnumerator;
		destructor Destroy; override;
		property Low: Int64 read GetLow write SetLow;
		property Length: Int64 read GetLength;
		property High: Int64 read GetHigh;
		property Elements[Index: Int64]: T read GetElement write SetElement; default;
		{ Die anfängliche Größe des internen Speichers. }
		property InitMemLength: Int64 read FInitMemLength write FInitMemLength;
	end;
 
{ Unit primes.pas}
	IIntList = specialize IList<QWord>;
	TCallback = function(Current: QWord; Len: Int64): Boolean of object;
 
	procedure GenPrimes(List: IIntList; Callback: TCallback);
 
{ Program reckonprimes.pas }
	TQWordList = specialize TSmartArrayList<QWord>;
 
	begin
		PrimeList := TQWordList.Create;
		Primes.GenPrimes(PrimeList, @CallbackObj.Cllbck);	// Und an dieser Stelle kommt der Fehler
	end.
 

Code: Alles auswählen

Free Pascal Compiler version 2.6.4 [2014/03/12] for x86_64
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling genprimes.pas
reckonprimes.pas(40,28) Error: Incompatible type for arg no. 1: Got "TSmartArrayList$QWord", expected "IList$QWord"
Fatal: Compilation aborted
An unhandled exception occurred at $000000000049C313 :
EAccessViolation : Access violation
  $000000000049C313
  $00000000005430D8
  $000000000054763D
  $0000000000532959
  $000000000052D607
  $0000000000453CE8
  $000000000056FA34
  $000000000042391F
 
Error: /usr/bin/ppcx64 returned an error exitcode (normal if you did not specify a source file to be compiled)
Incompatible type for arg no. 1: Got "TSmartArrayList$QWord", expected "IList$QWord"

Theoretisch müsste doch "TSmartArrayList$QWord" "IList$QWord" implementieren. Hab ich einen Denkfehler? Ist das Typsystem von FPC 2.6 noch nicht so weit, sollte ich auf Devlopment-Versionen wechseln? :?:

Ich freue mich auf Antworten,
Joz

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Vererbung / generische Interfaces

Beitrag von Socke »

Bei jeder Spezialisierung werden neue Typen definiert. In deinem Quelltext hast du also drei unterschiedliche Typen definiert:

Code: Alles auswählen

generic TSmartArrayList<T> = class(TInterfacedObject, specialize IList<T>)  // hier wird die 2. Spezialisierung aufgelöst
IIntList = specialize IList<QWord>; // 1. Spezialisierung
TQWordList = specialize TSmartArrayList<QWord>; // 2. Spezialisierung
ITList = specialize IList<T>; // dritte Spezialisierung
Diese drei Typen sind in Pascal nicht zuweisungskompatibel (auch wenn sie binär identisch sein sollten, da sie Interfaces sind).

Du solltest die Spezialisierung der Interfaces von der Spezialisierung der Klasse trennen oder das Interface als Sub-Typ der Klasse definieren - sinngemäß:

Code: Alles auswählen

type tmyclass = class
public type
  imyinterface = interface
  end
end;
Bei deiner Struktur beraubst du dich der einzigen Möglichkeit, Interfaces zur Laufzeit zu identifizieren. Hierzu werden GUIDs verwendet. Diese können jedoch bei der Spezialisierung nicht angegeben werden. Hier musst du dann ggf. nach der Spezialisierung noch einmal vererben um eine GUID zum Interface hinzufügen zu können:

Code: Alles auswählen

type
generic imyi<T> = interface
    ['{B5681B52-AD31-4005-AC32-35C03488FE88}']
    procedure DoIt(arg1: T);
  end;
  imy1 = specialize imyi<Word>;  // hat die selbe GUID wie imyi
  imy1a = interface(imy1)
    ['{16617C29-D650-401D-AB6A-FA48F34354FC}']
  end;
Auf diese Weise kannst du über die einschlägigen Methoden das passende Interface suchen. und an die Methode GenPrimes übergeben.

Falls damit dein Problem noch nicht gelöst sein sollte, poste doch bitte ein vollständiges Beispielprojekt. In deinem Code fehlt z.B. die Deklaration und Definition von CallbackObj.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Antworten