我可以将泛型限制(编译或运行时)作为char的数组[0..n]

时间:2015-12-07 10:53:35

标签: delphi generics delphi-xe2

我有一个程序,其中很多结构被定义为char和记录的静态数组(通常由char数组组成,但这并不重要)。

我正在尝试为这些结构创建一个通用接口,因此可以将它们传递给后端C DLL。

我能够使用<T: record>约束来处理所有类型的记录,但array[0..n] of char违反了“非可空值类型”规则。

我可以通过为不同的静态数组声明类型来使用无约束泛型(TMyArray = array[0..5] of char - 这很好,因为它已经存在于现有代码中),但我需要丢失<T: record>约束。由于我的代码不适用于类或动态数组,我希望能够将T限制为记录或静态数组char。

我能够有两个带有和不带record约束的构造函数。然后我可以通过使用旧式RTTI来测试无约束类型是一个数组:

var 
  l: PTypeInfo;
begin
  l := TypeInfo(T);
  assert(l.Kind = tkArray);
end;

但我不认为我可以检查包含的类型是否为char? 2010 RTTI抱怨T必须是一个班级,所以我认为这也不是一个好的。 我可以通过创建仅包含我的静态字符数组的记录来解决它,但这感觉有点软糖并且在代码中看起来很笨拙。

1 个答案:

答案 0 :(得分:3)

这是一个编译器缺陷。固定长度数组是不可为空的值类型。这个缺陷仍然存在于Delphi 10 Seattle中。该程序无法编译:

{$APPTYPE CONSOLE}

type
  TFoo = record
    class procedure Bar<T: record>(const arg: T); static;
  end;

class procedure TFoo.Bar<T>(const arg: T);
begin
end;

type
  TArr = array [0..0] of char;

var
  Arr: TArr;

begin
  TFoo.Bar<TArr>(Arr);
end.

错误是:

  

[dcc32错误] E2512类型参数&#39; T&#39;必须是不可为空的值类型

所以,我猜你必须使用RTTI进行运行时检查。你当然可以做到。该计划表明:

{$APPTYPE CONSOLE}

uses
  Rtti, TypInfo;

type
  TFoo = record
    class procedure Bar<T>(const arg: T); static;
  end;

class procedure TFoo.Bar<T>(const arg: T);
var
  TypInfo: PTypeInfo;
  ArrayTypeData: TArrayTypeData;
begin
  TypInfo := TypeInfo(T);
  if TypInfo.Kind = tkArray then begin
    ArrayTypeData := GetTypeData(TypInfo).ArrayData;
    Writeln(ord(ArrayTypeData.ElType^.Kind));
    Writeln(ArrayTypeData.Size);
    Writeln(ArrayTypeData.ElCount);
    Writeln(ArrayTypeData.DimCount);
  end;
end;

type
  TArr = array [1..32] of char;

var
  Arr: TArr;

begin
  TFoo.Bar<TArr>(Arr);
  Readln;
end.

输出结果为:

9
64
32
1

请注意ord(tkWChar) == 9。因此,这为您提供了执行以下操作的方法:

  1. 检测到该类型是数组。
  2. 检查它是否只有一个维度。
  3. 检查元素类型是否符合预期。
  4. 检查元素计数是否符合预期。
  5. 您需要检查类型是否符合要求的所有信息。