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>)

+3


source to share


2 answers


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.

+2


source


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;

      

0


source







All Articles