【发布时间】: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 做了太多的工作,它违背了目的。