Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DWScript setter for a property which is a Record

my issue is pretty simple. I have a dwsUnit which have this code:

type
  TPointCoord = record
    X: Float;
    Y: Float;
    Z: Float;
  end;

type
  TMyClass = class
  private
    fPosition: TPointCoord;

    function GetPosition: TPointCoord;
    procedure SetPosition(Val: TPointCoord);
  public
    property Position: TPointCoord read GetPosition write SetPosition;
    constructor Create;
end;

function TMyClass.GetPosition: TPointCoord;
begin
  Result := fPosition;
end;

procedure TMyClass.SetPosition(Val: TPointCoord);
begin
  fPosition := Val;
end;

constructor TMyClass.Create;
begin
  inherited Create;
  fPosition.X := 1;
  fPosition.Y := 2;  
  fPosition.Z := 3;    
end;

var
  mc: TMyClass;
begin
  mc := TMyClass.Create;
  mc.Position.X := 2;   //Syntax Error
end.

At mc.Position.X (or Position.Y or Z) i get:

Syntax Error: Cannot assign a value to the left-side argument [line: 42, column: 17]

What is the meaning of this? Is Record read-only if is a property? And how I can access that from Delphi Side. (the second issue, not a really big deal)

like image 313
user3931540 Avatar asked Aug 12 '14 02:08

user3931540


3 Answers

The reason for this error is because you're using a property of type record.

Record type is a value type, which means it's copied on assignment rather than referenced (like a class), so a function (or property) returning a record will make a copy and return a different record.

So your line

mc.Position.X := 2

is effectively equivalent to

temp := mc.getPosition;
temp.X := 2;

with "temp" being a different variable/storage from fPosition, so that code wouldn't change fPosition.X, it would only the "hidden" temporary copy's X field.

As this is generally unlikely to be what you're after, the DWS compiler, just like Delphi, throws an error.

The typical solution is to offer a distinct PositionX property, which will provide access to the X field of fPosition like

property PositionX : TPointCoord read (FPosition.X) write (FPosition.X);

or you can use explicit getters/setters, if you need more than the X field assigned.

Another solution would be use a reference types (a class f.i.), though this may not be very practical for a position or coordinate.

like image 86
Eric Grange Avatar answered Nov 15 '22 15:11

Eric Grange


While LHirstov code will work with DWScript that same particular code might not work with some older versions of Delphi.

So for those using older versions of Delphi I recomend simply adding more properties to TmyClass instead of modifying record.

What you need is simply add three new properties (one for each position parameter) and then define their Getter/Setter method to modify these specific parameters individually.

type
  TPointCoord = record
    X: Float;
    Y: Float;
    Z: Float;
  end;

  TMyClass = class
  private
    fPosition: TPointCoord;

    function GetPosition: TPointCoord;
    function GetPositionX: Float;
    function GetPositionY: Float;
    function GetPositionZ: Float;
    procedure SetPosition(Val: TPointCoord);
    procedure SetPositionX(Val: Float);
    procedure SetPositionY(Val: Float);
    procedure SetPositionZ(Val: Float);
  public
    property Position: TPointCoord read GetPosition write SetPosition;
    property PositionX: TPointCoord read GetPositionX write SetPositionX;
    property PositionY: TPointCoord read GetPositionY write SetPositionY;
    property PositionZ: TPointCoord read GetPositionZ write SetPositionZ;
    constructor Create;
end;

function TMyClass.GetPosition: TPointCoord;
begin
  Result := fPosition;
end;

procedure TMyClass.SetPosition(Val: TPointCoord);
begin
  fPosition := Val;
end;

function TMyClass.GetPositionX: Float;
begin
  Result := fPosition.X;
end;

procedure TMyClass.SetPositionX(Val: Float);
begin
  fPosition.X := Val;
end;


function TMyClass.GetPositionX: Float;
begin
  Result := fPosition.Y;
end;

procedure TMyClass.SetPositionY(Val: Float);
begin
  fPosition.Y := Val;
end;

function TMyClass.GetPositionX: Float;
begin
  Result := fPosition.Z;
end;

procedure TMyClass.SetPositionZ(Val: Float);
begin
  fPosition.Z := Val;
end;

So then in the end you access to specific position parameters using:

mc.PositionX := 2;

Now you might be asking yourself do I still need the old Position property. While you technically don't need it it could still come in handy when you need to read or write the position record as a whole.

like image 36
SilverWarior Avatar answered Nov 15 '22 15:11

SilverWarior


You have property of type TPointCoord and you want to assign only X coord, that's why you have an error. If you want to deal with X,Y and Z separately then you must assign properties to each of them. Here is an axample for X:

type
  TPointCoord = record
  private
    fX: real;
    fY: real;
    fZ: real;
    function GetX: real;
    procedure SetX(value: real);
  public
    property X: real read GetX write SetX;
  end;

function TPointCoord.GetX: real;
begin
  Result:=fX;
end;

procedure TPointCoord.SetX(value: Real);
begin
  fX:=Value;
end;

With the example above your statement mc.Position.X := 2; will be OK.

like image 1
LHristov Avatar answered Nov 15 '22 16:11

LHristov