views:

127

answers:

2

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?

A: 

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

Marco van de Voort
Mostly because I didn't know that I could :S
lochok
Should this be working? I changed everything to virtual constructors and got the following: Error: Virtual constructors are only supported in class object model
lochok
No, Lochok, it shouldn't work. The compiler is correct. Virtual constructors are only useful when you can refer to a *type* by reference, but that's only possible with classes, not old-style objects.
Rob Kennedy
+3  A: 

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.

Rob Kennedy
Hmm, seems FPC is bugcompatible here, don't see initialization of obj here too
Marco van de Voort
Yup - changing it to a ShortString fixed the problem. Thank you.Ironic that the function to help me debug was the cause of it crashing...
lochok