简体   繁体   English

在编译类型时检测泛型类的具体类型

[英]Detect concrete type of generic class at compile type

Followup on this question: Conditional behaviour based on concrete type for generic class 关于这个问题的跟进: 泛型类基于具体类型的条件行为

I know you can detect the type of a generic variable using RTTI. 我知道您可以使用RTTI检测通用变量的类型。 However I would like to go one step further. 但是,我想更进一步。 Is there a way to detect the type at compile time. 有没有一种方法可以在编译时检测类型。

I have the following code: 我有以下代码:

function TBPlusTree<K, V>.TLeaf.AddKey(const Key: K; const Value: V): boolean;
var
  i: integer;
begin
  i:= 0;
  while i < fCount do begin
    if Key = fKeys[i] then exit(false); // Key already exists
    //Todo: replace with binary search
    if Key < fKeys[i] then begin
      // Make space for the key
      Move(fKeys[i], fKeys[i+1], (fCount - i) * SizeOf(TKey));
      // If the key is a String or DynArray zero out the pointer
      if (SizeOf(K) = SizeOf(NativeInt)) then NativeInt((@fKeys[i])^):= 0;
      // If the value is a String or DynArray zero out the pointer
      Move(fValues[i], fValues[i+1], (fCount - i) * SizeOf(V));
      if (SizeOf(V) = SizeOf(NativeInt)) then NativeInt((@fValues[i])^):= 0;
      break;
    end;
    Inc(i);
  end; { while }
  Inc(fCount);
  //ClearKey(fKeys[i]);
  fKeys[i]:= Key;
  // if ValueType = tkUString then UniqueString(PString(@fValues[i])^);
  fValues[i]:= Value;
  Result:= true;
end;

If the tree hold a string as its K or V type then the pointer needs to be zeroed. 如果树包含一个字符串作为其K或V类型,则指针需要归零。 The move defeats the COW mechanism and the 2 strings will have the new value. move击败了COW机制,并且2根弦将具有新值。

The nice thing about the above if then is that the compiler eliminates the test. if then ,上面的if then是编译器消除了测试。

BPlusTrees.pas.454: if (SizeOf(K) = SizeOf(NativeInt)) then NativeInt((@fKeys[i])^):= 0;
0056BBC4 8B4610           mov eax,[esi+$10]  //Test is eliminated by compiler.
0056BBC7 8D0498           lea eax,[eax+ebx*4]
0056BBCA 33D2             xor edx,edx
0056BBCC 8910             mov [eax],edx

Because the SizeOf can be resolved at compile time the compiler can see that the test always resolves to true (or false) and will eliminate it. 因为SizeOf可以在编译时解析,所以编译器可以看到测试始终解析为true (或false)并将其消除。

However 然而
The above code needs to only run for reference counted types ie: interfaces, strings and dynarrays. 上面的代码只需要为引用计数类型运行,即:接口,字符串和dynarrays。
Right now it also runs for integers, objects etc. 现在它还可以运行整数,对象等。

The problem with TypeInfo is that it generates checking code which takes more time than the actual code I'm trying to avoid. TypeInfo的问题在于,它生成的检查代码比我要避免的实际代码花费更多的时间。

BPlusTrees.pas.454: if (SizeOf(K) = SizeOf(NativeInt)) and
          (PTypeInfo(TypeInfo(K))^.Kind <> tkInteger) 
          then NativeInt((@fKeys[i])^):= 0;
0056BBC4 A19C104000       mov eax,[$0040109c]
0056BBC9 803801           cmp byte ptr [eax],$01
0056BBCC 740A             jz $0056bbd8
0056BBCE 8B4610           mov eax,[esi+$10]
0056BBD1 8D0498           lea eax,[eax+ebx*4]
0056BBD4 33D2             xor edx,edx
0056BBD6 8910             mov [eax],edx

Is there a way to narrow down the above test so that the test expression can still be resolved at compile time so that it compiles to the same code as above but excludes more types? 有没有办法缩小上述测试的范围,以便测试表达式仍可以在编译时解析,以便编译为与上述相同的代码,但排除更多类型?

I've had a look at the intrinsic routines, but I see nothing there that can help me: http://docwiki.embarcadero.com/RADStudio/XE4/en/Delphi_Intrinsic_Routines 我看过内部例程,但没有任何东西可以帮助我: http : //docwiki.embarcadero.com/RADStudio/XE4/en/Delphi_Intrinsic_Routines

There's no way to distinguish, at compile time, unconstrained generic types. 无法在编译时区分不受约束的泛型类型。 There is no operator that is evaluated at compile time that can tell a managed type from an unmanaged type. 没有在编译时求值的运算符可以区分非托管类型的托管类型。

Update 更新资料

Since XE7, there is a new intrinsic IsManagedType that is designed to meet your needs. 从XE7开始,有一个新的内部IsManagedType可以满足您的需求。 This intrinsic is, as yet, undocumented. 到目前为止,此内在函数尚未记录。 Perhaps the best reference is here: http://delphisorcery.blogspot.co.uk/2014/10/new-language-feature-in-xe7.html?m=1 也许最好的参考在这里: http//delphisorcery.blogspot.co.uk/2014/10/new-language-feature-in-xe7.html?m = 1

From XE7 onwards the (undocumented) intrinsic function IsManagedType can be used. 从XE7开始,可以使用(未​​记录)内部函数IsManagedType

Like so: 像这样:

if IsManagedType(K) then ...

The is a way to do this pre-XE7, by using an (also undocumented) side effect of the TypeInfo intrinsic function. 通过使用TypeInfo内在函数的副作用(也是未记录的)在XE7之前执行此操作的方法。
If you compare one TypeInfo against another TypeInfo the result will be evaluated at compile time. 如果将一个TypeInfo与另一个TypeInfo进行比较,则将在编译时评估结果。

Thus: 从而:

if TypeInfo(K) = TypeInfo(UnicodeString) then .... //resolves at compile type
if (PTypeInfo(TypeInfo(K))^.Kind <> tkUString) //produces lots of code.

so a test to compare against all expected managed types will do the job. 因此,将测试与所有预期的托管类型进行比较就可以了。
Obviously if a record is used that itself contains a managed type then TypeInfo will not help, but IsManagedType will. 显然,如果使用的记录本身包含托管类型,则TypeInfo将无济于事,但IsManagedType将提供帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM