Delphi何时在泛型内联函数中的const参数上调用_CopyRecord?

时间:2015-07-06 18:12:44

标签: delphi generics inline delphi-xe7 compiler-bug

当使用内联泛型类函数比较两个托管记录和const参数时,会在记录上调用CopyRecord。 这会导致记录中的指针发生变化,并且两个(先前)相等的记录不会测试相等。

然而..... 在创建一个简单的MVCE时,我无法重现该行为。

以下代码可以正常使用:

program RTLTestManagedRecords;
{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TCompare<T> = class
    class function Compare(const Left, Right: T): integer; static; inline;
  end;

  TTest<T> = class
  private
    class var F: TCompare<T>;
  public
    class function Fast(const Left, Right: T): integer; static;
    class procedure Test(const Left, Right: T; const message: string = ''); static;
  end;

  function BinaryCompare(const Left, Right; Size: NativeInt): integer; forward;

class function TCompare<T>.Compare(const Left, Right: T): integer;
begin
  if GetTypeKind(T) = tkRecord then case SizeOf(T) of
    0: Result:= 0;
    1: Result:= (PByte(@Left)^)- (PByte(@Right)^);
    2: Result:= (PWord(@Left)^)- (PWord(@Right)^);
    4: Result:= (integer(PCardinal(@Left)^> PCardinal(@Right)^)-
        integer(PCardinal(@Left)^< PCardinal(@Right)^));
    else Result:= BinaryCompare(Left, Right, SizeOf(T));
  end;
end;

{pointermath on}
function BinaryCompare(const Left, Right; Size: NativeInt): integer;
var
  i: integer;
  L,R: PByte;
begin
  L:=@Left;
  R:=@Right;
  for i:= 0 to Size - 1 do begin
    if L[i] <> R[i] then exit(L[i] - R[i]);
  end;
  Result:= 0;
end;
{$pointermath off}

type
  TManagedRec = record
    a: integer;
    b: string;
  end;

var
  L,R: TManagedRec;

{ TTest<T> }

class function TTest<T>.Fast(const Left, Right: T): integer;
begin
  Result:= F.Compare(Left, Right);
end;

class procedure TTest<T>.Test(const Left, Right: T; const message: string);
begin
  try
    WriteLn(Format(message,[TTest<T>.Fast(Left,Right)]));
  except
    WriteLn('Oops');
  end;
end;

begin
  L.a:= 1;
  R.a:= 2;
  L.b:= '7878787';
  R.b:= '7777777';
  TTest<TManagedRec>.Test(L,L,'Compare(L,L) = %d');
  WriteLn(Format('Compare(L,R) = %d',[TCompare<TManagedRec>.Compare(L,R)]));
  WriteLn(Format('Compare(R,L) = %d',[TCompare<TManagedRec>.Compare(R,L)]));
  WriteLn(Format('Compare(R,R) = %d',[TCompare<TManagedRec>.Compare(R,R)]));
  WriteLn(Format('Compare(L,L) = %d',[TCompare<TManagedRec>.Compare(L,L)]));
  ReadLn;
end.

当我在更复杂的fastdefaults代码中运行它时,它会失败,因为CopyRecord被调用,即使参数被声明为const。 将它们声明为const [ref]并不能解决问题。 删除内联修复了它,但这消除了比较功能的许多好处。

问题
是否有人能够改变/扩展上面的MVCE,以便它就像更大的例子那样轰炸?&#34;

我试图了解问题的实际触发器是什么。

请不要评论比较托管记录的愚蠢行为
我知道Delphi在运行时不会在相同的字符串上进行字符串折叠 请不要建议解决问题的方法。我已经知道了解决方法。

TCompare<T> = class
        class function Compare(const Left, Right: T): integer; static; 

请注意,TCompare<T>类是通用的,因此它应该能够比较任何内容。包括托管记录。编译器应始终遵守const和。{ TCompare<T>.Compare(ManagedRec1, ManagedRec1)应始终返回0

我理解这一点 TCompare<T>.Compare(AManagedRec, SameManagedRec)并不总是返回0 我很好。

我不熟悉的是Delphi在const参数上调用_CopyRecord

0 个答案:

没有答案