Recent

Author Topic: Question on copying dynamic arrays  (Read 512 times)

Bart

  • Hero Member
  • *****
  • Posts: 4921
    • Bart en Mariska's Webstek
Question on copying dynamic arrays
« on: February 08, 2023, 10:09:08 pm »
Hi,

This started out as a PEBCAK problem.
I have a datastructure wich a.o. contains some dynamic arrays.
I pass that datstructure around to another class (as a property), change the copy, only to find out that the original was altered as well.
Just to explain what happened with some pseudo code:
Code: Pascal  [Select][+][-]
  1.   Dlg := TDlg.Create;
  2.   Dlg.Options := GlobalOptions;
  3.   if Dlg.Execute then
  4.   begin
  5.     //at his point GlobalOptions had changed before I copied Dlg.Options back to GlobalOptions
  6.     //in fact this also happened if Execute returned false
  7.     ...
  8.     GlobalOptions := Dlg.Options;
  9.   end;
(T.b.h. it took me some time to figure out that this "unexpected"  behaviour only applied to the dynamic arrays, not to the other fields of the record.)

OK, I get why: the copy holds the pointer to the original dynamic array, the dynamic array contents are not copied.
So, I need to manually copy the contents to the destination.

To create a copy of the datastructure where actually the content of the dynamic array is copied, I can use OptionsCopy.DynamicArray := Copy(GlobalOptions.DynamicArray,Low(GlobalOptions.DynamicArray)).

So, I added a Assign() function to my datastructure (an advanced record).
The datstructure has many fields, the fast majority of them are not dynamic arrays.
I did not want to manually copy each and every field by hand (especially since the datastructure may get new fields in the future).

Here's basically how I implemented it:
Code: Pascal  [Select][+][-]
  1. type
  2.   TData = record
  3.     //some 20 odd fields of fixed records and strings
  4.     ....
  5.     Files: Array[TFileType] of TFile;  /TFileType is an enum, TFile is a dynamic array of some type that is not dynamical
  6.     procedure Assign(Src: TData);
  7.   end;
  8.  
  9. procedure TData.Assign(Src: TData);
  10. var
  11.   FT: TFileType;
  12. begin
  13.   Self := Src;
  14.   for FT := Low(TFileType) to High(TFileType) do
  15.     Self.Files[FT] := Copy(Src.Files[FT],Low(Src.Files[FT]));
  16. end;

While this seems to work in fine in some limited tests I did, the question is: is this safe?
Especially the "Self := Src" bit inside the TData.Assign() procedure?

Bart
« Last Edit: February 09, 2023, 04:54:58 pm by Bart »

Blaazen

  • Hero Member
  • *****
  • Posts: 3155
  • POKE 54296,15
    • Eye-Candy Controls
Re: Question on coping dynamic arrays
« Reply #1 on: February 09, 2023, 12:35:01 am »
I don't know whether it's safe but I know it's the first time I see Self on the left side of the := operator.  :)
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

Blaazen

  • Hero Member
  • *****
  • Posts: 3155
  • POKE 54296,15
    • Eye-Candy Controls
Re: Question on coping dynamic arrays
« Reply #2 on: February 09, 2023, 12:54:40 am »
While Assign() method usually looks like this:
Code: Pascal  [Select][+][-]
  1. procedure TGridColumnTitle.Assign(Source: TPersistent);
  2. begin
  3.   if Source is TGridColumnTitle then begin
  4.     Alignment := TGridColumnTitle(Source).Alignment;
  5.     Layout := TGridColumnTitle(Source).Layout;
  6.     Caption := TGridColumnTitle(Source).Caption;
  7.     Color := TGridColumnTitle(Source).Color;
  8.     Font := TGridColumnTitle(Source).Font;
  9.     ImageIndex := TGridColumnTitle(Source).ImageIndex;
  10.   end else
  11.     inherited Assign(Source);
  12. end;
your TData is not class but an advanced record so it's probably safe.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

jamie

  • Hero Member
  • *****
  • Posts: 5167
Re: Question on coping dynamic arrays
« Reply #3 on: February 09, 2023, 02:32:08 am »
I believe they work on the same level as Strings, they are managed and referenced counted maybe?

 In any case, if that were true then changing anything in the secondary array should trigger a unique dynamic array copy.

  But I think that maybe asking for too much and if you were to store that managed array for a rainy day, then I am sure it isn't going to be valid, just like a managed string.


  if it's a simple single element array why not use the MOVE to your new array to speed things up?

 Wonder if you can put properties in the record, it would make it much nicer looking.

  This is one reason I still like objects!
The only true wisdom is knowing you know nothing

Warfley

  • Hero Member
  • *****
  • Posts: 1073
Re: Question on coping dynamic arrays
« Reply #4 on: February 09, 2023, 04:05:16 pm »
To copy an array just call setlength on it:
Code: Pascal  [Select][+][-]
  1. CopyArray := OldArray;
  2. Setlength(CopyArray, Length(CopyArray));

The reason for this is that arrays are reference counted so the memory is shared when a simple assignment is used. But SetLength guarantees that after a call you have a unique copy (i.e. that the reference counter is 1)

This is the so called lazy copy principle in FPC (which applies to both strings and arrays, but is slightly different between those two)
« Last Edit: February 09, 2023, 04:06:59 pm by Warfley »

 

TinyPortal © 2005-2018