Fallo de Delphi XE8 en TList<T>, necesita una solución alternativa
generics delphi-xe8 (1)
Después de actualizar a XE8 algunos de nuestros proyectos comienzan a romper datos. Parece un error en la realización de TList.
program XE8Bug1;
{$APPTYPE CONSOLE}
uses
System.SysUtils, Generics.Collections;
type
TRecord = record
A: Integer;
B: Int64;
end;
var
FRecord: TRecord;
FList: TList<TRecord>;
begin
FList := TList<TRecord>.Create;
FRecord.A := 1;
FList.Insert(0, FRecord);
FRecord.A := 3;
FList.Insert(1, FRecord);
FRecord.A := 2;
FList.Insert(1, FRecord);
Writeln(IntToStr(FList[0].A) + IntToStr(FList[1].A) + IntToStr(FList[2].A));
end.
Este código imprime "123" en XE7 y antes (como debería ser), pero en XE8 imprime "120". Tal vez alguien sepa una solución rápida para esto?
Actualización: solución no oficial está here
Descubrí que ahora el TList<T>.Insert
método llamado TListHelper.InternalInsertX
depende del tamaño de los datos, en mi caso:
procedure TListHelper.InternalInsertN(AIndex: Integer; const Value);
var
ElemSize: Integer;
begin
CheckInsertRange(AIndex);
InternalGrowCheck(FCount + 1);
ElemSize := ElSize;
if AIndex <> FCount then
Move(PByte(FItems^)[AIndex * ElemSize], PByte(FItems^)[(AIndex * ElemSize) + 1], (FCount - AIndex) * ElemSize);
Move(Value, PByte(FItems^)[AIndex * ElemSize], ElemSize);
Inc(FCount);
FNotify(Value, cnAdded);
end;
Veo el problema en la primera llamada de Move
. El destino debe ser:
PByte(FItems^)[(AIndex + 1) * ElemSize]
no
PByte(FItems^)[(AIndex * ElemSize) + 1]
Aaargh!
Finalmente, he usado las unidades System.Generics.Defaults.pas y System.Generics.Collections.pas de Delphi XE7 en mis proyectos, y ahora todo funciona como esperaba.
Actualización: como veo, RTL no se ve afectado, ya que no se usa TList<T>.Insert
para T con SizeOf> 8 (¿o tal vez me pierdo algo?)