Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What generic constraint do I use for an anonymous method type?

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?

like image 923
Johan Avatar asked Dec 25 '22 06:12

Johan


2 Answers

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.
like image 56
Graymatter Avatar answered Dec 28 '22 23:12

Graymatter


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.

like image 43
David Heffernan Avatar answered Dec 29 '22 00:12

David Heffernan