4

I would like to declare a generic record like so:

type
  TMyDelegate<T: constraint> = record
  private
    fDelegate: T;
  public
    class operator Implicit(a: T): TMyDelegate;
    class operator Implicit(A: TMyDelegate: T);
  end;

I'd like to limit T to reference to procedure/function. (As much as possible).

I've tried this, but it does not compile:

program Project3;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type

  TProc1 = reference to procedure(a: Integer);
  TProc2 = reference to procedure(b: TObject);

  TTest<T: TProc1, TProc2> = record
  private
    fData: T;
  public
    class operator Implicit(a: T): TTest<T>;
    class operator Implicit(a: TTest<T>): T;
  end;

  { TTest<T> }

class operator TTest<T>.Implicit(a: T): TTest<T>;
begin
  Result.fData:= a;
end;

class operator TTest<T>.Implicit(a: TTest<T>): T;
begin
  Result:= a.fData;
end;

var
  Delegate1: TProc1;
  Delegate2: TProc2;

var
  MyTest1: TTest<TProc1>;  <<-- error
  MyTest2: TTest<TProc2>;

begin
  MyTest1:=
    procedure(a: Integer)
    begin
      WriteLn(IntToStr(a));
    end;
end.

This gives compile error:

[dcc32 Error] Project3.dpr(39): E2514 Type parameter 'T' must support interface 'TProc2'

Is there a way to constrain a generic type to (a list of) anonymous types?

Johan
  • 74,508
  • 24
  • 191
  • 319

3 Answers3

5

David's is the correct answer but as a work around something like this may help:

program Project51;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  TTest<T> = record
  type
    TProcT = reference to procedure(a: T);
  private
    fData: TProcT;
  public
    class operator Implicit(a: TProcT): TTest<T>;
    class operator Implicit(a: TTest<T>): TProcT;
  end;

  { TTest<T> }

class operator TTest<T>.Implicit(a: TProcT): TTest<T>;
begin
  Result.fData:= a;
end;

class operator TTest<T>.Implicit(a: TTest<T>): TProcT;
begin
  Result:= a.fData;
end;

var
  MyTest1: TTest<Integer>;
  MyTest2: TTest<TObject>;

begin
  MyTest1:=
    procedure(a: Integer)
    begin
      WriteLn(IntToStr(a));
    end;
  MyTest2:=
    procedure(a: TObject)
    begin
      WriteLn(a.ClassName);
    end;
end.
Graymatter
  • 6,529
  • 2
  • 30
  • 50
  • @GreyMatter, this does go a while to solving my problem. I can see a solution from there. – Johan Jul 17 '14 at 22:05
  • 1
    Given the fact that System.Sysutils declares `TProc = reference to procedure (Arg1: T);` you can just use that one instead of declaring it yourself. – Uwe Raabe Jul 17 '14 at 22:54
  • @UweRaabe, No I want to limit the delegate to TArray, not just T. I would futher like to limit the allowable values to T itself, but this can be done by limiting the IData interface or TData class. – Johan Jul 17 '14 at 23:02
3

There is no way to specify such a constraint. Possible constraints are:

  • Value type.
  • Class, derived from specific ancestor.
  • Interface, derived from specific ancestor.
  • Parameterless constructor.

This is covered in the documentation: http://docwiki.embarcadero.com/RADStudio/en/Constraints_in_Generics

What the documentation does not make clear is that reference procedures types count as interfaces. Which is why your generic type compiles, with that constraint. But this is never any use to you. Because reference procedure types have no inheritance. And so the only thing that can meet a specific reference procedure type constraint is something of that specific type.

In fact your type cannot be instantiated. That's because the constraint

T: TProc1, TProc2

specifies that T supports both of those reference procedure interfaces. And nothing can do that. Nothing can simultaneously support both TProc1 and TProc2.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I forgot that the constraints are AND'ed together, not OR'ed. – Johan Jul 17 '14 at 22:23
  • Reference procedure constraints are utterly useless in fact. – David Heffernan Jul 17 '14 at 22:24
  • You can create a class: THack = class(TInterfacedObject, TProc1, TProc2) that would meet the requirements. No idea what use it would serve, but I'm guessing you can get it to compile. – Johan Jul 18 '14 at 06:39
2

Thanks to GrayMatter and David I've come up with a solution to the problem.

The solution is to redefine the anonymous procedure to fit within the constraints.

The following functions are defined.

TA = reference to procedure(const &In, &Out: TArray<TOmniValue>);
TB = reference to procedure(const &In, &Out: TArray<IOmniBlockingCollection>);
TC = .....

The trick is to redefine the methods like so:

program Project3;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  IData<Tin, Tout> = interface
    ['{D2132F82-CAA9-4F90-83A9-9EFD6221ABE2}']
    function GetInput: TArray<TIn>;
    function GetOutput: TArray<Tout>;
  end;

  TData<TIn, TOut> = class(TInterfacedObject, IData<Tin, Tout>)
  private
    fInput: TArray<Tin>;
    fOutput: TArray<Tout>;
  public
    constructor Create(const input: TArray<TIn>; const output: TArray<TOut>);
    function GetInput: TArray<Tin>;
    function GetOutput: TArray<Tout>;
  end;

  TDelegate<Tin, Tout> = reference to procedure(const Data: IData<TIn, TOut>);

{ TSimpleData }

constructor TData<TIn, TOut>.Create(const input: TArray<TIn>;
  const output: TArray<TOut>);
begin
  finput:= input;
  foutput:= output;
end;

function TData<Tin, Tout>.GetInput: TArray<Tin>;
begin
  Result:= fInput;
end;

function TData<Tin, Tout>.GetOutput: TArray<TOut>;
begin
  Result:= fOutput;
end;

var
  IntegerDelegate: TDelegate<Integer, Integer>;
  input, output: TArray<Integer>;
  i: Integer;
  Data: TData<Integer, Integer>;

begin
  IntegerDelegate:= procedure(const Data: IData<Integer, Integer>)
  var
    i: Integer;
    input: TArray<Integer>;
  begin
    input:= Data.GetInput;
    for i:= 0 to High(input) do begin
      Data.GetOutput[i]:= input[i]+10;
    end;
  end;
  SetLength(input,10);
  SetLength(output, Length(input));
  for i:= Low(input) to High(input) do begin
    input[i]:= i;
  end;
  Data:= TData<Integer, Integer>.Create(input, output);
  IntegerDelegate(Data);
  for i in output do Writeln(i);
  Readln;
end.

I can now limit the delegate to the types allowed (more or less).

Johan
  • 74,508
  • 24
  • 191
  • 319