views:

149

answers:

3

Consider:

TTest <T : class, constructor> = class 
public
  function CreateMyObject : T;
end;
function TTest<T>.CreateMyObject : T;
var
  Obj : TObject;
begin
  Obj := T.Create; 
  Result := (Obj as T);
end;

Why isn't this possible? Compiler yields an "Operator not applicable to this type" error message for the as operator. T is constrained to be a class type, so this should work, shouldn't it?

Thanks for the help.

A: 

Not sure why it doesn't work. I assume there are still some quirks/bugs in the generics implementation.

But the following code works:

// Class definition
function CreateMyObject<T : class, constructor>  : T;


// Class implementation
function TForm1.CreateMyObject<T>: T;
var
  Obj : T;
begin
  Obj := T.Create;
end;
Gamecat
Yes, this works but unfortunately I can only create a TObject, since I use TClass.Create to create the object. It's for a factory class (see my other recent questions)
Smasher
Why don't you use a common ancestor with a virtual constructor? Yo can create a class of type and use that (and not TClass)?
Gamecat
Can you please explain in more detail? As I understand it this does not help because I want the return value to be of type T and not of type TCommonAncestor.
Smasher
I will answer this evening. Then I have time to test this. But I have met some dead ends using generics so prepare for the worst...
Gamecat
A: 

This is a known issue with the Delphi compiler. Please vote for it if you'd like to see it fixed. In the meantime, you can work around it by saying

Result := (Obj as TClass(T));
Mason Wheeler
Hi Mason! Thanks for the hint. Unfortunately your workaround does not compile for me ("'(' expected but ')' found")
Smasher
Ack! If that doesn't work for your version then MvdH's answer might be the only way to do it.
Mason Wheeler
+2  A: 

I ran into the same problem and solved it by adding a low-level pointer-copy method to the class as a workaround:

  TTest <T : class, constructor> = class
  private
    function _ToGeneric(AItem: TObject): T; inline; //inline, so it's a little faster
  public
    function CreateMyObject : T;
  end;

  function TTest<T>.CreateMyObject : T;
  var
    Obj : TObject;
  begin
    Obj := T.Create;
    Result := _ToGeneric(Obj);
  end;

  function TTest<T>._ToGeneric(AItem: TObject): T;
  begin
    System.Move(AItem,Result,SizeOf(AItem))
  end;
MvdH
+1 That seems to work! Nice workaround. I hope that will get fixed soon.
Smasher