Do generic instantiations in multiple units bloat the executable? - delphi

This Embarcadero article discussing memory issues for the XE7 IDE contains the following:
Be aware of the “Growth by Generics”
Another scenario that might depend on your application code and cause an increase of the memory used by the compiler and the debugger relates with the way generic data types are used. The way the Object Pascal compiler works can cause the generation of many different types based on the same generic definition, at times even totally identical types that are compiled in different modules. While we won’t certainly suggest removing generics, quite the contrary, there are a few options to consider:
Try to avoid circular unit references for units defining core generic types
Define and use the same concrete type definitions when possible
If possible, refactor generics to share code in base classes, from which a generic class inherits
The last item I understand. The first two I am less clear on.
Do these issues affect only IDE performance, or is there an impact on the size of the compiled code?
For instance, considering the second item, if I declare TList<Integer> in two separate units, will I get two separate chunks of code in each of those units in my executable? I certainly hope not!

Point 2. This refers to instantiating same generic type where possible. For instance using TList<Integer> in all places instead of having two generic types TList<Integer> and TList<SmallInt>.
Declaring and using TList<Integer> in several units will only include single copy of TList<Integer> in exe file. Also, declaring TIntegerList = TList<Integer> will result with same.
Generic bloat people are referring to relates to having complete TList<T> copy for each specific type you use even though underlying generated code is the same.
For instance: TList<TObject> and TList<TPersistent> will include two separate copies of TList<T> even though generated code could be folded to single one.
That moves us to Point 3. where using base class for common class code and then using generic classes on top of that to get type safety, can save you memory both during compilation and in exe file.
For example, building generic class on top of non generic TObjectList will only include thin generic layer for each specific type instead of complete TObjectList functionality. Reported as QC 108966
TXObjectList<T: class, constructor> = class(TObjectList)
protected
function GetItem(index: Integer): T;
procedure SetItem(index: Integer; const Value: T);
public
function Add: T;
property Items[index: Integer]: T read GetItem write SetItem; default;
end;
function TXObjectList<T>.GetItem(index: Integer): T;
begin
Result := T( inherited GetItem(index));
end;
procedure TXObjectList<T>.SetItem(index: Integer; const Value: T);
begin
inherited SetItem(index, Value);
end;
function TXObjectList<T>.Add: T;
begin
Result := T.Create;
inherited Add(Result);
end;

The code bloat they are talking about in the article (as it is about the out of memory issue in the IDE) is related to the generated DCUs and all the meta information that is held in the IDE. Every DCU contains all the used generics. Only when compiling your binary the linker will remove duplicates.
That means if you have Unit1.pas and Unit2.pas and both are using TList<Integer> both Unit1.dcu and Unit2.dcu have the binary code for TList<Integer> compiled in.
If you declare TIntegerList = TList<Integer> in Unit3 and use that in Unit1 and Unit2 you might think this would only include the compiled TList<Integer> in Unit3.dcu but not in the other two. But unfortunately that is not the case.

Related

Is it possible to use a library just for design time in delphi?

