Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2010-10

object type: strange behaviour

15 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Birger Jansen

Hi all,

I'm converting a project from Delphi to FreePascal. It is a mathematical application that uses records with methods. Since this is not possible in FPC I am trying to convert this base type from record to object:

  TPoint = object
  public
    X,Y,Z: Double;
    class function Create(const Ax, Ay, Az: Double): TPoint;static;
    class function Distance(const P1, P2: TPoint): Double;static;
  end;
  class function TPoint.Create(const Ax, Ay, Az: Double): TPoint;static;
  begin
    Result.X := Ax;
    Result.Y := Ay;
    Result.Z := Az;
  end;
  class function TPoint.Distance(const P1, P2: TPoint): Double;static;
  var
    aX: Double;
  begin
    aX := Sqr(P1.X - P2.X) + Sqr(P1.Y - P2.Y) + Sqr(P1.Z - P2.Z);
    Result := Sqrt(aX);
  end;

So far so good, this works (compiles and produces the desired result):

  var
    P1, P2: TPoint;
    d: Double;
  begin
    P1 := TPoint.Create(0,1,1);
    P2 := TPoint.Create(1,0,0);
    d := TPoint.Distance(P1, P2);
    WriteLn(d);
    ReadLn;
  end.

Now there is a class type that has a TPoint field:

  TNode = class(TObject)
  public
    FCoord: TPoint; { coords }
    constructor Create(const X, Y, Z: Double);
  end;

And a constructor that sets this point:

  constructor TNode.Create(const X, Y, Z: Double);
  begin
    FCoord.X := X;
    FCoord.Y := Y;
    FCoord.Z := Z;
  end;

This works, however this doesn't:

  constructor TNode.Create(const X, Y, Z: Double);
  begin
    FCoord := TPoint.Create(X,Y,Z);
  end;

It fails with an error: Class isn't a parent class of the current class. If I set TNode.FCoord outside the constructor there is no , when I try to access other methods of TPoint this fails (see UpdateCoord in the full source below).

Any idea what causes this behaviour? I included the complete code below for your reference.

Kind regards,
Birger Jansen

program TestProject;
{$mode delphi}{$H+}
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes;
type
  { TPoint }
  TPoint = object
  public
    X,Y,Z: Double;
    class function Create(const Ax, Ay, Az: Double): TPoint;
    class function Distance(const P1, P2: TPoint): Double;
  end;
  { TNode }
  TNode = class(TObject)
  public
    FCoord: TPoint; { coords }
    constructor Create(const X, Y, Z: Double);
    procedure UpdateCoord(const Value: TPoint);
  end;

{ TPoint }

class function TPoint.Create(const Ax, Ay, Az: Double): TPoint;
begin
  Result.X := Ax;
  Result.Y := Ay;
  Result.Z := Az;
end;
class function TPoint.Distance(const P1, P2: TPoint): Double;
var
  aX: Double;
begin
  aX := Sqr(P1.X - P2.X) + Sqr(P1.Y - P2.Y) + Sqr(P1.Z - P2.Z);
  Result := Sqrt(aX);
end;
constructor TNode.Create(const X, Y, Z: Double);
begin
  FCoord.X := X;
  FCoord.Y := Y;
  FCoord.Z := Z;
// this does not work:
  FCoord := TPoint.Create(X,Y,Z);
end;
procedure TNode.UpdateCoord(const Value: TPoint);
begin
// this does not work:
  if TPoint.Distance(FCoord, Value) < 1E-10 then
    FCoord := Value;
  end;
end;
var
    P1, P2: TPoint;
    N: TNode;
    d: Double;
begin
  N := TNode.Create(1,2,3);
// this works:
  N.FCoord := TPoint.Create(1,2,3);
  P1 := TPoint.Create(0,1,1);
  P2 := TPoint.Create(1,0,0);
  d := TPoint.Distance(P1, P2);
  WriteLn(d);
  ReadLn;
end.

Reply by email

Birger Jansen
to Birger Jansen

Forget my last message I upgraded FPC 2.4.1 to 2.5.1 and now this works like it should!

