5

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)

LU RD
  • 34,438
  • 5
  • 88
  • 296
  • 1
    See [Can a record be used as a property of an object?](http://stackoverflow.com/q/9156401/576719) and [“Left side cannot be assigned to” for record type properties in Delphi](http://stackoverflow.com/q/620506/576719). The answers there clearly indicates this question is a duplicate. – LU RD Aug 12 '14 at 05:11
  • Haven't used DWS in quite some time, however, I'm a little surprised that it doesn't allow to access field within a record that is a property just like Delphi, anyhu', if it's OK with you, just change "TPointCoord = record" to "TPointCoord = class(TObject)" –  Aug 12 '14 at 08:11
  • 2
    @ComputerSaysNo: The latest Delphis don't allow you to modify a field of a record property either. The record can be a function result, so the record itself is read-only. – Rudy Velthuis Aug 12 '14 at 08:50
  • @RudyVelthuis IIRC even as far back as D7 you can't do that(don't quote me on that), but DWS is a scripting engine, it could make it work –  Aug 12 '14 at 09:35
  • @ComputerSaysNo no it can't a record is a value type, so the getter returns a copy, if change was allowed it would happen on the copy, not the original, which thus wouldn't change fPosition. – Eric Grange Aug 12 '14 at 09:46
  • @ComputerSaysNo: What Eric said applies to Delphi itself too. In older versions, it was allowed, but useless. Nowadays, it is not allowed. – Rudy Velthuis Aug 12 '14 at 09:58

3 Answers3

2

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.

SilverWarior
  • 7,372
  • 2
  • 16
  • 22
2

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.

Eric Grange
  • 5,931
  • 1
  • 39
  • 61
  • It doesn't bother me to use a Class instead of a Record, but the question now is how I may call `fPosition := TPointCoord.Create` from delphi (using XE2) side. Thank's for your time. – user3931540 Aug 12 '14 at 10:50
  • Are you referring to doing that assignment in the script context from a Delphi host application, or how to do it in Delphi in a cross-platform library? For the first you need to create the script object and assign it, though if you're going to assign coordinate by coordinate, declaring PositionX/etc. will be more convenient. For the second, you need to use a class in Delphi as well, though in XE2 you'll have to be careful about freeing the class instances manually (an issue you don't have with records) – Eric Grange Aug 12 '14 at 13:53
  • After 10+ hours, i finally get it right. Thank's allot, you are right, as always. – user3931540 Aug 12 '14 at 21:33
1

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.

LHristov
  • 1,103
  • 7
  • 16
  • I tried this, so no Syntax Error but `mc.Position.X := 2;` is not set to 2, is still 1, as I set'it in the Class Constructor. Thx. – user3931540 Aug 12 '14 at 09:45