【问题标题】:Detect concrete type of generic class at compile type在编译类型检测泛型类的具体类型
【发布时间】:2015-11-24 03:03:46
【问题描述】:

跟进这个问题:Conditional behaviour based on concrete type for generic class

我知道您可以使用 RTTI 检测泛型变量的类型。但是,我想更进一步。有没有办法在编译时检测类型。

我有以下代码:

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;

如果树将字符串作为其 K 或 V 类型,则指针需要归零。 move 破坏了 COW 机制,2 个字符串将具有新值。

上述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

因为可以在编译时解析 SizeOf,编译器可以看到测试总是解析为 true(或 false)并将消除它。

但是
上面的代码只需要针对引用计数类型运行,即:接口、字符串和 dynarrays。
现在它也适用于整数、对象等。

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

有没有办法缩小上述测试范围,使测试表达式在编译时仍然可以解析,从而编译成与上面相同的代码但排除更多类型?

我已经查看了内部例程,但我没有看到任何可以帮助我的东西:http://docwiki.embarcadero.com/RADStudio/XE4/en/Delphi_Intrinsic_Routines

【问题讨论】:

  • Initialize 几乎完成了这项工作,它只被托管类型调用,但是.... Initialize 做了太多的工作,它违背了目的。

标签: delphi generics


【解决方案1】:

在编译时无法区分不受约束的泛型类型。没有在编译时评估的运算符可以区分托管类型和非托管类型。

更新

自 XE7 以来,有一个新的内在 IsManagedType 旨在满足您的需求。到目前为止,这种内在的东西还没有被记录下来。也许最好的参考在这里:http://delphisorcery.blogspot.co.uk/2014/10/new-language-feature-in-xe7.html?m=1

【讨论】:

  • 无赖,问题是,编译器知道 K 和 V 是什么,为什么它不让我在编译时访问该信息。一个简单的if K is string 就可以完成这项工作。
  • 不知道。设计者没有包含任何超出 sizeof 的特征机制。
  • 在 XE7 中,我们有新的内在 IsManagedType,那不就行了吗?
  • 是的,我想是的。我认为这个答案早于 XE7。
【解决方案2】:

从 XE7 开始,可以使用(未​​记录的)内部函数 IsManagedType

像这样:

if IsManagedType(K) then ...

一种在 XE7 之前执行此操作的方法,通过使用 TypeInfo 内在函数的(也未记录的)副作用。
如果您将一个 TypeInfo 与另一个 TypeInfo 进行比较,则将在编译时评估结果。

因此:

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

因此,与所有预期的托管类型进行比较的测试就可以完成这项工作。
显然,如果使用的记录本身包含托管类型,则 TypeInfo 将无济于事,但 IsManagedType 会。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多