Sign in Join

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

Mailing lists / fpc-pascal / 2009-06

Records as properties and Delphi compiler error

15 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

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

Unknown author

Hi Guys,

Is there a reason why the following code fails to compile in Delphi but compile in FPC? Could the reason be that FPC allows the use of global properties? Tested with Delphi 5 and up.

The Delphi compiler complains about the lines:
TestClass.TestRec.X := 10;
TestClass.TestRec.Y := 20; within the button click handler.
Delphi Error:  "Left side cannot be assigned to"
//*******************************************************
unit Unit1;

(*
This code sample is written for use with Lazarus.
The Delphi equivalent fails to compile using Delphi. *)

{$mode objfpc}{$H+}
interface

uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,

  StdCtrls;
type
  TForm1 = class(TForm)
    Button1 : TButton;
    procedure Button1Click(Sender : TObject);
  private
  public
  end;
  TTestRec = record
    X : Byte;
    Y : Byte;
  end;
  TTestClass = class
  private
    fTestRec : TTestRec;
  public
    property TestRec : TTestRec read fTestRec write fTestRec;
  end;
var
  Form1 : TForm1;
implementation
procedure TForm1.Button1Click(Sender : TObject);
var
  TestClass : TTestClass;
begin
  TestClass := TTestClass.Create();
  try
    ShowMessage('TestRec before' + #13 +
                'X = ' + IntToStr(TestClass.TestRec.X) + #13 +
                'Y = ' + IntToStr(TestClass.TestRec.Y));
    TestClass.TestRec.X := 10; //* Error "Left side cannot be assigned to"
    TestClass.TestRec.Y := 20 ;//* Error "Left side cannot be assigned to"
    ShowMessage('TestRec after' + #13 +
                'X = ' + IntToStr(TestClass.TestRec.X) + #13 +
                'Y = ' + IntToStr(TestClass.TestRec.Y));
  finally
    FreeAndNil(TestClass);
  end;
end;
initialization
  {$I unit1.lrs}
end.

Reply by email

Graeme Geldenhuys
to Leonardo M. Ramé
Leonardo M. Ramé wrote
in Graphics.pas, but I can't find it in FPC. Is this class replaced by a similar one?

I you just want to load and manipulate an image, you should be able to use the fpimage.pp unit included with FPC. I you are looking for the TPicture class specifically, then you will have to use the Lazarus LCL (Lazarus Component Library) which is a clone of the Delphi VCL.

Regards,
- Graeme -

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

Reply by email

Unknown author
to Jonas Maebe: No, it's an error in FPC which has been fixed in 2.3.1:…

Hi Jonas

Thanks for the reply.

A high level, a class is like a record that has been modified to include functions and procedures. I know that I'm over simplifying thing here, please bare with me.

I'm trying to understand the logic employed by the creators of Delphi where they don't allow to write to the fields of a record type property, but if the property points to a class type, then anything goes. In the example bellow, where a property is of a class type, both Delphi and FPC compile the code, but there is no guarantee that the object referenced to by the property has been instantiated before the property is used (The programmer must instantiate the TTestProp class within TTestClass prior to any call made to the property). IMO, it would be a nice feature if the compiler could be modified to issue a warning in such a case.Again, I'm over simplifying, to the compiler, it would be similar to checking for a variable declaration before the variable is used.

I thing that the "error" in the way that FPC allows record properties to access the record fields could be handy if retained. Perhaps this feature could be reserved for objfpc mode. What are your thoughts on the matter?

Regards,
Nino

//--------------------------------------------------------------------------
unit Unit1;

(*
This code sample is written for use with Lazarus.
The Delphi equivalent also compile using Delphi.
*)

{$mode objfpc}{$H+}
interface

uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,

  StdCtrls;
type
  TForm1 = class(TForm)
    Button1 : TButton;
    procedure Button1Click(Sender : TObject);
  private
  public
  end;
  TTestProp = class
  private
    fX : Byte;
    fY : Byte;
  public
    property X : Byte read fX write fX;
    property Y : Byte read fY write fY;
  end;
  TTestClass = class
   private
     fTestProp : TTestProp;
   public
     constructor Create();
     destructor Destroy(); override;
     property TestProp : TTestProp read fTestProp write fTestProp;
  end;