-----Oorspronkelijk bericht-----
Van: Birger Jansen
Verzonden: vr 22-10-2010 11:38
Aan: ;
Onderwerp: [Lazarus] object type: strange behaviour

Show quoted text 164 lines
Hi all, I'm converting a project from Delphi to FreePascal. It is a mathematical application that uses records with methods. Since this is not possible in FPC I am trying to convert this base type from record to object: TPoint = object public X,Y,Z: Double; class function Create(const Ax, Ay, Az: Double): TPoint;static; class function Distance(const P1, P2: TPoint): Double;static; end; class function TPoint.Create(const Ax, Ay, Az: Double): TPoint;static; begin Result.X := Ax; Result.Y := Ay; Result.Z := Az; end; class function TPoint.Distance(const P1, P2: TPoint): Double;static; var aX: Double; begin aX := Sqr(P1.X - P2.X) + Sqr(P1.Y - P2.Y) + Sqr(P1.Z - P2.Z); Result := Sqrt(aX); end; So far so good, this works (compiles and produces the desired result): var P1, P2: TPoint; d: Double; begin P1 := TPoint.Create(0,1,1); P2 := TPoint.Create(1,0,0); d := TPoint.Distance(P1, P2); WriteLn(d); ReadLn; end. Now there is a class type that has a TPoint field: TNode = class(TObject) public FCoord: TPoint; { coords } constructor Create(const X, Y, Z: Double); end; And a constructor that sets this point: constructor TNode.Create(const X, Y, Z: Double); begin FCoord.X := X; FCoord.Y := Y; FCoord.Z := Z; end; This works, however this doesn't: constructor TNode.Create(const X, Y, Z: Double); begin FCoord := TPoint.Create(X,Y,Z); end; It fails with an error: Class isn't a parent class of the current class. If I set TNode.FCoord outside the constructor there is no , when I try to access other methods of TPoint this fails (see UpdateCoord in the full source below). Any idea what causes this behaviour? I included the complete code below for your reference. Kind regards, Birger Jansen program TestProject; {$mode delphi}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Classes; type { TPoint } TPoint = object public X,Y,Z: Double; class function Create(const Ax, Ay, Az: Double): TPoint; class function Distance(const P1, P2: TPoint): Double; end; { TNode } TNode = class(TObject) public FCoord: TPoint; { coords } constructor Create(const X, Y, Z: Double); procedure UpdateCoord(const Value: TPoint); end; { TPoint } class function TPoint.Create(const Ax, Ay, Az: Double): TPoint; begin Result.X := Ax; Result.Y := Ay; Result.Z := Az; end; class function TPoint.Distance(const P1, P2: TPoint): Double; var aX: Double; begin aX := Sqr(P1.X - P2.X) + Sqr(P1.Y - P2.Y) + Sqr(P1.Z - P2.Z); Result := Sqrt(aX); end; constructor TNode.Create(const X, Y, Z: Double); begin FCoord.X := X; FCoord.Y := Y; FCoord.Z := Z; // this does not work: FCoord := TPoint.Create(X,Y,Z); end; procedure TNode.UpdateCoord(const Value: TPoint); begin // this does not work: if TPoint.Distance(FCoord, Value) < 1E-10 then FCoord := Value; end; end; var P1, P2: TPoint; N: TNode; d: Double; begin N := TNode.Create(1,2,3); // this works: N.FCoord := TPoint.Create(1,2,3); P1 := TPoint.Create(0,1,1); P2 := TPoint.Create(1,0,0); d := TPoint.Distance(P1, P2); WriteLn(d); ReadLn; end. -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Michael Van Canneyt
to Birger Jansen: I'm converting a project from Delphi to FreePascal. It is a mathematical application that…
Birger Jansen wrote
Hi all, I'm converting a project from Delphi to FreePascal. It is a mathematical application that uses records with methods. Since this is not possible in FPC I am trying to convert this base type from record to object:

[snip]

It fails with an error: Class isn't a parent class of the current class. If I set TNode.FCoord outside the constructor there is no , when I try to access other methods of TPoint this fails (see UpdateCoord in the full source below). Any idea what causes this behaviour? I included the complete code below for your reference.

