Can I restrict (compile or runtime) a generic to being an array [0..n] of char

情到浓时终转凉″ 提交于 2021-01-28 02:14:11

问题


I have a program with a lot of structures defined as static arrays of char and records (usually consisting of arrays of char, but that's not so important).

I am trying to create a generic interface for these structures, so they can be passed to a back-end C DLL.

I am able to handle all types of records by using the <T: record> constraint, but array[0..n] of char falls foul of the 'non-nullable value type' rule.

I can use unconstrained generics by declaring types for my different static arrays (TMyArray = array[0..5] of char - this is fine, as it's already there in existing code), but I need to lose my <T: record> constraint. As my code will not work with classes nor dynamic arrays I'd like to be able to restrict T to being either a record or static array of char.

I am able to have two constructors with and without the record constraint. I can then test that it the unconstrained type is an array by using old-style RTTI:

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

but I don't think I can check that the contained type is a char? 2010 RTTI complains that T must be a class, so I don't think that's a goer either. I can get around it somewhat by creating records containing only my static char array, but that feels a bit of a fudge and will look quite clumsy in code.


回答1:


This is a compiler defect. A fixed length array is a non-nullable value type. The defect is still present in Delphi 10 Seattle. This program fails to compile:

{$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.

The error is:

[dcc32 Error] E2512 Type parameter 'T' must be a non-nullable value type

So, I guess you will have to handle this with a runtime check using RTTI. Which you can certainly do. This program demonstrates:

{$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.

The output is:

9
64
32
1

Note that ord(tkWChar) == 9. So, this gives you the means to do the following:

  1. Detect that the type is an array.
  2. Check that it has a single dimension.
  3. Check that the element type is as expected.
  4. Check that the element count is as expected.

That's all the information you need to check that the type meets your requirements.



来源:https://stackoverflow.com/questions/34131842/can-i-restrict-compile-or-runtime-a-generic-to-being-an-array-0-n-of-char

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!