var
  Form1 : TForm1;
implementation
constructor TTestClass.Create();
begin
  //* If this line is commented, the compilation will succeed
  //* but a violation will occur at runtime in button click handler
  //* since the object referenced does not exist.
  //*
  //* Note: I'm not suggesting here that the compile should be aware
  //* that this object, whilst being referenced later in the code,
  //* is not instantiated, but it would be a cool feature if the compiler
  //* could be modified to issue at least a warning advising of such an event.
  fTestProp := TTestProp.Create();
end;
destructor TTestClass.Destroy();
begin
  if Assigned(fTestProp) then
    FreeAndNil(fTestProp);
  inherited Destroy();
end;
procedure TForm1.Button1Click(Sender : TObject);
var
  TestClass : TTestClass;
begin
  TestClass := TTestClass.Create();
  try
    ShowMessage('TestProp before' + #13 +
                'X = ' + IntToStr(TestClass.TestProp.X) + #13 +
                'Y = ' + IntToStr(TestClass.TestProp.Y));
    TestClass.TestProp.X := 10;
    TestClass.TestProp.Y := 20 ;
    ShowMessage('TestProp after' + #13 +
                'X = ' + IntToStr(TestClass.TestProp.X) + #13 +
                'Y = ' + IntToStr(TestClass.TestProp.Y));
  finally
    FreeAndNil(TestClass);
  end;
end;
initialization
  {$I unit1.lrs}
end.
//*************************************
Jonas Maebe wrote 15 lines
On 06 Jun 2009, at 17:36, wrote: > Is there a reason why the following code fails to compile in Delphi > but > compile in FPC? Could the reason be that FPC allows the use of global > properties? No, it's an error in FPC which has been fixed in 2.3.1: http://wiki.freepascal.org/User_Changes_Trunk#Treating_direct-mapped_proper ties_as_regular_fields Jonas _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Leonardo M. Ramé

Thanks Graeme, I'll take a look at fpimage unit.

Leonardo M. Ramé
http://leonardorame.blogspot.com

Graeme Geldenhuys wrote 28 lines
From: Graeme Geldenhuys Subject: Re: [fpc-pascal] TPicture (Graphics unit) To: "FPC-Pascal users discussions" Date: Sunday, June 7, 2009, 4:22 AM Leonardo M. Ramé wrote: > in Graphics.pas, but I can't find it in FPC. Is this class replaced > by a similar one? I you just want to load and manipulate an image, you should be able to use the fpimage.pp unit included with FPC. I you are looking for the TPicture class specifically, then you will have to use the Lazarus LCL (Lazarus Component Library) which is a clone of the Delphi VCL. Regards, - Graeme - -- ________________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/ _______________________________________________ fpc-pascal maillist  -  http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Jonas Maebe
to Unknown author: Hi Jonas Thanks for the reply. A high level, a class is like a record that has been…
Show quoted text 5 lines
A high level, a class is like a record that has been modified to include functions and procedures. I know that I'm over simplifying thing here, please bare with me.

The difference you skip over is the fundamental reason why one works and the other doesn't: a class is a pointer, while a record is a value.

Show quoted text 5 lines
I'm trying to understand the logic employed by the creators of Delphi where they don't allow to write to the fields of a record type property, but if the property points to a class type, then anything goes.

In case of a class, the property returns a pointer (namely the pointer to the class instance data), and then you (implicitly) dereference the pointer and write data where it points to. In case of a record, the property returns a record's value, and then (semantically) you change the value of this returned record (not the value of the element of the record that the property referred to).

