Discussion:
How to solve this generics design problem ? (access violations and crashes in Delphi XE, no warnings/errors)
(too old to reply)
Skybuck Flying
2011-09-20 12:40:57 UTC
Permalink
Hello,

This is my first serious consideration of using generics in real code, but
it seems to miss behave a lot, no warnings/errors giving, but it still
crashes like it's the end of the world ! ;) :)

The idea is to design a simply TBoundingBox which has a Min and a Max
property.

This property could either be a 2D or a 3D coordinate type, which for
convenience is a class to solve the "value property problem", and prevent
the need for pointers.

So that Min.X := 100; will automatically lead to a change of X and not a
useless change as described in the previous thread.

This this program also solves as a nice compiler test, currently the
compiler seems pretty shaky/flimsy ! ;)

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test generics (bug with Destroy? or design problem ? not error messages
giving in Delphi XE)

version 0.01 created on 20 september 2011 by Skybuck Flying

I would like to try and design generic code so a bounding box could
be re-used for 2D or 3D coordinates,

The example below tries to instantiate 3D coordinates, but all kinds
of bad things happen. Current code leads to access violation at .Min access.

If code is moved to seperate units, the .Destroy inside destructor fails.

How to solve the design problem ? And/Or are it delphi bugs ?

}

uses
SysUtils;

// generics interfaces
type
TCoordinate3D<ComponentType> = class
private

protected
mX : ComponentType;
mY : ComponentType;
mZ : ComponentType;
public
constructor Create;
destructor Destroy; override;

property X : ComponentType read mX write mX;
property Y : ComponentType read mY write mY;
property Z : ComponentType read mZ write mZ;
end;

type
TBoundingBox<TCoordinate : class, constructor> = class
private

protected
mMin : TCoordinate;
mMax : TCoordinate;
public
constructor Create;
destructor Destroy; override;

property Min : TCoordinate read mMin write mMin;
property Max : TCoordinate read mMax write mMax;
end;


// generics implementations

constructor TCoordinate3D<ComponentType>.Create;
begin
inherited Create;

end;

destructor TCoordinate3D<ComponentType>.Destroy;
begin

inherited Destroy;
end;

// ---

constructor TBoundingBox<TCoordinate>.Create;
begin
inherited create;

// perhaps problem here ??? no type passed to TCoordinate ?!?
mMin := TCoordinate.Create;
mMax := TCoordinate.Create;
end;

destructor TBoundingBox<TCoordinate>.Destroy;
begin
mMax.Destroy;
mMin.Destroy;

inherited Destroy;
end;


// instantiations
type
TWorldFloat = double;

type
TWorldCoordinate = TCoordinate3D<TWorldFloat>;

type
TWorldBox = TBoundingBox<TWorldCoordinate>;


procedure Main;
var
vWorldBox : TWorldBox;
begin
vWorldBox := TWorldBox.Create;

// access violation.
vWorldBox.Min.X := -1000000;
vWorldBox.Min.Y := -2000000;
vWorldBox.Min.Z := -3000000;

vWorldBox.Max.X := 4000000;
vWorldBox.Max.Y := 5000000;
vWorldBox.Max.Z := 6000000;

writeln( 'vWorldBox.Min.X: ', vWorldBox.Min.X );
writeln( 'vWorldBox.Min.Y: ', vWorldBox.Min.Y );
writeln( 'vWorldBox.Min.Z: ', vWorldBox.Min.Z );
writeln;
writeln( 'vWorldBox.Max.X: ', vWorldBox.Max.X );
writeln( 'vWorldBox.Max.Y: ', vWorldBox.Max.Y );
writeln( 'vWorldBox.Max.Z: ', vWorldBox.Max.Z );

vWorldBox.Free; // crash when put in seperate units.
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

// *** End of Test Program ***

Bye,
Skybuck.
Skybuck Flying
2011-09-20 13:44:04 UTC
Permalink
Hello,

Here is version 0.02 of the test program.

This program includes a 2d and a 3d version of the coordinate.

