I have a setup like so :
IBuilder = interface(IInvokable)
end;
IBuilder<T: IBuilder; TOut : TWinControl> = interface(IInvokable)
end;
TBuilder<T: IBuilder; TOut : TWinControl> = class(TInterfacedObject, IBuilder, IBuilder<T, TOut>)
end;
TBuilder = class(TBuilder<TBuilder, TWinControl>)
end;
This kind of structure allows me to build a sugar syntax like so :
TBuilder<T : IBuilder; TOut : TWinControl> = class(TInterfacedObject, IBuilder, IBuilder<T, TOut>)
function Output : TOut;
function Name(aName : string) : T;
function Left(aLeft : Integer) : T;
function Top(aTop : Integer) : T;
end;
// ... later
TBuilder.Create().Left(10).Top(5).Name('ABC'); // Nice one liner
The problem is that I get a compilation error, saying that
E2514 The type parameter TBuilder must support interface 'IBuilder'.
This is probably due to the typed constraint T: IBuilder
present on the interface, even though TBuilder does support IBuilder (trough it's ancestor).
Can anyone please direct me on how to get around this?
Though, I cannot use TBuilder = class(TBuilder<IBuilder, TObject>)
This can't be done. You're essentially trying to do this :
IBar = interface(IInterface) end;
TFoo<T : IBar> = class(TObject, IBar) end;
TBar = TFoo<TBar>;
Which generates error
E2086 Type 'TBar' is not yet completely defined
Without the interface dependence you can write this as
TBar = class(TFoo<TBar>) end;
making it a true descendent and not just an alias. This could normally resolve the type, but the interface dependence is forcing the compiler to ask the question : Does TBar
support IBar
?
If you think about it, this works out as :
TBar = TFoo<TBar> {TBar support IBar?}
|
TBar = TFoo<TBar>... {ok, TBar support IBar?}
|
TBar = TFoo<TBar> {ok, TBar support IBar?}
|
{...turtles all the way down}
You're asking the compiler to solve an infinite recursion problem. It cannot do this.
You can fix this by changing the return type of your methods, and excluding the recursive type parameter.
interface
type
//IBuilder = interface(IInvokable)
//end; //I don't think you need this
IBuilder<TOut : TWinControl> = interface(IInvokable)
function Output : TOut;
function Name(const aName : string) : IBuilder<TOut>;
function Left(aLeft : Integer) : IBuilder<TOut>;
function Top(aTop : Integer) : IBuilder<TOut>;
end;
TFactory<TOut: TWinControl> = record
class function New: IBuilder<TOut>; static;
end;
implementation
type
//Put the actual class in the implementation
TBuilder<TOut : TWinControl> = class(TInterfacedObject, IBuilder<TOut>)
//see interface
end;
You normally use this like so:
var
MyButton: IBuilder<TButton>;
begin
MyButton:= TFactory<TButton>.New.Left(10).Top(5).Name('ABC');
If you're using interface then you should never work with the class, always interact with the interface exclusively. By moving the class definition in the implementation you enforce this. To compensate you add a factory method in the interface.
In this case it has to be a record, because you cannot (yet) have generic stand-alone methods.
class function TFactory<TOut>.New: IBuilder<TOut>;
begin
Result:= TBuilder<TOut>.Create;
end;