6

I want to make the type of record that uses dynamic arrays.

Using the variables A and B of this type I want to be able to perform operations A: = B (and other) and be able to modify the content of A without modification B like in snipped code below:

    type
      TMyRec = record
        Inner_Array: array of double;
      public
        procedure SetSize(n: integer);
        class operator Implicit(source: TMyRec): TMyRec;
      end;

    implementation

    procedure TMyRec.SetSize(n: integer);
    begin
      SetLength(Inner_Array, n);
    end;

    class operator TMyRec.Implicit(source: TMyRec): TMyRec;
    begin
    //here I want to copy data from source to destination (A to B in my simple example below)
    //but here is the compilator error
    //[DCC Error] : E2521 Operator 'Implicit' must take one 'TMyRec' type in parameter or result type
    end;


    var
      A, B: TMyRec;
    begin
      A.SetSize(2);
      A.Inner_Array[1] := 1;
      B := A;
      A.Inner_Array[1] := 0;
//here are the same values inside A and B (they pointed the same inner memory)

There are two problems:

  1. when I don't use overriding assigning operator in my TMyRec, A:=B means A and B (their Inner_Array) are pointing the same place in memory.
  2. to avoid problem 1) I want to overload assign operator using:

    class operator TMyRec.Implicit(source: TMyRec): TMyRec;

but compilator (Delphi XE) says:

[DCC Error] : E2521 Operator 'Implicit' must take one 'TMyRec' type in parameter or result type

How to resolve this problems. I read several similar posts on stackoverflow but they don't work (if I understood they well) on my situation.

Artik

Artik
  • 803
  • 14
  • 36
  • 1
    Introduce a `TMyRec.Clone` function instead. – LU RD Jul 31 '13 at 20:15
  • 2
    You should make your `Inner_Array` immutable; code like `A.Inner_Array[1] := 1;` will be forbidden - any write to `Inner_Array` should create a new array instance. Read also http://sergworks.wordpress.com/2013/04/10/on-the-operator-overloading-in-delphi/ for some more hints. – kludg Aug 01 '13 at 02:25
  • @user246408 Immutable vector/matrix types are usually inconvenient and lead to poor performance. For example, many matrix algorithms operate in place. – David Heffernan Aug 01 '13 at 09:54
  • Thank you.I was convinced for your opinion. – Artik Aug 01 '13 at 11:52
  • [There is a way to catch assignment A:=B and copy data from A to B](https://stackoverflow.com/a/47320631/7579632), if you are willing to abuse the language. – Nasreddine Galfout Nov 16 '17 at 12:31
  • https://www.thedelphigeek.com/2015/01/implementing-record-assignment-operator_9.html – Gabriel Dec 21 '19 at 19:26
  • Related question: https://stackoverflow.com/questions/953151/can-you-overload-the-assignment-operator-for-delphi-records – Gabriel Jun 09 '22 at 10:02

2 Answers2

6

It is not possible to overload the assignment operator. This means that what you are attempting to do is not possible.


Edit: It is possible now - http://docwiki.embarcadero.com/RADStudio/Sydney/en/Custom_Managed_Records#The_Assign_Operator

Kromster
  • 7,181
  • 7
  • 63
  • 111
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Does it mean, there are no way to catch assignment A:=B and copy data from A to B? – Artik Jul 31 '13 at 20:25
  • 2
    It is possible to introduce a COW functionality on dynamic arrays. This will take care of the cloning when writing to the array, just as the `String` type works. It's a bit messy, but I managed to make it work. I will publish the code soon. Main thing that disturbs me is that intrinsic functions like `SetLength`,`Length`, etc are not possible to introduce as operator overloads. – LU RD Jul 31 '13 at 20:46
0

The only known way to do it is to use pointers, which is not safe, so you must understand what you're doing.

It's something like this:

type
  PMyRec = ^TMyRec;
  TMyRec = record
    MyString : string;
    class operator Implicit(aRec : PMyRec) : TMyRec;
  end;
....
class operator TMyRec.Implicit(aRec : PMyRec) : TMyRec;
begin
  if aRec = nil then // to do something...
    raise Exception.Create('Possible bug is here!');
  Result.MyString := aRec^.MyString;
end;

And the call example should look like this:

var
  aRec1, aRec2 : TMyRec;
begin
  aRec1.MyString := 'Hello ';
  aRec2.MyString := 'World';
  writeln(aRec1.MyStr, aRec2.MyStr);
  aRec2 := @aRec1;
  writeln(aRec1.MyStr, aRec2.MyStr);
end.