It also includes a 2d integer and 3d floating point instantiation.

It seems Delphi XE2 crashes as well.

The crashes seem somewhat random, so it might depend on existing garbage in
memory for a crash to occur.

So test multiple test to see the crash in action ?!?

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test generics (bug with Destroy? or design problem ? not error messages
giving in Delphi XE)

version 0.01 created on 20 september 2011 by Skybuck Flying

I would like to try and design generic code so a bounding box could
be re-used for 2D or 3D coordinates,

The example below tries to instantiate 3D coordinates, but all kinds
of bad things happen. Current code leads to access violation at .Min access.

If code is moved to seperate units, the .Destroy inside destructor fails.

How to solve the design problem ? And/Or are it delphi bugs ?

This seems like a delphi bug in xe ?!

}

{

version 0.02 created on 20 september 2011 by Skybuck Flying

Perhaps it are Delphi bugs.

Extended code below has been tried in Delphi XE2 and it still seems
to crash in the .Free.

The crashes seem to be somewhat random, so it might depend on
existing garbage in memory ?!?

}

uses
SysUtils;

// generics interfaces
type
TCoordinate2D<TComponent> = class
private

protected
mX : TComponent;
mY : TComponent;
public
constructor Create;
destructor Destroy; override;

property X : TComponent read mX write mX;
property Y : TComponent read mY write mY;
end;

type
TCoordinate3D<TComponent> = class
private

protected
mX : TComponent;
mY : TComponent;
mZ : TComponent;
public
constructor Create;
destructor Destroy; override;

property X : TComponent read mX write mX;
property Y : TComponent read mY write mY;
property Z : TComponent read mZ write mZ;
end;

type
TBoundingBox<TSpecificCoordinate : class, constructor> = class
private

protected
mMin : TSpecificCoordinate;
mMax : TSpecificCoordinate;
public
constructor Create;
destructor Destroy; override;

property Min : TSpecificCoordinate read mMin;
property Max : TSpecificCoordinate read mMax;
end;


// generics implementations

constructor TCoordinate2D<TComponent>.Create;
begin
inherited Create;

end;

destructor TCoordinate2D<TComponent>.Destroy;
begin

inherited Destroy;
end;

constructor TCoordinate3D<TComponent>.Create;
begin
inherited Create;

end;

destructor TCoordinate3D<TComponent>.Destroy;
begin

inherited Destroy;
end;

// ---

constructor TBoundingBox<TSpecificCoordinate>.Create;
begin
inherited create;

mMin := TSpecificCoordinate.Create;
mMax := TSpecificCoordinate.Create;
end;

destructor TBoundingBox<TSpecificCoordinate>.Destroy;
begin
mMax.Destroy;
mMin.Destroy;

inherited Destroy;
end;


// instantiations
// a simple example of a 3d double/floating point version of the bounding
box
type
TWorldFloat = double;

type
TWorldCoordinate = TCoordinate3D<TWorldFloat>;

type
TWorldBox = TBoundingBox<TWorldCoordinate>;

// a simple example of a 2d integer version of the bounding box
type
TScreenInteger = integer;

type
TScreenCoordinate = TCoordinate2D<TScreenInteger>;

type
TScreenBox = TBoundingBox<TScreenCoordinate>;

procedure Main;
var
vWorldBox : TWorldBox;
vScreenBox : TScreenBox;
begin
vWorldBox := TWorldBox.Create;

vWorldBox.Min.X := -1000000;
vWorldBox.Min.Y := -2000000;
vWorldBox.Min.Z := -3000000;

vWorldBox.Max.X := 4000000;
vWorldBox.Max.Y := 5000000;
vWorldBox.Max.Z := 6000000;

writeln( 'vWorldBox.Min.X: ', vWorldBox.Min.X );
writeln( 'vWorldBox.Min.Y: ', vWorldBox.Min.Y );
writeln( 'vWorldBox.Min.Z: ', vWorldBox.Min.Z );
writeln;
writeln( 'vWorldBox.Max.X: ', vWorldBox.Max.X );
writeln( 'vWorldBox.Max.Y: ', vWorldBox.Max.Y );
writeln( 'vWorldBox.Max.Z: ', vWorldBox.Max.Z );

