Sizing a virtual class using class members, which are arrays of common types

Below is a very simple bit of code that mimics the structure of a class in some code that I have (the form contains only one button attached to a click event). I use Delphi XE and XE II and I see unpleasant crashes when destroying objects in my production code on which this class is based. These crashes only occur if I allow the members of the array element in TMyClass to initialize the Clear () method.

Unfortunately, the crash is not repeated so far in this code example, but it imitates some very strange behavior with the size of the instance, which, I suspect, might be causing the problem.

If I put a breakpoint in the TMyClass.Clear function and look at the InstanceSize member of 1288 class reports. This is strange, since the size of the array element is actually 12kb. I can change the type provided from TRecord2 to Integer and get the same InstanceSize result. If I completely remove the array from the class, I get an instance of ~ 264 bytes in size, which appears above, for a class that contains only one record instance of 128 bytes. This indicates that the compiler allocated 1024 bytes to the array storage for member V (type TT) in TMyClass.

I suspect that the weird instance size causes bad things when I write 12kb of data to an object, which, according to the compiler, is 1kb.

unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} type TRecord = record A : Array[1..128] of byte; end; TRecord2 = packed record S : Single; T : TDateTime; end; TBase = class(TObject) public R : TRecord; end; TBase2<T> = class(TBase) public type TT = packed array [0..31, 0..31] of T; var V : TT; R2 : TRecord; end; TMyClass = class(TBase2<TRecord2>) public procedure Clear; end; procedure TForm1.Button1Click(Sender: TObject); var O : TMyClass; begin O := TMyClass.Create; O.Clear; O.Free; end; { TMyClass } procedure TMyClass.Clear; var i, j : integer; begin for i := 0 to 31 do for j := 0 to 31 do begin V[I, J].S := 0; V[I, J].T := 0; end; end; end. 
+8
generics delphi delphi-xe delphi-xe2
source share
1 answer

This is an error, as shown in this minimal replay:

 program InstanceSizeBug; {$APPTYPE CONSOLE} type TMyClass<T> = class V: array [0..255] of T; end; TMyClassSingle = class(TMyClass<Single>); begin Writeln(TMyClass<Single>.InstanceSize); Writeln(TMyClassSingle.InstanceSize); Readln; end. 

Output:

 1032 264 

Note that the same behavior can be observed in Delphi 2010, the only other version of Delphi that I have to relate.

Go through the debugger and you will _GetMem in _GetMem with the Size parameter equal to 264 , so obviously there is not enough memory.

And just in case there are doubts, this version does not work with AV.

 program InstanceSizeBug; {$APPTYPE CONSOLE} type TMyClass<T> = class V: array [1..256*256] of T; end; TMyClassSingle = class(TMyClass<Single>); begin TMyClassSingle.Create.V[256*256] := 0; end. 

I posted this to Quality Central, released # 100561 .

As a workaround, I suggest using a dynamic array that you assign using SetLength in the constructor. I rather imagine that it is the presence of a fixed-size array that makes the compiler incorrect.

+8
source share

All Articles