I am trying to write a component which is loading 3D objects from obj files.
I am using ToolsAPI library for GetActiveProject.FileName. I added designide.dcp to Requiers part in the bpl. I registered my object and in design when I put an instance of this object on a TViewPort3D which I put before everything is OK and I can see the object from the obj file is loaded in the scene, but when I try to compile the project I get an error that says ToolsAPI.dcu not found.
The procedure that I use for loading the obj file is (Type of Model variable is TModel3D) :
procedure TMyObject.LoadModel(fileName: string);
begin
if(csDesigning in ComponentState)then
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;
This procedure is used in constructor as follow (TMyObject inherited from TDummy):
constructor TMyObject.Create(AOwner:TComponent)
begin
inherited;
Model:=TModel3D.Create(Self);
Model.Parent:=Self;
LoadModel('Object1.obj');
end;
Is there anyway to prevent using the ToolsAPI library when the host project for the component is about to compile?
I just thinking about something like directives as follow.
{$IFDEF DESIGNTIME}
uses ToolsAPI;
{$ENDIF}
But is it possible to do such a thing?
It sounds as though you are trying to compile the design time code into a run time project. Either a run time package, or an executable. That's not allowed. You simply cannot compile any of the ToolsAPI units into a project that is not a design time package.
You can certainly use conditional compilation to exclude the ToolsAPI units, but you will have to define your own conditional define. There is no built-in conditional that will serve your needs.
But using conditional compilation is probably not the best solution. Typically you would separate the code that used Tools API into distinct units, and only include those units in the design time projects.
So the code for your component would be split into, say, two units. The first unit, uMyComp.pas, say, contains the bulk of the code. This unit declares the component and provides its implementation. Nothing in uMyComp.pas makes any reference to ToolsAPI. The second unit, uMyCompReg.pas say, performs the component registration and any other tasks that require the ToolsAPI. There is a dependency between these units in that uMyCompReg.pas uses uMyComp.pas. Then your design time package will include both units, and any other projects that are not design time will include only uMyComp.pas.
You could achieve the same effect using conditionals. The design time project would define a conditional to indicate that this was design time. So the project settings might include a definition of a conditional named DESIGNTIME. Then all the code for your component would reside in a unit named uMyComp.pas, say. Any code related to design time would be conditional on DESIGNTIME. And any other projects that included uMyComp.pas would not have DESIGNTIME defined and so would omit the design time only code.
Whilst this is possible it is not, in my view, the best way to solve the problem. Indeed if you look around the wealth of open source examples of component development I'd be surprised if you found any that handled the separation of design time code from run time code using conditionals.
How would you separate the ToolsAPI code into a design time unit? Here's the problem method:
procedure TMyObject.LoadModel(fileName: string);
begin
if csDesigning in ComponentState then
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;
First of all, let's look at the commonality of this code. The first think to observe is that the outsides of the call to LoadFromFile are the same. Only in the middle, the choice of directory, is there variation. So let's write it like this:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if csDesigning in ComponentState then
ModelDir := ExtractFilePath(GetActiveProject.FileName)
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;
The problem for you is how to move GetActiveProject.FileName into the design time code. You need to use dependency injection (DI) to do this. Allow some other party to supply the logic. You need to make TMyObject ignorant of this particular detail. You could use a DI framework for this, but that's perhaps a little heavyweight just for this one task. So instead let's declare a class variable that holds a function pointer:
type
TMyObject = class(...)
...
public
class var GetModelDir: TFunc<string>;
end;
This function point allows other parties, external to the class, to specify how the model directory is located. Now LoadModel becomes:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if Assigned(GetModelDir) then
ModelDir := GetModelDir()
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;
At this point, your code can now be used outside of a design time package. The next step is to add code to specify GetModelDir at design time. This code goes in the design time only unit that also registers the component. The obvious place for the code is in the initialization section of that unit. It looks like this:
initialization
TMyObject.GetModelDir :=
function: string
begin
Result := GetActiveProject.FileName;
end;
I've used anonymous methods here, but you could equally use method of object, or plain old functional types, depending on your Delphi version.
Yes, but preferably not with conditional defines as this would create far more complications and restrictions than it's worth.
You need to separate your code into different units according to whether it's design-time code or run-time code.
E.g. For a single component, the bulk of the (with no ToolsAPI dependency) goes into one unit.
A second unit performs component registration and perhaps provides custom design-time editors for the component.
The second unit uses the first and you have a clean separation without conditional defines.
You then create 2 separate packages: design-time and run-time.
The design time package will have a dependency on the ToolsAPI.
Make sure that none of the run-time units use any of the design-time units.
If any design-time units use run-time units (very likely) then the design-time package will require the run-time package.
With the above package structure, your application that uses your new components should only have dependencies on the run-time units.

how to define classes inside classes in delphi?

