Reputation: 1861
I am trying to create a generic list class for use with tiOPF (an Object Persistence Framework for delphi @ www.tiopf.com). Specifically I am trying to take an existing generic class (TtiObjectList) and make a generic version that uses TtiObject descenants.
I have limited scope for altering the base classes as they need to compile under D7 - D2009 and Free Pascal. I need to descend from TtiObjectList to keep the existing persistence mechanisms working.
// base class
type
TtiObjectList = class(TtiObject)
...
protected
function GetItems(i: integer): TtiObject; virtual;
procedure SetItems(i: integer; const AValue: TtiObject); virtual;
...
public
function Add(const AObject : TtiObject): integer; overload; virtual;
...
end;
My class is defined as follows:
TtiGenericObjectList<T: TtiObject> = class(TtiObjectList)
protected
function GetItems(i:integer): T; reintroduce;
procedure SetItems(i:integer; const Value: T); reintroduce;
public
function Add(const AObject: T): integer; reintroduce;
property Items[i:integer]: T read GetItems write SetItems; default;
end;
implementation
{ TtiGenericObjectList<T> }
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
obj:= TtiObject(AObject); /// Invalid typecast
result:= inherited Add(obj);
end;
// alternate add, also fails
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
begin
result:= inherited Add(AObject); /// **There is no overloaded version**
/// **of 'Add' that can be called with these arguments**
end;
function TtiGenericObjectList<T>.GetItems(i: integer): T;
begin
result:= T(inherited GetItems(i)); /// **Invalid typecast **
end;
procedure TtiGenericObjectList<T>.SetItems(i: integer; const Value: T);
begin
inherited SetItems(i, Value);
end;
The problem I have is that delphi is not seeing T as a TtiObject descendant. I am getting invalid typecast errors when I do something like:
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
obj:= TtiObject(AObject); /// **Invalid typecast***
result:= inherited Add(obj);
end;
If I don't do a type cast, then I get overload errors instead as shown in the listing above.
Any ideas where I am going wrong?
Sean
Upvotes: 2
Views: 3033
Reputation: 26371
(I got here due to a similar problem, but while nailing it down I got to a different solution, though with a newer(XE) Delphi):
Or simply try to make that local variable Obj of type T.
Upvotes: 0
Reputation: 725
I recently tackled a similar problem using D2010, and here is the code I came up with.
type
TGenericList<T: TtiObject> = class(TtiObjectList)
protected
function GetItems(AIndex: integer): T; reintroduce;
procedure SetItems(AIndex: integer; const AValue: T); reintroduce;
public
property Items[i:integer]: T read GetItems write SetItems; default;
function Add(const AObject: T): integer; reintroduce;
end;
implementation
{ TGenericList<T> }
function TGenericList<T>.Add(const AObject: T): integer;
begin
Result := inherited Add(TtiObject(AObject));
end;
function TGenericList<T>.GetItems(AIndex: integer): T;
begin
Result := T(inherited GetItems(AIndex));
end;
procedure TGenericList<T>.SetItems(AIndex: integer; const AValue: T);
begin
inherited SetItems(AIndex, AValue);
end;
Upvotes: 1
Reputation: 1861
It looks like the problem is not me, but the compiler :).
In the end, I have hacked around it using the following methods
class function TtiGenericObjectList<T>.GenericAsObject(const Value): TObject;
begin
Result := TObject(Value);
end;
class function TtiGenericObjectList<T>.ObjectAsGeneric(const Value): T;
begin
Result := T(Value);
end;
used as follows
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
obj:= TtiObject(GenericAsObject(AObject));
result:= inherited Add(obj);
// replaces the following which gets overload errors
// result:= inherited Add(TtiObject(AObject));
end;
and
function TtiGenericObjectList<T>.GetItems(i: integer): T;
var obj: TtiObject;
begin
obj:= inherited GetItems(i);
result:= ObjectAsGeneric(obj);
// replaces the following which gets "Invalid typecast" errors
// result:= inherited Add(AObject);
end;
I will clean these up a bit and use them till the compiler gets fixed.
Upvotes: 2
Reputation: 84650
The Delphi 2009 compiler has a few very serious flaws in its generics implementation. It doesn't understand the implications of constraints nearly as well as it ought to, (Barry Kelly admitted this somewhere else on SO; I don't remember exactly where,) and cross-unit generics can cause very strange problems. Best bet is to handle this one on a case-by-case basis: If your code compiles, use it. If not, go back to a non-generic implementation until they get it fixed. Hopefully we'll see an update that fixes generics (and the Generics.Collections unit) sometime in the near future.
Upvotes: 2
Reputation: 53446
I have found some little issues with generics, which is not surprising for such a new feature. And you are trying to confuse the compiler real hard ;-).
Unfortunately I have no 2009 at hand here so I can't test, but I have some suggestions:
Have you looked for updates (and installed them)?
Have you tried using the as and is operators:
obj:= AObject as TtiObject;
Have you tried using an intermediate class:
TtiGenericList<T: TObject> = class(TtiObjectList)
protected
function GetItems(i:integer): T; reintroduce;
procedure SetItems(i:integer; const Value: T); reintroduce;
public
function Add(const AObject: T): integer; reintroduce;
property Items[i:integer]: T read GetItems write SetItems; default;
end;
Upvotes: 0