Type X parameter must support Y interface
I have a setup like this:
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 structure allows me to build a sugar syntax like:
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 I am getting a compilation error saying that
E2514 The type parameter TBuilder must support interface 'IBuilder'.
This is probably due to a typed constraint T: IBuilder
present on the interface, although TBuilder supports IBuilder (via its ancestor).
Can anyone please guide me on how to get around this?
Although I cannot use TBuilder = class(TBuilder<IBuilder, TObject>)
source to share
It's impossible. You are trying to do this:
IBar = interface(IInterface) end;
TFoo<T : IBar> = class(TObject, IBar) end;
TBar = TFoo<TBar>;
What generates the error
E2086 Type 'TBar' not fully defined yet
Without any interface dependency, you can write this as
TBar = class(TFoo<TBar>) end;
makes it a true descendant, not just an alias. Typically this can resolve the type, but the interface dependency forces the compiler to ask the question: does it support TBar
IBar
?
If you think about it, it looks like:
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 are asking the compiler to solve the infinite recursion problem. He cannot do it.
source to share
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 usually use it like this:
var
MyButton: IBuilder<TButton>;
begin
MyButton:= TFactory<TButton>.New.Left(10).Top(5).Name('ABC');
If you use an interface, you should never work with a class, always interact with the interface exclusively. By moving the class definition into the implementation, you accomplish this. To compensate for the addition of a factory method to the interface.
In this case, it should be an entry, because you cannot (yet) have shared standalone methods.
class function TFactory<TOut>.New: IBuilder<TOut>;
begin
Result:= TBuilder<TOut>.Create;
end;
source to share