Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler reject the declaration of a 2D generic array?

I'd like to declare a type like this:

type
  TDynMatrix<T> = TArray<TArray<T>>;

The compiler rejects this with:

[dcc32 Error] E2508 Type parameters not allowed on this type

I wondered whether the issue was related to the nesting of generics. But it seems not:

type
  TDynArray<T> = TArray<T>;//pointless type I know, but for the sake of the Q

also results in the same compiler error.

The documentation for the compiler error left me knowing perhaps even less than I knew before I read it:

E2508 type parameters not allowed on this type (Delphi)

When using class references, you cannot use generic classes directly. You need to use a wrapper class to be able to use generics.

program E2508;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TMyClass = class
  end;
  TMyClassClass<T> = class of TMyClass;

begin
   Writeln('FAIL - E2508 type parameters not allowed on this type');
end.

Can anyone explain why I cannot declare generic types in this way?

like image 462
David Heffernan Avatar asked Feb 19 '14 19:02

David Heffernan


3 Answers

Your code is invalid because you cannot re-declare a generic type as open generic type.

I would declare this as:

type
  TDynMatrix<T> = array of TArray<T>;

This way you still have compatibility that you probably need: the elements of one dimension to a TArray<T>.

So then you can write

var
  matrix: TDynMatrix<Integer>;
  values: TArray<Integer>;
....
SetLength(matrix, 2, 2);
values := matrix[0];
values := Copy(matrix[1]);

etc.

like image 172
Stefan Glienke Avatar answered Nov 13 '22 11:11

Stefan Glienke


Sorry no reasons, but this one is not limited to TArray,

Using the following type definitions:

TGenClass<T> = class fx: T; end;
TGenArray<T> = array of T;
TGenRecord<T> = record fx: T; end;
TGenProc<T> = procedure(const A: T);

I have tried all combinations:

TGenClassClass<T> = TGenClass<TGenClass<T>>;
TGenClassArray<T> = TGenClass<TGenArray<T>>;
TGenClassRecord<T> = TGenClass<TGenClass<T>>;
TGenClassProc<T> = TGenClass<TGenClass<T>>;

TGenArrayClass<T> = TGenArray<TGenClass<T>>;
TGenArrayArray<T> = TGenArray<TGenArray<T>>;
TGenArrayRecord<T> = TGenArray<TGenClass<T>>;
TGenArrayProc<T> = TGenArray<TGenClass<T>>;

TGenRecordClass<T> = TGenRecord<TGenClass<T>>;
TGenRecordArray<T> = TGenRecord<TGenArray<T>>;
TGenRecordRecord<T> = TGenRecord<TGenClass<T>>;
TGenRecordProc<T> = TGenRecord<TGenClass<T>>;

TGenProcClass<T> = TGenProc<TGenClass<T>>;
TGenProcArray<T> = TGenProc<TGenArray<T>>;
TGenProcRecord<T> = TGenProc<TGenClass<T>>;
TGenProcProc<T> = TGenClass<TGenProc<T>>;

They all fail.

You can declare types with complex type expressions, if those types are not generic themself:

TClassClass = TGenClass<TGenClass<Integer>>;
TClassArray = TGenClass<TGenArray<Integer>>;
TClassRecord = TGenClass<TGenClass<Integer>>;
TClassProc = TGenClass<TGenClass<Integer>>;

TArrayClass = TGenArray<TGenClass<Integer>>;
TArrayArray = TGenArray<TGenArray<Integer>>;
TArrayRecord = TGenArray<TGenClass<Integer>>;
TArrayProc = TGenArray<TGenClass<Integer>>;

TRecordClass = TGenRecord<TGenClass<Integer>>;
TRecordArray = TGenRecord<TGenArray<Integer>>;
TRecordRecord = TGenRecord<TGenClass<Integer>>;
TRecordProc = TGenRecord<TGenClass<Integer>>;

TProcClass = TGenProc<TGenClass<Integer>>;
TProcArray = TGenProc<TGenArray<Integer>>;
TProcRecord = TGenProc<TGenClass<Integer>>;
TProcProc = TGenClass<TGenProc<Integer>>;

There is a circumvention. You can declare the type within a class with a type parameter.

type
  TTheClass<T> = class
  type
    TGenClassClass = TGenClass<TGenClass<T>>;
  end;

So you can use TTheClass<T>.TGenClassClass as a type.

like image 44
Toon Krijthe Avatar answered Nov 13 '22 13:11

Toon Krijthe


FWIW, inspired by the various comments and answers, this is the code that I believe is the most effective:

type
  TDynMatrix<T> = array of TArray<T>;
  TDynMatrix = class
  public
    class function New<T>(const Source: array of TArray<T>): TDynMatrix<T>; static;
  end;

class function TDynMatrix.New<T>(const Source: array of TArray<T>): TDynMatrix<T>;
var
  i: Integer;
begin
  SetLength(Result, Length(Source));
  for i := 0 to high(Result) do begin
    Result[i] := Copy(Source[i]);
  end;
end;

This allows be to declared variables like this:

var
  RealM: TDynMatrix<Real>;
  ComplexM: TDynMatrix<TComplex>;

And make new instances like this:

RealM := TDynMatrix.New<Real>([TArray<Real>.Create(...)]);
ComplexM := TDynMatrix.New<TComplex>([TArray<TComplex>.Create(...)]);

Now, if only the compiler's generic inference capabilities were a little better.

like image 1
David Heffernan Avatar answered Nov 13 '22 12:11

David Heffernan