vWorldBox.Free; // still crashes in Delphi XE2

vScreenBox := TScreenBox.Create;

vScreenBox.Min.X := 0;
vScreenBox.Min.Y := 0;

vScreenBox.Max.X := 320;
vScreenBox.Max.Y := 200;

writeln( 'vScreenBox.Min.X: ', vScreenBox.Min.X );
writeln( 'vScreenBox.Min.Y: ', vScreenBox.Min.Y );
writeln;
writeln( 'vScreenBox.Max.X: ', vScreenBox.Max.X );
writeln( 'vScreenBox.Max.Y: ', vScreenBox.Max.Y );

vScreenBox.Free;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

// *** End of Test Program ***

Bye,
Skybuck.
Skybuck Flying
2011-09-20 13:45:59 UTC
Permalink
(Also this code does not seem to compile in free pascal 2.4.4 for X64,
apperently generics not support in free pascal 2.4.2 ?)

It would be interesting to know if free pascal 2.6.0 or so is going to
support generics and how this example would compile ?!?

Bye,
Skybuck.

"Skybuck Flying" wrote in message news:7ac91$4e7898a1$5419acc3$***@cache2.tilbu1.nb.home.nl...

Hello,

Here is version 0.02 of the test program.

This program includes a 2d and a 3d version of the coordinate.

It also includes a 2d integer and 3d floating point instantiation.

It seems Delphi XE2 crashes as well.

The crashes seem somewhat random, so it might depend on existing garbage in
memory for a crash to occur.

So test multiple test to see the crash in action ?!?

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test generics (bug with Destroy? or design problem ? not error messages
giving in Delphi XE)

version 0.01 created on 20 september 2011 by Skybuck Flying

I would like to try and design generic code so a bounding box could
be re-used for 2D or 3D coordinates,

The example below tries to instantiate 3D coordinates, but all kinds
of bad things happen. Current code leads to access violation at .Min access.

If code is moved to seperate units, the .Destroy inside destructor fails.

How to solve the design problem ? And/Or are it delphi bugs ?

This seems like a delphi bug in xe ?!

}

{

version 0.02 created on 20 september 2011 by Skybuck Flying

Perhaps it are Delphi bugs.

Extended code below has been tried in Delphi XE2 and it still seems
to crash in the .Free.

The crashes seem to be somewhat random, so it might depend on
existing garbage in memory ?!?

}

uses
SysUtils;

// generics interfaces
type
TCoordinate2D<TComponent> = class
private

protected
mX : TComponent;
mY : TComponent;
public
constructor Create;
destructor Destroy; override;

property X : TComponent read mX write mX;
property Y : TComponent read mY write mY;
end;

type
TCoordinate3D<TComponent> = class
private

protected
mX : TComponent;
mY : TComponent;
mZ : TComponent;
public
constructor Create;
destructor Destroy; override;

property X : TComponent read mX write mX;
property Y : TComponent read mY write mY;
property Z : TComponent read mZ write mZ;
end;

type
TBoundingBox<TSpecificCoordinate : class, constructor> = class
private

protected
mMin : TSpecificCoordinate;
mMax : TSpecificCoordinate;
public
constructor Create;
destructor Destroy; override;

property Min : TSpecificCoordinate read mMin;
property Max : TSpecificCoordinate read mMax;
end;


// generics implementations

constructor TCoordinate2D<TComponent>.Create;
begin
inherited Create;

end;

destructor TCoordinate2D<TComponent>.Destroy;
begin

inherited Destroy;
end;

constructor TCoordinate3D<TComponent>.Create;
begin
inherited Create;

end;

destructor TCoordinate3D<TComponent>.Destroy;
begin

inherited Destroy;
end;

// ---

