Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi XE: Can I call virtual constructors with parameters from a classtype-constrained generic type without resigning to hacks?

I'm trying to build a generic ancestor for composite controls. The initial idea looked something like this:

type
  TCompositeControl<TControl1: TControl; TControl2: TControl> = class(TWinControl)
  private
    FControl1,
    FControl2: TControl;
  public
    constructor Create(AOwner: TComponent); override; 
  end;

  TLabelAndEdit = TCompositeControl<TLabel, TEdit>; // simple example for illustration only

constructor TCompositeControl<TControl1,TControl2>.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FControl1 := TControl1.Create(Self);
  FControl2 := TControl2.Create(Self);
end;

As you might already be aware, this will trigger compiler error E2568: Can't create new instance without CONSTRUCTOR constraint in type parameter declaration. Adding the constructor constraint doesn't help however as it implies a parameter-less constructor.

Casting the templates to TControl makes the code compilable:

...
FControl1 := TControl(TControl1).Create(Self);
...

...but it results in an Access Violation at runtime.

One hack that would probably work is invoking the constructor via RTTI, but I would consider that a rather dirty solution.

Another hack that essentially works is using class type variables as intermediates:

type
  TControlClass = class of TControl;

constructor TCompositeControl<TControl1,TControl2>.Create(AOwner: TComponent);
var
  lCtrlClass1,
  lCtrlClass2: TControlClass;
begin
  inherited Create(AOwner);
  lCtrlClass1 := TControl1;
  FControl1 := lCtrlClass1.Create(Self);
  lCtrlClass2 := TControl2;
  FControl2 := lCtrlClass2.Create(Self);
end;

Is there a cleaner solution? Also, can somebody explain to me why the classtype-constraint does not suffice for invoking the virtual constructor on the type parameter directly?

like image 458
Oliver Giesen Avatar asked Mar 06 '12 15:03

Oliver Giesen


1 Answers

Your typecast is bad: TControl(TControl1).Create(Self). That tells the compiler that TControl1 is an instance of TControl, but we know that it's not an instance. It's a class reference. Type-cast it to the class-reference type instead:

FControl1 := TControlClass(TControl1).Create(Self);
like image 190
Rob Kennedy Avatar answered Oct 14 '22 23:10

Rob Kennedy