I am in the progress of trying to learn a few new tricks in order to better organize some of the sourcecode in my units in Delphi.
I have noticed that some functions or methods I access appear to be classes inside classes, but I have not yet been successful in making a working class inside a class, although it compiles fine, I still get error messages when executing the code.
What I would like to do is to make kind of a tree with functions, procedures and values to set or get. I would be grateful if somebody could help me out a little bit with an example.
I have today some classes that are Types.
I then assign the types to a variable:
something=TSomething
and Then for something to happen I write "something.action".
My aim is to go further, and define sub-functions or/and sub-procedures.
Lets say I have three or four classes. TSnippet, TAction1, TAction2, TSubAction1, Etc.
I would like to use or assign these to a single variable and use them like:
Snippet.Action1.SubAction1.Organize(param1,param2);
Snippet.Action2.SubAction2.Returns='SomeString';
Snippet.Action1.SubAction1.SomeProcedure;
Is anybody able to help me with a useful example as in how to write code for this approach to work?
And also.. does anybydy know how such an implementation of code will affect CPYCycles needed in order to execute code versus the old fashioned method of having thousands of procedures with all different names, but more direct access (it feels like more direct access).
As of my first text was maybe a bit unclear, this follows.
I would like to make use of the editors automatic suggestions of procedures/functions available in order to simplify programming a little bit.
I started to make a Class for this, and it works great.
Consider a classname "Data". What can we do with data? We can Edit, Add, Delete, Save and Load.
Ok. This is my first Class.
Consider then another Class: "Encrypt". We can do DES, DES3, HASH, BITSHUFFLE.
I can go on with a third Class: "Compress". We can do LZW, ZIP, RAR, MP3, MPG, MP4, etc.
Instead of using these as 3 different classes, I would like to combine them in one, yet keeping them separate.
I would like to make a kind of an OwnerClass for the other classes. We can call this "MyStuff"
Whenever I type "MyStuff." in the editor, I should get up a list of "Data, Encrypt, Compress". Further, When I then choose "Compress", the next list for that class' procedures and functions will list up.
The classes may have some local variables, but the main functionality will be towards global arrays.
Maybe there are other ways of achieving this. I don't know.
My basic aim is to be able to categorize and put together routines that belong together.
This is already done in Units, but this does not help with the automatic list from the editor.
Thank you in advance.
Morten.
I think I understand what it is you're asking, after your edit.
What you're calling "classes in classes" are called properties and methods; they're other classes, variables, or procedures/functions that are declared in their containing class. The "list" you're talking about is called Code Insight, and it shows you the available properties and methods of the class you're referencing in your code at that point.
This should do something like you describe, and give you an idea of how to implement it in your own code:
unit MyStuffUnit;
interface
uses
SysUtils;
type
TEncryptionType = (etDES, etDES3, etHASH, etBITSHUFFLE);
TMyStuffEncryption = class(TObject)
private
FEncryptType: TEncryptionType;
public
constructor Create;
published
property EncryptionType: TEncryptionType read FEncryptType
write FEncryptType;
end;
TCompressionType = (ctLZW, ctZIP, ctRAR, ctMP3, ctMPG, ctMP4);
TMyStuffCompression = class(TObject)
private
FCompressionType: TCompressionType;
public
constructor Create;
published
property CompressionType: TCompressionType read FCompressionType
write FCompressionType;
end;
TMyStuff = class(TObject)
private
FCompression: TMyStuffCompression;
FEncryption: TMyStuffEncryption;
public
constructor Create;
destructor Destroy; override;
published
property Compression: TMyStuffCompression read FCompression
write FCompression;
property Encryption: TMyStuffEncryption read FEncryption
write FEncryption;
end;
implementation
constructor TMyStuffEncryption.Create;
begin
inherited;
FEncryptType := etDES;
end;
constructor TMyStuffCompression.Create;
begin
inherited;
FCompressionType := ctLZW;
end;
constructor TMyStuff.Create;
begin
inherited;
FCompression := TMyStuffCompression.Create;
FEncryption := TMyStuffEncryption.Create;
end;
destructor TMyStuff.Destroy;
begin
FCompression.Free;
FEncryption.Free;
inherited;
end;
end.
If you create an instance of TMyStuff in your code, you should be able to type MyStuff. and get the option of choosing Compression or Encryption. Choosing Compression should allow you to set the CompressionType property.
This should be enough to get you going. :-) Remember that Delphi includes the source code for the VCL and RTL in almost all editions, so you always have that to look at for examples. (They're not always the very best examples, but they should give you ideas on how to do things.)

Correct way to duplicate Delphi object

What are pros and cons of duplication an object instance with constructor or instance function?
Example A:
type
TMyObject = class
strict private
FField: integer;
public
constructor Create(srcObj: TMyObject); overload;
//alternatively:
//constructor CreateFrom(srcObj: TMyObject);
property Field: integer read FField;
end;
constructor TMyObject.Create(srcObj: TMyObject);
begin
inherited Create;
FField := srcObj.Field;
end;
Example B:
type
TMyObject = class
strict private
FField: integer;
public
function Clone: TMyObject;
property Field: integer read FField;
end;
function TMyObject.Clone: TMyObject;
begin
Result := TMyObject.Create;
Result.FField := FField;
end;
One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.
Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)
EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.
newObj := TMyObject.Create(oldObj)
vs.
newObj := oldObj.Clone;
EDIT2 or "Why I want single-line operation"
I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.
I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.
IOW, I prefer to write
Send(TMyObject.Create(obj));
or
Send(obj.Clone);
to
newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class
The Delphi way (TPersistent) separates creation and cloning:
dest := TSomeClass.Create;
dest.Assign(source);
and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.
edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)
type
TBaseSomeObject = class;
TBaseObjectClass = class of TBaseSomeObject;
TBaseSomeObject = class(TPersistent)
function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
end;
...
function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
begin
if Assigned(t) then
Result := t.Create
else
Result := TBaseObjectClass(Self.ClassType).Create;
Result.Assign(Self);
end;
SendObject(obj.Clone); // full clone.
SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object
For the rest, just implement your assign() operators, and you can mix multiple ways.
edit2
I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.
I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)
The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
The clone way is short although you need to provide a call for each class.
The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.
And if you implement Assign using streams, you have only one place to worry about which fields need to be available.
I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.
I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.

