问题
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:
- Detect that the type is an array.
- Check that it has a single dimension.
- Check that the element type is as expected.
- 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