Sounds like a bug in the compiler. Probably it sees the TPoint.Create as a reference to an 'inherited constructor'.

I think you should create a bug report for this.

Michael.

Reply by email

Graeme Geldenhuys
to Birger Jansen: I'm converting a project from Delphi to FreePascal. It is a mathematical application that…

Op 2010-10-22 10:19, Birger Jansen het geskryf:

I'm converting a project from Delphi to FreePascal. It is a mathematical application that uses records with methods.

Side Note:
I still don't get why Borland/Embarcadero did that... records with methods. MvC hinted that it is because .Net has records with methods. But delphi Objects do exactly that, and existed since Turbo Pascal days. In terms of Delphi, what exactly is the difference between a "record with methods" and a Object?

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net:8080/fpgui/

Reply by email

A.J. Venter
to Graeme Geldenhuys

TP Objects were implemented as "Records with methods" years ago - but it wasn't really true OO.
There was no concept of a class - and objects were literally nothing BUT a record with methods, only the barest concepts of virtual, private and public methods were present, inherittence was... shoddy at best...

It was a poor man's OO before OP gave us proper class-based objects.

The whole records-with-methods thing to me sounds like a cheap way of using code that was lying around to add an entry to a featurelist that was technically already there ages ago - and useless.

Graeme Geldenhuys wrote 26 lines
Op 2010-10-22 10:19, Birger Jansen het geskryf: > > I'm converting a project from Delphi to FreePascal. It is a mathematical > application that uses records with methods. Side Note: I still don't get why Borland/Embarcadero did that... records with methods. MvC hinted that it is because .Net has records with methods. But delphi Objects do exactly that, and existed since Turbo Pascal days. In terms of Delphi, what exactly is the difference between a "record with methods" and a Object? Regards, ?- Graeme - -- fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net:8080/fpgui/ -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
A.J. Venter Founder and lead developer, Kongoni GNU/Linux www.kongoni.co.za www.silentcoder.co.za - Blog

Reply by email

Graeme Geldenhuys
to A.J. Venter

Op 2010-10-22 13:22, A.J. Venter het geskryf:

TP Objects were implemented as "Records with methods" years ago

I know that, and that is what puzzles me. Borland/Embarcadero introduced in again a little while ago (while TP Objects still exits in the Delphi language), and actually called it a "new feature". Puzzling?

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net:8080/fpgui/

Reply by email

Hans-Peter Diettrich
to Graeme Geldenhuys: Op 2010-10-22 10:19, Birger Jansen het geskryf: Side Note: I still don't get why…
Show quoted text 5 lines
I still don't get why Borland/Embarcadero did that... records with methods. MvC hinted that it is because .Net has records with methods. But delphi Objects do exactly that, and existed since Turbo Pascal days. In terms of Delphi, what exactly is the difference between a "record with methods" and a Object?

Delphi Objects should only be used with TP7 member types.

The initialization and finalization, required for managed types (AnsiString...), is broken in derived Objects, since the introduction of these types. Because it were easy to fix that bug (like with nested records), it seems to be a purely political decision, to deprecate the Object type for a real reason. Note that Object types with no ancestors are properly initialized and finalized...

DoDi

Reply by email

Alexander Klenin
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote 8 lines
Delphi Objects should only be used with TP7 member types. The initialization and finalization, required for managed types (AnsiString...), is broken in derived Objects, since the introduction of these types. Because it were easy to fix that bug (like with nested records), it seems to be a purely political decision, to deprecate the Object type for a real reason. Note that Object types with no ancestors ?are properly initialized and finalized...

I hope FPC does not have this bug?

Alexander S. Klenin

Reply by email

Marco van de Voort
to Graeme Geldenhuys: Op 2010-10-22 10:19, Birger Jansen het geskryf: Side Note: I still don't get why…
Graeme Geldenhuys wrote 8 lines
> application that uses records with methods. Side Note: I still don't get why Borland/Embarcadero did that... records with methods. MvC hinted that it is because .Net has records with methods. But delphi Objects do exactly that, and existed since Turbo Pascal days. In terms of Delphi, what exactly is the difference between a "record with methods" and a Object?