constructor TBoundingBox<TSpecificCoordinate>.Create;
begin
inherited create;

mMin := TSpecificCoordinate.Create;
mMax := TSpecificCoordinate.Create;
end;

destructor TBoundingBox<TSpecificCoordinate>.Destroy;
begin
mMax.Destroy;
mMin.Destroy;

inherited Destroy;
end;


// instantiations
// a simple example of a 3d double/floating point version of the bounding
box
type
TWorldFloat = double;

type
TWorldCoordinate = TCoordinate3D<TWorldFloat>;

type
TWorldBox = TBoundingBox<TWorldCoordinate>;

// a simple example of a 2d integer version of the bounding box
type
TScreenInteger = integer;

type
TScreenCoordinate = TCoordinate2D<TScreenInteger>;

type
TScreenBox = TBoundingBox<TScreenCoordinate>;

procedure Main;
var
vWorldBox : TWorldBox;
vScreenBox : TScreenBox;
begin
vWorldBox := TWorldBox.Create;

vWorldBox.Min.X := -1000000;
vWorldBox.Min.Y := -2000000;
vWorldBox.Min.Z := -3000000;

vWorldBox.Max.X := 4000000;
vWorldBox.Max.Y := 5000000;
vWorldBox.Max.Z := 6000000;

writeln( 'vWorldBox.Min.X: ', vWorldBox.Min.X );
writeln( 'vWorldBox.Min.Y: ', vWorldBox.Min.Y );
writeln( 'vWorldBox.Min.Z: ', vWorldBox.Min.Z );
writeln;
writeln( 'vWorldBox.Max.X: ', vWorldBox.Max.X );
writeln( 'vWorldBox.Max.Y: ', vWorldBox.Max.Y );
writeln( 'vWorldBox.Max.Z: ', vWorldBox.Max.Z );

vWorldBox.Free; // still crashes in Delphi XE2

vScreenBox := TScreenBox.Create;

vScreenBox.Min.X := 0;
vScreenBox.Min.Y := 0;

vScreenBox.Max.X := 320;
vScreenBox.Max.Y := 200;

writeln( 'vScreenBox.Min.X: ', vScreenBox.Min.X );
writeln( 'vScreenBox.Min.Y: ', vScreenBox.Min.Y );
writeln;
writeln( 'vScreenBox.Max.X: ', vScreenBox.Max.X );
writeln( 'vScreenBox.Max.Y: ', vScreenBox.Max.Y );

vScreenBox.Free;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

// *** End of Test Program ***

Bye,
Skybuck.
Skybuck Flying
2011-09-20 13:56:14 UTC
Permalink
Generics do seem supported in free pascal 2.4.4 for x64 but then it requires
a "generic" keyword in front of the class type/name.

I tried that too, but then it complains about "constructors" having to be
methods, so far free pascal's syntax seems to be a bit different.

"Skybuck Flying" wrote in message news:7ac91$4e7898a1$5419acc3$***@cache2.tilbu1.nb.home.nl...

Hello,

Here is version 0.02 of the test program.

This program includes a 2d and a 3d version of the coordinate.

It also includes a 2d integer and 3d floating point instantiation.

It seems Delphi XE2 crashes as well.

The crashes seem somewhat random, so it might depend on existing garbage in
memory for a crash to occur.

So test multiple test to see the crash in action ?!?

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test generics (bug with Destroy? or design problem ? not error messages
giving in Delphi XE)

version 0.01 created on 20 september 2011 by Skybuck Flying

I would like to try and design generic code so a bounding box could
be re-used for 2D or 3D coordinates,

The example below tries to instantiate 3D coordinates, but all kinds
of bad things happen. Current code leads to access violation at .Min access.

If code is moved to seperate units, the .Destroy inside destructor fails.

How to solve the design problem ? And/Or are it delphi bugs ?

This seems like a delphi bug in xe ?!

}