getting around circular references in Delphi [duplicate]

This question already has answers here:
Delphi Enterprise: how can I apply the Visitor Pattern without circular references?
(4 answers)
Closed 8 years ago.
Is there a way of getting around circular unit references in Delphi?
Maybe a newer version of delphi or some magic hack or something?
My delphi project has 100 000+ lines of code mostly based on singleton classes. I need to refactor this, but that would mean several months of "circular reference" hell :)
I've been maintaining close to a million lines of legacy code for the past 10 years so I understand your pain!
In the code that I maintain, when I've encountered circular uses, I frequently have found that they are caused by constants or type definitions in unit A that are needed by unit B. (Sometimes it's also a small bit of code (or even, global variables) in Unit A that is also needed by unit B.
In this situation (when I'm lucky!) I can carefully extract those parts of the code into a new unit C that contains the constants, type definitions, and shared code. Then units A and B use unit C.
I post the above with some hesitance because I'm not an expert on software design and realize there are many others here who are far more knowledgeable than I am. Hopefully, though, my experience will be of some use to you.
It seems you have quite serious code design issues. Besides many signs of such issues, one is the circular unit reference. But as you said: you cannot refactor all the code.
Move all what is possible to the implementation section. They are allowed to have circular references.
To simplify this task you can use 3rd party tools. I would recommend
Peganza Pascal Analyzer - it will suggest what you can move to the implementation section. And will give you many more hints to improve your code quality.
Use the implementation section uses whenever possible, and limit what's in the interface uses clause to what has to be visible in the interface declarations.
There is no "magic hack". Circular references would cause an endless loop for the compiler (unit A requires compiling unit B which requires compiling unit A which requires compiling unit B, etc.).
If you have a specific instance where you think you cannot avoid circular references, edit your post and provide the code; I'm sure someone here can help you figure out how to get it fixed.
There is many ways to avoid circular references.
Delegates.
Way too often, an object will execute some code that should be done in an event instead than being done by the object itself. Whether it is because the programmer working on the project was too short on time(aren't we always?), didn't have enough experience/knowledge or was just lazy, some code like this eventually end up in applications. Real world exemple : TCPSocket component that directly update some visual component on the application's MainForm instead of having the main form register a "OnTCPActivity" procedure on the component.
Abstract Classes/Interfaces. Using either of them allow to remove a direct dependance between many units. An abstract class or an interface can be declared alone in its own unit, limiting dependancies to a maximum. Exemple: Our application has a debug form. It has uses on pretty much the whole application as it displays information from various area of the application. Even worse, every form that allows to show the debug form will also also end up requiring all the units from the debug form. A better approach would be to have a debug form which is essentially empty, but that has the capacity to register "DebugFrames".
TDebugFrm.RegisterDebugFrame(Frame : TDebugFrame);
That way, the TDebugFrm has no dependancies of its own (Except than on the TDebugFrame class). Any and all unit that requires to show the debug form can do so without risking to add too many dependancies either.
There are many other exemple... I bet it could fill a book of its own. Designing a clean class hierarchy in a time efficient fashion is pretty hard to do and it comes with experience. Knowing the tools available to achieve it and how to use them is the 1st step to achieve it. But to answer your question... There is no 1-size-fit-all answer to your question, it's always to be taken on a case by case basis.
Similar Question: Delphi Enterprise: how can I apply the Visitor Pattern without circular references?
The solution presented by Uwe Raabe uses interfaces to resolve the circular dependency.
Modelmaker Code Explorer has a really nice wizard for listing all the uses, including cycles.
It requires that your project compiles.
I agree with the other posters that it is a design issue.
You should carefully look at your design, and remove unused units.
At DelphiLive'09, I did a session titled Smarter code with Databases and data aware controls which contains quite few tips on good design (not limited to DB apps).
--jeroen
I found a solution that doesn't need the use of Interfaces but may not resolve every issues of the circular reference.
I have two classes in two units: TMap and TTile.
TMap contains a map and display it using isometric tiles (TTile).
I wanted to have a pointer in TTile to point back on the map. Map is a class property of TTile.
Class Var FoMap: TObject;
Normaly, you will need to declare each corresponding unit in the other unit... and get the circular reference.
Here, how I get around it.
In TTile, I declare map to be a TObject and move Map unit in the Uses clause of the Implementation section.
That way I can use map but need to cast it each time to TMap to access its properties.
Can I do better? If I could use a getter function to type cast it. But I will need to move Uses Map in the Interface section.... So, back to square one.
In the Implementation section, I did declare a getter function that is not part of my class. A Simple function.
Implementation
Uses Map;
Function Map: TMap;
Begin
Result := TMap(TTile.Map);
End;
Cool, I thought. Now, every time I need to call a property of my Map, I just use Map.MyProperty.
Ouch! Did compile! :) Did not work the expected way. The compiler use the Map property of TTile and not my function.
So, I rename my function to aMap. But my Muse spoke to me. NOOOOO! Rename the Class Property to aMap... Now I can use Map the way I intented it.
Map.Size; This call my little function, who typecast aMap as TMap;
Patrick Forest
I gave a previous answer but after some thinking and scratching I found a better way to solve the circular reference problem. Here my first unit who need a pointer on an object TB define in unit B.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, b, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
FoB: TB;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
FoB := TB.Create(Self);
showmessage(FoB.owner.name);
end;
end.
Here the code of the Unit B where TB has a pointer on TForm1.
unit B;
interface
Uses
dialogs, Forms;
type
TForm1 = class(TForm);
TB = class
private
FaOwner: TForm1;
public
constructor Create(aOwner: TForm);
property owner: TForm1 read FaOwner;
end;
implementation
uses unit1;
Constructor TB.create(aOwner: TForm);
Begin
FaOwner := TForm1(aOwner);
FaOwner.Left := 500;
End;//Constructor
end.
And here why it compiles. First Unit B declare the use of Unit1 in the implementation section. Resolving immediately the circular reference unit between Unit1 et Unit B.
But to allow Delphi to compile, I need to give him something to chew on the declaration of FaOwner: TForm1. So, I add stub class name TForm1 who match the declaration of TForm1 in Unit1.
Next, when come the time to call the constructor, TForm1 is able to pass itself has the parameter. In the constructor code, I need to typecast the aOwner parameter to Unit1.TForm1. And voilà, FaOwner his set to point on my form.
Now, if the class TB need to use FaOwner internally, I don't need to typecast it every time
to Unit1.TForm1 because both declaration are the same. Note that you could set the declaration of to constructor to
Constructor TB.create(aOwner: TForm1);
but when TForm1 will call the constructor and pass itself has a parameter, you will need to typecast it has b.TForm1. Otherwise Delphi will throw an error telling that both TForm1 are not compatible. So each time you call the TB.constructor you will need to typecast to the appropriate TForm1. The first solution, using a common ancestor, his better. Write the typecast once and forget it.
After I posted it, I realized that I made a mistake telling that both TForm1 were identical. They are not Unit1.TForm1 has components and methods that are unknown to B.TForm1. Has long TB doesn't need to use them or just need to use the commonality given by TForm you're okay. If you need to call something particular to UNit1.TForm1 from TB, you will need to typecast it to Unit1.TForm1.
I try it and test it with Delphi 2010 and it compiled and worked.
Hope it will help and spare you some headache.

Object crashing when in main program, but not when moved to unit

I've written a custom SDL GUI toolkit (source is on http://sourceforge.net/projects/lkgui/files/) and I'm having an issue with an inherited object.
When the object is within the main program, the constructor isn't called hence the program doesn't properly initialise the object and it crashes after some commands (Specifically, TStartGameButton inherits from GUI_Canvas inherits from GUI_Element and anything that is not defined in GUI_Element crashes the program with an EAccessViolation). When the object is placed within a unit, this problem goes away.
I understand that I could just leave it in the unit, but it will lead to some ugly code that could hopefully be avoided.
Has anyone any idea why this might be happening and how I may avoid it?
Old-style Delphi objects have been broken since the release of Delphi 2, perhaps earlier. They do not do inheritance well when they have fields of compiler-managed types, such as string or dynamic arrays. There was a discussion about it in 2004 on comp.lang.pascal.delphi.misc. Here was the code to reproduce it:
type
TBase = object
public
s: string;
end;
TDerived = object(TBase)
end;
procedure test;
var
obj: TDerived; //okay for TBase!
begin
assert(obj.s = '', 'uninitialized dynamic variable');
end;
And in fact it's only OK for TBase by accident because of how the function's prologue code happens to be generated. Putting additional code in that function can make it crash anyway.
Indeed, it's exactly as you've observed — old-style objects don't get initialized properly. Their string fields don't start out holding an empty string; instead, they hold garbage, and so it's not even possible to initialize them yourself without using something like FillChar.
This appears to be due to the variables being local variables. Unit-scope ("global") variables seem to work OK. Variables that are declared at unit scope but only used by the unit's initialization section, or at program scope and used only in the DPR file's main begin-end block, are treated by the compiler as local variables, so they're not set to all-bits-zero like their global counterparts. When you move your variable declaration to a unit but continue to use it in your DPR file, it's elevated to "global" status.
Your TGUI_Element type has a string member called DbgName, and it looks like that's the only string field you have in the type hierarchy. Take that out, or change it to ShortString, and I'll bet your crashes go away, at least temporarily.
Why are you giving all objects individual named constructors instead of making them virtual?
type tx = object
constructor init; virtual;
end;
txx = object(tx)
constructor init; virtual; // like override in Delphi classes.
end;
If you need a visual hierarchy to look at, have a look at Free Vision, it demonstrates nearly every facet of the TP object model
Oops apparantly virtual constructors are not possible in the TP model

Resources