Not so much for .NET, but for the emulation of .NET features. Several of these are based on duck typing like functionatily where the compiler searches for "magical" function names.

I think they didn't pick the object type because it also supports VMTs, a feature that complicates resolving methods, and considerably complicating those features. TP Tobject can magically gain a VMT when a virtual method is used.

Reply by email

Marco van de Voort
to Alexander Klenin: On Sat, Oct 23, 2010 at 06:55, Hans-Peter Diettrich I hope FPC does not have this bug?…
Alexander Klenin wrote 8 lines
> The initialization and finalization, required for managed types > (AnsiString...), is broken in derived Objects, since the introduction of > these types. Because it were easy to fix that bug (like with nested > records), it seems to be a purely political decision, to deprecate the > Object type for a real reason. Note that Object types with no ancestors ??are > properly initialized and finalized... I hope FPC does not have this bug?

Yes it does.

Reply by email

Graeme Geldenhuys
to Hans-Peter Diettrich: Delphi Objects should only be used with TP7 member types. The initialization and…

Op 2010-10-22 21:55, Hans-Peter Diettrich het geskryf:

Delphi Objects should only be used with TP7 member types.

...snip...

Note that Object types with no ancestors are properly initialized and finalized...

Thanks, I didn't know about the bug you mention. Luckily for me, when I need a hierarchy I always use Classes, but for a single type that will never have descendants, I often use Objects with member methods.

Here are two examples of Object usage in fpGUI:

type
  TfpgRect = object  // not class for static allocations
    Top: TfpgCoord;       // TfpgCoord is an alias for Integer
    Left: TfpgCoord;
    Width: TfpgCoord;
    Height: TfpgCoord;
    procedure SetRect(aleft, atop, awidth, aheight: TfpgCoord);
    function  Bottom: TfpgCoord;
    function  Right: TfpgCoord;
    procedure SetBottom(Value: TfpgCoord);
    procedure SetRight(Value: TfpgCoord);
  end;
  TfpgPoint = object  // not class for static allocations
    X: integer;
    Y: integer;
    procedure SetPoint(AX, AY: integer);
    function  ManhattanLength: integer;
    function  ManhattanLength(const PointB: TfpgPoint): integer;
  end;

And because an instance can be allocated without a constructor, and automatically free'ed, Object is really useful.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net:8080/fpgui/

Reply by email

Marco van de Voort
to Graeme Geldenhuys: Op 2010-10-23 14:20, Marco van de Voort het geskryf: For Delphi compatibility I guess? -…
Graeme Geldenhuys wrote 5 lines
>> I hope FPC does not have this bug? > > Yes it does. For Delphi compatibility I guess?

Not that I know. One can argue if it really is a bug. See my other mail. For most purposes where one would use object, records with methods are better since a VMT can't accidentally confuse the picture (and that probably makes optimization easier too)

Reply by email

Graeme Geldenhuys
to Marco van de Voort: Not so much for .NET, but for the emulation of .NET features. Several of these are based…

Op 2010-10-23 14:18, Marco van de Voort het geskryf:

Show quoted text 5 lines
I think they didn't pick the object type because it also supports VMTs, a feature that complicates resolving methods, and considerably complicating those features. TP Tobject can magically gain a VMT when a virtual method is used.

Interesting thought, thanks Marco.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net:8080/fpgui/

Reply by email

Marco van de Voort
to Graeme Geldenhuys
Graeme Geldenhuys wrote 6 lines
> I think they didn't pick the object type because it also supports VMTs, a > feature that complicates resolving methods, and considerably complicating > those features. TP Tobject can magically gain a VMT when a virtual method > is used. Interesting thought, thanks Marco.

In the same sense, I wouldn't try to use objects with generics either :)

It will raise a gigantic amount of bugs, for no particular reason. Let it be legacy.

Btw, I noticed the ansistring-in-tobject issue when I tried to update turbo vision to ansistring.

Reply by email