4

What happens when the assign operator := gets overloaded in Object Pascal? I mainly mean what gets evaluated first and more importantly how (if possible) can I change this order. Here is an example that bugs me:

I declare TMyClass thusly:

TMyClass = class
  private
    FSomeString: string;
    class var FInstanceList: TList;
  public
    function isValid: boolean;
    property SomeString: String write setSomeString;
  end;

the isValid function checks MyObject for nil and dangling pointers.

Now lets assume I want to overload the := operator to assign a string to TMyClass. I also want to check if the object I'm assigning this string to is a valid object and if not create a new one, so:

operator :=(const anewString: string): TMyClass;
  begin
    if not(result.isValid) then
      result:= TMyObject.Create;
    result.SomeString:= aNewString;
  end;

In short I was hoping that the result would automatically hold the pointer to the object I'm assigning to. But tests with the following:

procedure TForm1.TestButtonClick(Sender: TObject);
  var
    TestObject: TMyObject;
  begin
    TestObject:= TMyObject.Create;
    TestObject:= 'SomeString';
    TestObject.Free;
  end;

led me to believe that instead an intermediate value for result is assigned first and the actual assignment to TestObject happens after the code in := executes.

Everything I know about coding is self taught but this example shows that I clearly missed some basic concept somewhere.

I understand that there are easier ways to do this than by overloading a := operator but out of scientific curiosity is there ANY way to make this code work? (No matter how complicated.)

Cœur
  • 37,241
  • 25
  • 195
  • 267
N00BKING
  • 41
  • 4
  • This is clearly visible in the syntax. Result is not a reference, so it is a temporary value to be filled by you. And no I don't think this can be done by operator overloading. Maybe with a default property. – Marco van de Voort Oct 31 '15 at 18:06

2 Answers2

4

It's not possible to do what you want with operator overloads. You must use a method.

The problem is that the := operator does not give you the access to the left hand side (LHS) argument (here it's the Self, a pointer to the current instance) but only to the right hand side argument.

Currently in you example if not(result.isValid) then is dangereous because the result at the beginning of the function is undefined (it can have any value, it can be either nil or not and when not nil, calling isValid will lead to some possible violation access. It does not represent the LHS at all.

Using a regular method you would have an access to the Self and you could call isValid.

Abstract type
  • 1,901
  • 2
  • 15
  • 26
  • Thanks. That's what I thought. But does that mean there is no way to get the LHS? How about using the `inline` keyword? Or maybe some low level assembly code? (For me assembler is pure magic so maybe it can help here.) You're right of course that regular methods make more sense but still... The reference counting does something slightly similar to what I want to do, I think? – N00BKING Nov 01 '15 at 17:55
  • Yes, that means that. Operator overloading in Object Pascal is not as good as in other languages. I particularly think to another one where the overloads are directly written as member functions, so the _self_ is always available. You could also try to ask on the FPC mailing list or on lazarus-ide.org forums, maybe someone will have a better answer there. – Abstract type Nov 01 '15 at 18:30
  • @N00BKING I would think attempting to overload in the manner you are wanting to would obfuscate the code behavior. In other language, such as Ruby, you would accomplish what you're after using an operation such as `x = x || y` or abbreviated, `x ||= y` which says to assign `y` to `x` if `x` is `nil`. However, as Nestedtype points out, Pascal doesn't allow you to create your own operators, which would actually be a better, clearer way to do this IMHO. You're stuck doing it long-hand, or write a very small function or method to do it, which would also express the behavior clearly. – lurker Nov 02 '15 at 16:29
2

I do not have Lazarus to check, but it is possible in Delphi in the following way. We give access to an instance of the class indirectly via TValue.

Here is a sample class:

  type
  TMyClass = class(TComponent)
  private
    FSomeString: string;
  published
    property SomeString: string read FSomeString write FSomeString;
  end;

And we do the following in the container class (for example, TForm1).

  TForm1 = class(TForm)
  private
    FMyClass: TMyClass;
    function GetMyTypeString: TValue;
    procedure SetMyTypeString(const Value: TValue);
  public   
    property MyClass: TValue read GetMyTypeString write SetMyTypeString;
  end;

...

function TForm1.GetMyTypeString: TValue;
begin
  Result := FMyClass;
end;

procedure TForm1.SetMyTypeString(const Value: TValue);
begin
  if Value.Kind in [TTypeKind.tkChar, TTypeKind.tkUString,
    TTypeKind.tkString, TTypeKind.tkWChar, TTypeKind.tkWString]
  then
  begin
  if not Assigned(FMyClass) then
    FMyClass := TMyClass.Create(self);
  FMyClass.SomeString := Value.AsString;
  end else
    if Value.Kind = TTypeKind.tkClass then
      FMyClass := Value.AsType<TMyClass>;
end;

In this case both button clicks will work properly. In other words, it simulates := overloading:

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyClass := 'asd';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MyClass := TMyClass.Create(self);
end;

And here is how to get access to TMyClass instance:

procedure TForm1.Button3Click(Sender: TObject);
begin
  if Assigned(TMyClass(MyClass.AsObject)) then
    ShowMessage(TMyClass(MyClass.AsObject).SomeString)
  else
    ShowMessage('nil');
end;
asd-tm
  • 3,381
  • 2
  • 24
  • 41