This worked in previous FPC versions because rather than treating the result of the property like a function result (which it has to, because that's the semantical meaning of a property, so you can transparently change them into getters/setters without breaking any code), it treated it like a direct field access in case no getter/ setter existed. So rather than returning a record's value, the property returned "a reference to a record".

Show quoted text 15 lines
In the example bellow, where a property is of a class type, both Delphi and FPC compile the code, but there is no guarantee that the object referenced to by the property has been instantiated before the property is used (The programmer must instantiate the TTestProp class within TTestClass prior to any call made to xx the property). IMO, it would be a nice feature if the compiler could be modified to issue a warning in such a case.Again, I'm over simplifying, to the compiler, it would be similar to checking for a variable declaration before the variable is used.

Except that it's about dynamically allocated memory and depends on interprocedural control flow graph analysis, which makes it immensely more complex.

Show quoted text 6 lines
I thing that the "error" in the way that FPC allows record properties to access the record fields could be handy if retained. Perhaps this feature could be reserved for objfpc mode. What are your thoughts on the matter?

Things will remain the way they are for the reasons explained above.

Jonas

Reply by email

Unknown author
to Jonas Maebe
Jonas Maebe wrote 9 lines
On 07 Jun 2009, at 10:35, wrote: > A high level, a class is like a record that has been modified to > include > functions and procedures. I know that I'm over simplifying thing > here, please > bare with me. The difference you skip over is the fundamental reason why one works and the other doesn't: a class is a pointer, while a record is a value.

I understand the difference, but a pointer to a record still suffers the from the same problem as a pointer to a class i.e. if memory is not allocated then the code 's interesting to note though, that Delphi allows the assignment to the fields of a record type property via the with - do construct. Is this also the case with FPC 2.3.1?

Show quoted text 56 lines
> I'm trying to understand the logic employed by the creators of > Delphi where > they don't allow to write to the fields of a record type property, > but if the > property points to a class type, then anything goes. In case of a class, the property returns a pointer (namely the pointer to the class instance data), and then you (implicitly) dereference the pointer and write data where it points to. In case of a record, the property returns a record's value, and then (semantically) you change the value of this returned record (not the value of the element of the record that the property referred to). This worked in previous FPC versions because rather than treating the result of the property like a function result (which it has to, because that's the semantical meaning of a property, so you can transparently change them into getters/setters without breaking any code), it treated it like a direct field access in case no getter/ setter existed. So rather than returning a record's value, the property returned "a reference to a record". > In the example bellow, > where a property is of a class type, both Delphi and FPC compile the > code, > but there is no guarantee that the object referenced to by the > property has > been instantiated before the property is used (The programmer must > instantiate the TTestProp class within TTestClass prior to any call > made to xx > the property). IMO, it would be a nice feature if the compiler could > be > modified to issue a warning in such a case.Again, I'm over > simplifying, to > the compiler, it would be similar to checking for a variable > declaration > before the variable is used. Except that it's about dynamically allocated memory and depends on interprocedural control flow graph analysis, which makes it immensely more complex. > I thing that the "error" in the way that FPC allows record > properties to > access the record fields could be handy if retained. Perhaps this > feature > could be reserved for objfpc mode. What are your thoughts on the > matter? Things will remain the way they are for the reasons explained above. Jonas _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Graeme Geldenhuys
to Unknown author

Hi Nino,

Can you please refrain from quoting the *whole* previous message, when your reply only relates to one or two lines. If other developers want to read the whole context of the message thread they can look in their email history or the mailing list archive.

It is REALLY annoying seeing 80 lines in a message and then only 3 of those lines are actually the reply and the other 77 line are quoted text!

Regards, - Graeme - ________________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/

Reply by email

Unknown author
to Graeme Geldenhuys
Graeme Geldenhuys wrote 9 lines
Hi Nino, Can you please refrain from quoting the *whole* previous message, when your reply only relates to one or two lines. If other developers want to read the whole context of the message thread they can look in their email history or the mailing list archive. It is REALLY annoying seeing 80 lines in a message and then only 3 of those lines are actually the reply and the other 77 line are quoted text!

Hi Graeme,

Okay. Point taken. Mind you don't blow a gasket. Remember that today is only Monday.

Regards,
Nino

Reply by email