{

version 0.02 created on 20 september 2011 by Skybuck Flying

Perhaps it are Delphi bugs.

Extended code below has been tried in Delphi XE2 and it still seems
to crash in the .Free.

The crashes seem to be somewhat random, so it might depend on
existing garbage in memory ?!?

}

uses
SysUtils;

// generics interfaces
type
TCoordinate2D<TComponent> = class
private

protected
mX : TComponent;
mY : TComponent;
public
constructor Create;
destructor Destroy; override;

property X : TComponent read mX write mX;
property Y : TComponent read mY write mY;
end;

type
TCoordinate3D<TComponent> = class
private

protected
mX : TComponent;
mY : TComponent;
mZ : TComponent;
public
constructor Create;
destructor Destroy; override;

property X : TComponent read mX write mX;
property Y : TComponent read mY write mY;
property Z : TComponent read mZ write mZ;
end;

type
TBoundingBox<TSpecificCoordinate : class, constructor> = class
private

protected
mMin : TSpecificCoordinate;
mMax : TSpecificCoordinate;
public
constructor Create;
destructor Destroy; override;

property Min : TSpecificCoordinate read mMin;
property Max : TSpecificCoordinate read mMax;
end;


// generics implementations

constructor TCoordinate2D<TComponent>.Create;
begin
inherited Create;

end;

destructor TCoordinate2D<TComponent>.Destroy;
begin

inherited Destroy;
end;

constructor TCoordinate3D<TComponent>.Create;
begin
inherited Create;

end;

destructor TCoordinate3D<TComponent>.Destroy;
begin

inherited Destroy;
end;

// ---

constructor TBoundingBox<TSpecificCoordinate>.Create;
begin
inherited create;

mMin := TSpecificCoordinate.Create;
mMax := TSpecificCoordinate.Create;
end;

destructor TBoundingBox<TSpecificCoordinate>.Destroy;
begin
mMax.Destroy;
mMin.Destroy;

inherited Destroy;
end;


// instantiations
// a simple example of a 3d double/floating point version of the bounding
box
type
TWorldFloat = double;

type
TWorldCoordinate = TCoordinate3D<TWorldFloat>;

type
TWorldBox = TBoundingBox<TWorldCoordinate>;

// a simple example of a 2d integer version of the bounding box
type
TScreenInteger = integer;

type
TScreenCoordinate = TCoordinate2D<TScreenInteger>;

type
TScreenBox = TBoundingBox<TScreenCoordinate>;

procedure Main;
var
vWorldBox : TWorldBox;
vScreenBox : TScreenBox;
begin
vWorldBox := TWorldBox.Create;

vWorldBox.Min.X := -1000000;
vWorldBox.Min.Y := -2000000;
vWorldBox.Min.Z := -3000000;

vWorldBox.Max.X := 4000000;
vWorldBox.Max.Y := 5000000;
vWorldBox.Max.Z := 6000000;

writeln( 'vWorldBox.Min.X: ', vWorldBox.Min.X );
writeln( 'vWorldBox.Min.Y: ', vWorldBox.Min.Y );
writeln( 'vWorldBox.Min.Z: ', vWorldBox.Min.Z );
writeln;
writeln( 'vWorldBox.Max.X: ', vWorldBox.Max.X );
writeln( 'vWorldBox.Max.Y: ', vWorldBox.Max.Y );
writeln( 'vWorldBox.Max.Z: ', vWorldBox.Max.Z );

vWorldBox.Free; // still crashes in Delphi XE2

vScreenBox := TScreenBox.Create;

vScreenBox.Min.X := 0;
vScreenBox.Min.Y := 0;

vScreenBox.Max.X := 320;
vScreenBox.Max.Y := 200;

writeln( 'vScreenBox.Min.X: ', vScreenBox.Min.X );
writeln( 'vScreenBox.Min.Y: ', vScreenBox.Min.Y );
writeln;
writeln( 'vScreenBox.Max.X: ', vScreenBox.Max.X );
writeln( 'vScreenBox.Max.Y: ', vScreenBox.Max.Y );

vScreenBox.Free;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Loading...