Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic method returning generic interface in Delphi 2010

Given the code below, wich is a very trimmed down version of the actual code, I get the following error:

[DCC Error] Unit3.pas(31): E2010 Incompatible types: 'IXList<Unit3.TXList<T>.FindAll.S>' and 'TXList<Unit3.TXList<T>.FindAll.S>'

In the FindAll<S> function.

I can't really see why since there is no problem with the previous very similar function.

Can anyone shed some light on it?
Is it me or is it a bug in the compiler?

unit Unit3;

interface
uses Generics.Collections;

type
  IXList<T> = interface
  end;

  TXList<T: class> = class(TList<T>, IXList<T>)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    function Find: IXList<T>;
    function FindAll<S>: IXList<S>;
  end;

implementation
uses Windows;

function TXList<T>.Find: IXList<T>;
begin
  Result := TXList<T>.Create;
end;

function TXList<T>.FindAll<S>: IXList<S>;
begin
  Result := TXList<S>.Create; // Error here  
end;

function TXList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := E_NoInterface;
end;

function TXList<T>._AddRef: Integer;
begin
  InterlockedIncrement(FRefCount);
end;

function TXList<T>._Release: Integer;
begin
  InterlockedDecrement(FRefCount);
  if FRefCount = 0 then Self.Destroy;
end;

end.

Thanks for the answers! It seems like a compiler bug with an acceptable workaround available.

With the interface declared as

IXList<T: class> = interface
   function GetEnumerator: TList<T>.TEnumerator;
end;

and findall implemented as

function TXList<T>.FindAll<S>: IXList<S>;
var
  lst: TXList<S>;
  i: T;
begin
  lst := TXList<S>.Create;
  for i in Self do
    if i.InheritsFrom(S) then lst.Add(S(TObject(i)));

  Result := IXList<S>(IUnknown(lst));
end;

I got it working in a simple example.

Doing something like:

var
  l: TXList<TAClass>;
  i: TASubclassOfTAClass;
begin
.
.
.
for i in l.FindAll<TASubclassOfTAClass> do
begin
   // Do something with i
end;
like image 408
PeterS Avatar asked Aug 26 '10 10:08

PeterS


2 Answers

With three minor modification (IInterface, FindAll with "S: class" [Thanks Mason] and the typecasts in FindAll) I got it compiling.

Full code:

unit Unit16;

interface

uses
  Generics.Collections;

type
  IXList<T> = interface
  end;

  TXList<T: class> = class(TList<T>, IInterface, IXList<T>)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    function Find: IXList<T>;
    function FindAll<S: class>: IXList<S>;
  end;

implementation
uses Windows;

function TXList<T>.Find: IXList<T>;
begin
  Result := TXList<T>.Create;
end;

function TXList<T>.FindAll<S>: IXList<S>;
begin
  Result := IXList<S>(IUnknown(TXList<S>.Create));
end;

function TXList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := E_NoInterface;
end;

function TXList<T>._AddRef: Integer;
begin
  InterlockedIncrement(FRefCount);
end;

function TXList<T>._Release: Integer;
begin
  InterlockedDecrement(FRefCount);
  if FRefCount = 0 then Self.Destroy;
end;

end.
like image 107
Uwe Schuster Avatar answered Nov 15 '22 10:11

Uwe Schuster


That definitely looks like a compiler error. They're saying how they've focused a lot of effort into improving Generics issues for the next version, Delphi XE. When it gets released, which should be within the next couple weeks, download the preview and see if that will compile now. If not, try filing a bug report with QC.

Also, FindAll<S> should probably be declared as function FindAll<S: class>: IXList<S>;. That doesn't fix the error, but a working compiler would probably give you an error on that.

like image 23
Mason Wheeler Avatar answered Nov 15 '22 10:11

Mason Wheeler