Forum > General

Question on copying dynamic arrays

(1/1)

Bart:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---  Dlg := TDlg.Create;  Dlg.Options := GlobalOptions;  if Dlg.Execute then  begin    //at his point GlobalOptions had changed before I copied Dlg.Options back to GlobalOptions    //in fact this also happened if Execute returned false    ...    GlobalOptions := Dlg.Options;  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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TData = record    //some 20 odd fields of fixed records and strings    ....    Files: Array[TFileType] of TFile;  /TFileType is an enum, TFile is a dynamic array of some type that is not dynamical    procedure Assign(Src: TData);  end; procedure TData.Assign(Src: TData);var  FT: TFileType;begin  Self := Src;  for FT := Low(TFileType) to High(TFileType) do    Self.Files[FT] := Copy(Src.Files[FT],Low(Src.Files[FT]));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

Blaazen:
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.  :)

Blaazen:
While Assign() method usually looks like this:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TGridColumnTitle.Assign(Source: TPersistent);begin  if Source is TGridColumnTitle then begin    Alignment := TGridColumnTitle(Source).Alignment;    Layout := TGridColumnTitle(Source).Layout;    Caption := TGridColumnTitle(Source).Caption;    Color := TGridColumnTitle(Source).Color;    Font := TGridColumnTitle(Source).Font;    ImageIndex := TGridColumnTitle(Source).ImageIndex;  end else    inherited Assign(Source);end;your TData is not class but an advanced record so it's probably safe.

jamie:
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!

Warfley:
To copy an array just call setlength on it:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---CopyArray := OldArray;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)

Navigation

[0] Message Index

Go to full version