Martin Friebe
to Unknown author: I understand the difference, but a pointer to a record still suffers the from the same…
Show quoted text 20 lines
On Sunday 07 June 2009 22:19:47 Jonas Maebe wrote: > On 07 Jun 2009, at 10:35, wrote: > >> A high level, a class is like a record that has been modified to >> include >> functions and procedures. I know that I'm over simplifying thing >> here, please >> bare with me. >> > The difference you skip over is the fundamental reason why one works > and the other doesn't: a class is a pointer, while a record is a value. > I understand the difference, but a pointer to a record still suffers the from the same problem as a pointer to a class i.e. if memory is not allocated then the code 's interesting to note though, that Delphi allows the assignment to the fields of a record type property via the with - do construct. Is this also the case with FPC 2.3.1?
  property ARecord: TSomeRecorType read FTheRecord;
  property AClass: TSomeClassType read FTheClass;
must be treated the same as
  function GetARecord: TSomeRecorType;
  function GetAClass: TSomeClassType;

Why? Because that is what a property is for. A property means you are free to change the "read" part to use a getter-function. And doing that change *must* not break any code.

You can not (not in a sensible way) do
  GetARecord() := ....
Neither does it make sense to do
  GetARecord().X := ....
The last bit may be possible, but it would assign .X to a *temporary*
copy of the record, same as
  MyTemp := GetARecord();
  MyTemp.X := ....

What is the Point of this assignment, the value is lost, and unless you actually have a temp variable (like in the expanded example) you can not even retrieve the assigned value.

However
  with GetARecord() do begin
      x := 5;  // only the temp copy
     writeln(x); // using the value from the temp copy
  end;

-------------
As for classes
GetAClass()
does not return a temporary copy of your class, it return a temporary pointer to your class.

so the following makes NO sense
   GetAClass() := TClass.Create; // trying to replace the class

but

   GetAClass().X := 5;

following the temporary pointer still leads to the real object

-------------
This is because TClass behaves like a pointer.
Record does not behave like a pointer ** (the whole memory occupied by all values of the record is copied). **

This difference also applies if you pass such values as params to a procedure or function.

Martin

Reply by email

Tom Backer Johnsen
to Martin Friebe: property ARecord: TSomeRecorType read FTheRecord; property AClass: TSomeClassType read…

I have just recently downloaded FPC / Lazarus with the plan of porting some Delphi 7 programs (Windows XP). Then I am encountering some some errors where I thought was a trivial task.

I want to print the contents of a plain text file to the default printer, adding some frills like page headers and such to the pages in the process. What I have done is to include Printers in a uses declaration, and declare Prn as a type TextFile. Then something like:

AssignPrn(Prn);
Rewrite (Prn);
for ...  begin
    writeln (Prn, ....);
    end;
Closefile (Prn);

Then I find that there is no such thing as "AssignPrn". I have to add that I have used Delphi 7 and its predecessors for a long time and may have acquired bad habits which work under Delphi 7 but is doubtful.

Is it possible for someone to provide a code snippet for this seemingly trivial task?

Tom

Reply by email

Felipe Monteiro de Carvalho
to Tom Backer Johnsen

First include the package Printers4Lazarus as a requirement.

Then you can write something like this:

program printtest;
{$mode objfpc}{$H+}

uses
Interfaces, // this includes the LCL widgetset
Classes,
Forms,
Printer4Lazarus,
Printers,

  PrintersDlgs;
var
  PaperWorkRect: TRect;
  PrintDlg: TPrintDialog;
begin
  Application.Initialize;
  PrintDlg := TPrintDialog.Create(nil);
  try
    PrintDlg.MinPage := 1;
    PrintDlg.MaxPage := 1;
    if PrintDlg.Execute then
    begin
      PaperWorkRect := Printer.PaperSize.PaperRect.WorkRect;
      Printer.BeginDoc;
      Printer.Canvas.TextOut(PaperWorkRect.Left, PaperWorkRect.Top, 'TextOut');
      Printer.EndDoc;
    end;
  finally
    PrintDlg.Free;
  end;
end.
Felipe Monteiro de Carvalho

Reply by email