Recent

Author Topic: Forcing TVector To Copy Construct  (Read 1074 times)

del

  • Full Member
  • ***
  • Posts: 113
Forcing TVector To Copy Construct
« on: October 20, 2019, 08:54:50 am »
I have this utility that reads a file into a TStringList (bandList) and then populates an array to be used as a lookup table (lut) to cross reference YouTube ID with the band that's in the video. Simple enough - but I wanted to try TVector. The problem was that every time I "pushed back" new stuff, all the old stuff then pointed to (referenced) the new stuff. So I ended up with a table entirely populated with the last stuff entered. Not cool.

I read somewhere that std::vector automatically calls the copy constructor when objects are "pushed back". I was not seeing this behavior with TVector. So I put copy constructors where the objects would normally go and now every entry stays the way it was when first entered. MStr0D and VStr0D are just classes that contain UnicodeString and TVector and have functions which manage the member variables. "lut" is an instance of a class that manages a TVector of VStr0D. At any rate the main idea is to force copy construction during TVector push back.

Code: Pascal  [Select]
  1. for i := 0 to numBands - 1 do
  2. begin
  3.   tmpArr.Clear;
  4.   tmpStr.Assign(bandList[i]);
  5.   tmpStr.Extract('_', ':'); //extracts YouTube ID
  6.   tmpArr.PushBack(MStr0D.Copy(tmpStr)); //copy constructor as parameter
  7.   tmpStr.Assign(bandList[i]);
  8.   tmpStr.Extract('"', '"'); //extracts band name
  9.   tmpArr.PushBack(MStr0D.Copy(tmpStr));
  10.   lut.PushBack(VStr0D.Copy(tmpArr)); //copy constructor as parameter
  11. end;
  12.  

This was driving me crazy but I learned stuff. Here's the last few rows of the result.

Quote
1899 lut.V1D[1899].V0D[0].S0D 43434 oIIxlgcuQRU lut.V1D[1899].V0D[1].S0D 87943 Yeah Yeah Yeahs
1900 lut.V1D[1900].V0D[0].S0D 21024 vo4rbhR_vsk lut.V1D[1900].V0D[1].S0D 87466 Yeasayer
1901 lut.V1D[1901].V0D[0].S0D 37200 MVVsUMNMgLg lut.V1D[1901].V0D[1].S0D 65296 Yellow Days
1902 lut.V1D[1902].V0D[0].S0D 44829 -OB0PJEreus lut.V1D[1902].V0D[1].S0D 14347 Yo La Tengo
1903 lut.V1D[1903].V0D[0].S0D 81989 UIkMeaAfIRw lut.V1D[1903].V0D[1].S0D 14427 Yo La Tengo
1904 lut.V1D[1904].V0D[0].S0D 31283 O9VzggHUzMo lut.V1D[1904].V0D[1].S0D 18929 Yolks, The
1905 lut.V1D[1905].V0D[0].S0D 74755 ni4DHoNmnsI lut.V1D[1905].V0D[1].S0D 12977 You Am I
1906 lut.V1D[1906].V0D[0].S0D 30465 blNCvZG_LT4 lut.V1D[1906].V0D[1].S0D 18158 Young Marble Giants
1907 lut.V1D[1907].V0D[0].S0D 58282 hxvZ65K2IA4 lut.V1D[1907].V0D[1].S0D 33707 Young Prisms
1908 lut.V1D[1908].V0D[0].S0D 85286 tbG-wR-oXq8 lut.V1D[1908].V0D[1].S0D 44343 Young, The
1909 lut.V1D[1909].V0D[0].S0D 64935 Ta3OsyNCuvs lut.V1D[1909].V0D[1].S0D 4229 Zella Day

Each MStr0D object was given a random number (0 to 99999) during its construction so I could track it. I like it but I'm definitely open to other approaches (that specifically use TVector), comments regarding nuances, leaks, ownership, etc.



Thaddy

  • Hero Member
  • *****
  • Posts: 9183
Re: Forcing TVector To Copy Construct
« Reply #1 on: October 20, 2019, 09:02:03 am »
Unless you want a true copy of a living object that has already been manipulated, there is no need to mimic a copy constructor in Object Pascal.
also related to equus asinus.

del

  • Full Member
  • ***
  • Posts: 113
Re: Forcing TVector To Copy Construct
« Reply #2 on: October 20, 2019, 12:51:33 pm »
Unless you want a true copy of a living object that has already been manipulated, there is no need to mimic a copy constructor in Object Pascal.

Do you guys do "clones"? Sometimes you need a clone.
 :D


marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7503
Re: Forcing TVector To Copy Construct
« Reply #3 on: October 20, 2019, 01:03:41 pm »
Unless you want a true copy of a living object that has already been manipulated, there is no need to mimic a copy constructor in Object Pascal.

Do you guys do "clones"? Sometimes you need a clone.
 :D

https://stackoverflow.com/questions/4041760/correct-way-to-duplicate-delphi-object/4041906#4041906

Note that maybe nowadays this could be aided by a tobject helper?

PascalDragon

  • Hero Member
  • *****
  • Posts: 673
  • Compiler Developer
Re: Forcing TVector To Copy Construct
« Reply #4 on: October 21, 2019, 09:17:46 am »
I have this utility that reads a file into a TStringList (bandList) and then populates an array to be used as a lookup table (lut) to cross reference YouTube ID with the band that's in the video. Simple enough - but I wanted to try TVector. The problem was that every time I "pushed back" new stuff, all the old stuff then pointed to (referenced) the new stuff. So I ended up with a table entirely populated with the last stuff entered. Not cool.

I read somewhere that std::vector automatically calls the copy constructor when objects are "pushed back". I was not seeing this behavior with TVector. So I put copy constructors where the objects would normally go and now every entry stays the way it was when first entered. MStr0D and VStr0D are just classes that contain UnicodeString and TVector and have functions which manage the member variables. "lut" is an instance of a class that manages a TVector of VStr0D. At any rate the main idea is to force copy construction during TVector push back.
The idea is that you need to create a new instance of MStr0D and VStr0D each time you fill it anew. Pseudo Code:

Code: Pascal  [Select]
  1. for i := 0 to numBands - 1 do
  2. begin
  3.   tmpArr := TVStr0D.Create;
  4.   tmpStr := TMStr0D.Create;
  5.   // set up tmpStr
  6.   tmpArr.PushBack(tmpStr);
  7.   tmpStr := TMStr0D.Create;
  8.   // set up tmpStr again
  9.   tmpArr.PushBack(tmpStr);
  10.   lut.PushBack(tmpArr);
  11. end;
  12.  

That's how it works in Object Pascal. Don't try to apply C++-isms there.

del

  • Full Member
  • ***
  • Posts: 113
Re: Forcing TVector To Copy Construct
« Reply #5 on: October 21, 2019, 08:13:34 pm »
I have this utility that reads a file into a TStringList (bandList) and then populates an array to be used as a lookup table (lut) to cross reference YouTube ID with the band that's in the video. Simple enough - but I wanted to try TVector. The problem was that every time I "pushed back" new stuff, all the old stuff then pointed to (referenced) the new stuff. So I ended up with a table entirely populated with the last stuff entered. Not cool.

I read somewhere that std::vector automatically calls the copy constructor when objects are "pushed back". I was not seeing this behavior with TVector. So I put copy constructors where the objects would normally go and now every entry stays the way it was when first entered. MStr0D and VStr0D are just classes that contain UnicodeString and TVector and have functions which manage the member variables. "lut" is an instance of a class that manages a TVector of VStr0D. At any rate the main idea is to force copy construction during TVector push back.
The idea is that you need to create a new instance of MStr0D and VStr0D each time you fill it anew. Pseudo Code:

Code: Pascal  [Select]
  1. for i := 0 to numBands - 1 do
  2. begin
  3.   tmpArr := TVStr0D.Create;
  4.   tmpStr := TMStr0D.Create;
  5.   // set up tmpStr
  6.   tmpArr.PushBack(tmpStr);
  7.   tmpStr := TMStr0D.Create;
  8.   // set up tmpStr again
  9.   tmpArr.PushBack(tmpStr);
  10.   lut.PushBack(tmpArr);
  11. end;
  12.  

That's how it works in Object Pascal. Don't try to apply C++-isms there.
I'll do whatever it takes to get the shit to work. Did you test your idea?

PascalDragon

  • Hero Member
  • *****
  • Posts: 673
  • Compiler Developer
Re: Forcing TVector To Copy Construct
« Reply #6 on: October 22, 2019, 09:13:46 am »
I'll do whatever it takes to get the shit to work. Did you test your idea?
Obviously I can't test it, cause I don't have your full source, only a snippet. But unlike you I took the time to learn how the language behaves and thus can provide code that points in the right direction even if it might not compile right away.

del

  • Full Member
  • ***
  • Posts: 113
Re: Forcing TVector To Copy Construct
« Reply #7 on: October 30, 2019, 08:27:25 pm »
I'm pretty sure I tried it your way cuz that's one of the obvious ways of doing it. But IIRC when I freed the objects in the loop it did crazy things in the TVector. It was if the most recent "push_back" was still tied to the object, and all the previous "push_backs" were still daisy chained to the most recent one. So it was basically a chain reaction of SNAFU that wiped out the whole vector. Not what I wanted. And if I didn't free the objects, but instead reused them, then a similar chain reaction would populate the entire vector with contents identical to the most recent "push_back". Not at all what I wanted

Why does this matter? Cuz if you don't have a vector that can load objects and protect the data, then the TVector is pretty useless. That's the whole point of these vector classes. Easy use and maintenance of arrays of complicated objects. OK - on to the experiment. If your idea works there will be nobody on this planet more happy than me. Stay tuned.

del

  • Full Member
  • ***
  • Posts: 113
Re: Forcing TVector To Copy Construct
« Reply #8 on: October 30, 2019, 09:47:39 pm »
OK I'm back. Here's my technique. First the code:

Code: Pascal  [Select]
  1. if (FileExists(crossRef)) then
  2. begin
  3.   doBands := True;
  4.   bandList := TStringList.Create;
  5.   bandList.LoadFromFile(crossRef);
  6.   lut := VStr1D.Create;
  7.  
  8.   for i := 0 to bandList.Count - 1 do
  9.   begin
  10.     tmpArr.Clear;
  11.     tmpStr.Assign(bandList[i]);
  12.     tmpStr.Extract('_', ':'); //extracts YouTube ID
  13.     tmpArr.PushBack(MStr0D.Clone(tmpStr)); //clone as parameter
  14.     tmpStr.Assign(bandList[i]);
  15.     tmpStr.Extract('"', '"'); //extracts band name
  16.     tmpArr.PushBack(MStr0D.Clone(tmpStr)); //clone as parameter
  17.     lut.PushBack(VStr0D.Clone(tmpArr)); //clone as parameter
  18.   end;
  19.  
  20.   for i := bandList.Count - 16 to bandList.Count - 4 do
  21.   begin
  22.     writeln(lut.V1D[i].V0D[0].S0D, '  ', lut.V1D[i].V0D[1].S0D);
  23.   end;
  24.  
  25.   bandList.Free;
  26. end;
  27.  

I renamed my copy constructor "clone". Clone sounds less C++ ish and we don't want anybody getting unhinged. OK - here are the results:

Quote
jMMkP_ofpXg  Yeah Yeah Yeahs
oIIxlgcuQRU  Yeah Yeah Yeahs
vo4rbhR_vsk  Yeasayer
MVVsUMNMgLg  Yellow Days
-OB0PJEreus  Yo La Tengo
UIkMeaAfIRw  Yo La Tengo
vVIs3WySZ7U  Yo La Tengo
O9VzggHUzMo  Yolks, The
ni4DHoNmnsI  You Am I
blNCvZG_LT4  Young Marble Giants
hxvZ65K2IA4  Young Prisms
tbG-wR-oXq8  Young, The
Ta3OsyNCuvs  Zella Day

That's exactly correct. Here's the original data, with the delimiters:

Quote
_jMMkP_ofpXg: "Yeah Yeah Yeahs"
_oIIxlgcuQRU: "Yeah Yeah Yeahs"
_vo4rbhR_vsk: "Yeasayer"
_MVVsUMNMgLg: "Yellow Days"
_-OB0PJEreus: "Yo La Tengo"
_UIkMeaAfIRw: "Yo La Tengo"
_vVIs3WySZ7U: "Yo La Tengo"
_O9VzggHUzMo: "Yolks, The"
_ni4DHoNmnsI: "You Am I"
_blNCvZG_LT4: "Young Marble Giants"
_hxvZ65K2IA4: "Young Prisms"
_tbG-wR-oXq8: "Young, The"
_Ta3OsyNCuvs: "Zella Day"

And here's my implementation of Le Technique Dragon:

Code: Pascal  [Select]
  1. if (FileExists(crossRef)) then
  2. begin
  3.   doBands := True;
  4.   bandList := TStringList.Create;
  5.   bandList.LoadFromFile(crossRef);
  6.   lut := VStr1D.Create;
  7.  
  8.   for i := 0 to bandList.Count - 1 do
  9.   begin
  10.     tmpStr.Free;
  11.     tmpStr := MStr0D.Create(bandList[i]);
  12.     tmpStr.Extract('_', ':'); //extracts YouTube ID
  13.     tmpArr.Free;
  14.     tmpArr := VStr0D.Create;
  15.     tmpArr.PushBack(tmpStr);
  16.     tmpStr.Free;
  17.     tmpStr := MStr0D.Create(bandList[i]);
  18.     tmpStr.Extract('"', '"'); //extracts band name
  19.     tmpArr.PushBack(tmpStr);
  20.     lut.PushBack(tmpArr);
  21.   end;
  22.  
  23.   for i := bandList.Count - 16 to bandList.Count - 4 do
  24.   begin
  25.     writeln(lut.V1D[i].V0D[0].S0D, '  ', lut.V1D[i].V0D[1].S0D);
  26.   end;
  27.  
  28.   bandList.Free;
  29. end;
  30.  

Let's look at the results ...

Quote
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day
Zella Day  Zella Day

So it's like a chain of references all pointing to the most recent "push_back". Anyhoo - I'm gonna stick with my "clone" technique for the time being. I'm guessing that the temporary variables are "owned" by the array. And get freed by the array (sent to the destructor for those classes) during its own destruction.



bytebites

  • Full Member
  • ***
  • Posts: 213
Re: Forcing TVector To Copy Construct
« Reply #9 on: October 31, 2019, 07:54:10 am »
Neither code compiles, but tmpStr.Free and tmpArr.Free seem wrong in the latter.

PascalDragon

  • Hero Member
  • *****
  • Posts: 673
  • Compiler Developer
Re: Forcing TVector To Copy Construct
« Reply #10 on: October 31, 2019, 09:06:05 am »
And here's my implementation of Le Technique Dragon:
It's not, because I never said anything about using Free, for good reason: TVector<> does not take any ownership of the object reference (for something like that use e.g. TFPGObjectList<> from unit fgl or TObjectList<> from unit Generics.Collections), thus when you call Free on either tmpStr or tmpArr you release also the instance stored in the vector. The memory manager will then reuse the address, thus all entries in the vector appear to point to the same instance.

The important difference to C++ you don't seem to know/understand is that in Object Pascal all variables of type class are essentially (in C++ lingo) a TMyClass*.

So it's like a chain of references all pointing to the most recent "push_back". Anyhoo - I'm gonna stick with my "clone" technique for the time being. I'm guessing that the temporary variables are "owned" by the array. And get freed by the array (sent to the destructor for those classes) during its own destruction.

You're wrong. You leak not only the instances you pushed into the vector, but also the two instances to tmpStr and tmpArr, because the vector does not own them and doesn't even know about the destructor. This would only be true of your tmpStr and tmpArr would be of type record instead of class. You can compile your code with -gh to see this.

Thaddy

  • Hero Member
  • *****
  • Posts: 9183
Re: Forcing TVector To Copy Construct
« Reply #11 on: October 31, 2019, 09:38:33 am »
I found a quite clear Cow example for arrays. The technique used can also be used for your vector class.
Code: Pascal  [Select]
  1. {$mode delphi}
  2. { Copy on write example based on code by Barry Kelly }
  3. uses SysUtils;
  4.  
  5. type
  6.   TArray<T> = array of T;
  7.  
  8.   ICowArrayData<T> = interface
  9.     function GetLength: Integer;
  10.     function MutableClone: ICowArrayData<T>;
  11.     function GetItem(Index: Integer): T;
  12.     procedure SetItem(Index: Integer; const Value: T);
  13.     function ToArray: TArray<T>;
  14.   end;
  15.  
  16.   TCowArrayData<T> = class(TInterfacedObject, ICowArrayData<T>)
  17.   private
  18.     FData: TArray<T>;
  19.   public
  20.     constructor Create(const Data: TArray<T>);
  21.     function GetLength: Integer;
  22.     function MutableClone: ICowArrayData<T>;
  23.     function GetItem(Index: Integer): T;
  24.     procedure SetItem(Index: Integer; const Value: T);
  25.     function ToArray: TArray<T>;
  26.   end;
  27.  
  28.   TCowArray<T> = record
  29.   private
  30.     FData: ICowArrayData<T>;
  31.     function GetItems(Index: Integer): T;
  32.     procedure SetItems(Index: Integer; const Value: T);
  33.     function GetLength: Integer;
  34.   public
  35.     constructor Create(const Data: TArray<T>); overload;
  36.     constructor Create(const Data: array of T); overload;
  37.     property Items[Index: Integer]: T read GetItems write SetItems; default;
  38.     property Length: Integer read GetLength;
  39.     function ToArray: TArray<T>;
  40.     class operator Add(const Left, Right: TCowArray<T>): TCowArray<T>;
  41.   end;
  42.  
  43. { TCowArray<T> }
  44.  
  45. class operator TCowArray<T>.Add(const Left, Right: TCowArray<T>): TCowArray<T>;
  46. var
  47.   resultArr: TArray<T>=[];
  48.   i: Integer;
  49. begin
  50.   SetLength(resultArr, Left.Length + Right.Length);
  51.   for i := 0 to Left.Length - 1 do
  52.     resultArr[i] := Left[i];
  53.   for i := 0 to Right.Length - 1 do
  54.     resultArr[Left.Length + i] := Right[i];
  55.   Result := TCowArray<T>.Create(resultArr);
  56. end;
  57.  
  58. constructor TCowArray<T>.Create(const Data: TArray<T>);
  59. begin
  60.   if Data = nil then
  61.     FData := nil
  62.   else
  63.     FData := TCowArrayData<T>.Create(Data);
  64. end;
  65.  
  66. constructor TCowArray<T>.Create(const Data: array of T);
  67. var
  68.   arr: TArray<T> =[];
  69.   i: Integer;
  70. begin
  71.   if System.Length(Data) = 0 then
  72.     FData := nil
  73.   else
  74.   begin
  75.     SetLength(arr, System.Length(Data));
  76.     for i := 0 to System.Length(Data) - 1 do
  77.       arr[i] := Data[i];
  78.     FData := TCowArrayData<T>.Create(arr);
  79.   end;
  80. end;
  81.  
  82. function TCowArray<T>.GetItems(Index: Integer): T;
  83. begin
  84.   Result := FData.GetItem(Index);
  85. end;
  86.  
  87. function TCowArray<T>.GetLength: Integer;
  88. begin
  89.   if FData = nil then
  90.     Exit(0);
  91.   Result := FData.GetLength;
  92. end;
  93.  
  94. procedure TCowArray<T>.SetItems(Index: Integer; const Value: T);
  95. begin
  96.   FData := FData.MutableClone;
  97.   FData.SetItem(Index, Value);
  98. end;
  99.  
  100. function TCowArray<T>.ToArray: TArray<T>;
  101. begin
  102.   if FData = nil then
  103.     Exit(nil);
  104.   Result := FData.ToArray;
  105. end;
  106.  
  107. { TCowArrayData<T> }
  108.  
  109. constructor TCowArrayData<T>.Create(const Data: TArray<T>);
  110. begin
  111.   FData := Data;
  112. end;
  113.  
  114. function TCowArrayData<T>.GetItem(Index: Integer): T;
  115. begin
  116.   Result := FData[Index];
  117. end;
  118.  
  119. function TCowArrayData<T>.GetLength: Integer;
  120. begin
  121.   Result := Length(FData);
  122. end;
  123.  
  124. function TCowArrayData<T>.MutableClone: ICowArrayData<T>;
  125. begin
  126.   if RefCount = 1 then
  127.     Exit(Self);
  128.   Result := TCowArrayData<T>.Create(ToArray);
  129. end;
  130.  
  131. procedure TCowArrayData<T>.SetItem(Index: Integer; const Value: T);
  132. begin
  133.   FData[Index] := Value;
  134. end;
  135.  
  136. function TCowArrayData<T>.ToArray: TArray<T>;
  137. var
  138.   i: Integer;
  139. begin
  140.   Result:=[];
  141.   SetLength(Result, Length(FData));
  142.   for i := 0 to Length(FData) - 1 do
  143.     Result[i] := FData[i];
  144. end;
  145.  
  146. procedure WriteArray(const Msg: string; Arr: TCowArray<Integer>);
  147. var
  148.   i: Integer;
  149. begin
  150.   Write(Msg, ':');
  151.   for i := 0 to Arr.Length - 1 do
  152.     Write(' ', Arr[i]);
  153.   Writeln;
  154. end;
  155.  
  156. var
  157.   x, y: TCowArray<Integer>;
  158. begin
  159.   try
  160.     x := TCowArray<Integer>.Create([1, 2, 3]);
  161.     y := x;
  162.  
  163.     Writeln('Starting out, both x and y refer to same instance data');
  164.     WriteArray('x', x);
  165.     WriteArray('y', y);
  166.  
  167.     Writeln('Modifying x; note that y doesn''t change:');
  168.     x[1] := 42;
  169.     WriteArray('x', x);
  170.     WriteArray('y', y);
  171.  
  172.     // Add operator as concatenation
  173.     Writeln('Concatenation:');
  174.     y := x + y;
  175.     WriteArray('x', x);
  176.     WriteArray('y', y);
  177.    
  178.   except
  179.     on E: Exception do
  180.       Writeln(E.ClassName, ': ', E.Message);
  181.   end;
  182.   end.
also related to equus asinus.

del

  • Full Member
  • ***
  • Posts: 113
Re: Forcing TVector To Copy Construct
« Reply #12 on: October 31, 2019, 03:33:26 pm »
And here's my implementation of Le Technique Dragon:
... I never said anything about using Free, for good reason: TVector<> does not take any ownership of the object reference (for something like that use e.g. TFPGObjectList<> from unit fgl or TObjectList<> from unit Generics.Collections), thus when you call Free on either tmpStr or tmpArr you release also the instance stored in the vector. The memory manager will then reuse the address, thus all entries in the vector appear to point to the same instance.

Here's what you said to do:

Code: Pascal  [Select]
  1. for i := 0 to numBands - 1 do
  2. begin
  3.   tmpArr := TVStr0D.Create;
  4.   tmpStr := TMStr0D.Create;
  5.   // set up tmpStr
  6.   tmpArr.PushBack(tmpStr);
  7.   tmpStr := TMStr0D.Create;
  8.   // set up tmpStr again
  9.   tmpArr.PushBack(tmpStr);
  10.   lut.PushBack(tmpArr);
  11. end;
  12.  

I'll try that. I just thought creating the same object over and over again in a loop without ever freeing it was odd. And we KNOW it's not falling out of scope at the bottom of the loop, and we know that even if it did it's destructor would not be called. So yeah - all bets are off. I'll give it a shot.

del

  • Full Member
  • ***
  • Posts: 113
Re: Forcing TVector To Copy Construct
« Reply #13 on: October 31, 2019, 03:52:39 pm »
Well I'll be damned ...

Code: Pascal  [Select]
  1. if (FileExists(crossRef)) then
  2. begin
  3.   doBands := True;
  4.   bandList := TStringList.Create;
  5.   bandList.LoadFromFile(crossRef);
  6.   lut := VStr1D.Create;
  7.  
  8.   for i := 0 to bandList.Count - 1 do
  9.   begin
  10.     tmpArr := VStr0D.Create;
  11.     tmpStr := MStr0D.Create(bandList[i]);
  12.     tmpStr.Extract('_', ':'); //extracts YouTube ID
  13.     tmpArr.PushBack(tmpStr);
  14.     tmpStr := MStr0D.Create(bandList[i]);
  15.     tmpStr.Extract('"', '"'); //extracts band name
  16.     tmpArr.PushBack(tmpStr);
  17.     lut.PushBack(tmpArr);
  18.   end;
  19.  
  20.   for i := bandList.Count - 16 to bandList.Count - 4 do
  21.   begin
  22.     writeln(lut.V1D[i].V0D[0].S0D, '  ', lut.V1D[i].V0D[1].S0D);
  23.   end;
  24.  
  25.   bandList.Free;
  26. end;
  27.  
  28.  

Results:

Quote
jMMkP_ofpXg  Yeah Yeah Yeahs
oIIxlgcuQRU  Yeah Yeah Yeahs
vo4rbhR_vsk  Yeasayer
MVVsUMNMgLg  Yellow Days
-OB0PJEreus  Yo La Tengo
UIkMeaAfIRw  Yo La Tengo
vVIs3WySZ7U  Yo La Tengo
O9VzggHUzMo  Yolks, The
ni4DHoNmnsI  You Am I
blNCvZG_LT4  Young Marble Giants
hxvZ65K2IA4  Young Prisms
tbG-wR-oXq8  Young, The
Ta3OsyNCuvs  Zella Day

PascalDragon

  • Hero Member
  • *****
  • Posts: 673
  • Compiler Developer
Re: Forcing TVector To Copy Construct
« Reply #14 on: October 31, 2019, 05:06:52 pm »
I'll try that. I just thought creating the same object over and over again in a loop without ever freeing it was odd. And we KNOW it's not falling out of scope at the bottom of the loop, and we know that even if it did it's destructor would not be called. So yeah - all bets are off. I'll give it a shot.
It's because you're dealing with pointers: the reference to the instance is still contained in the vector, thus one must not free it (and it's also not leaked - yet). It is essentially the same you do with your Clone call, but without the copying and the need for the two local variables you reset each time.

Well I'll be damned ...

Code: Pascal  [Select]
  1. if (FileExists(crossRef)) then
  2. begin
  3.   doBands := True;
  4.   bandList := TStringList.Create;
  5.   bandList.LoadFromFile(crossRef);
  6.   lut := VStr1D.Create;
  7.  
  8.   for i := 0 to bandList.Count - 1 do
  9.   begin
  10.     tmpArr := VStr0D.Create;
  11.     tmpStr := MStr0D.Create(bandList[i]);
  12.     tmpStr.Extract('_', ':'); //extracts YouTube ID
  13.     tmpArr.PushBack(tmpStr);
  14.     tmpStr := MStr0D.Create(bandList[i]);
  15.     tmpStr.Extract('"', '"'); //extracts band name
  16.     tmpArr.PushBack(tmpStr);
  17.     lut.PushBack(tmpArr);
  18.   end;
  19.  
  20.   for i := bandList.Count - 16 to bandList.Count - 4 do
  21.   begin
  22.     writeln(lut.V1D[i].V0D[0].S0D, '  ', lut.V1D[i].V0D[1].S0D);
  23.   end;
  24.  
  25.   bandList.Free;
  26. end;
  27.  
  28.  

Results:

Quote
jMMkP_ofpXg  Yeah Yeah Yeahs
oIIxlgcuQRU  Yeah Yeah Yeahs
vo4rbhR_vsk  Yeasayer
MVVsUMNMgLg  Yellow Days
-OB0PJEreus  Yo La Tengo
UIkMeaAfIRw  Yo La Tengo
vVIs3WySZ7U  Yo La Tengo
O9VzggHUzMo  Yolks, The
ni4DHoNmnsI  You Am I
blNCvZG_LT4  Young Marble Giants
hxvZ65K2IA4  Young Prisms
tbG-wR-oXq8  Young, The
Ta3OsyNCuvs  Zella Day
Told you so. :P

Now you only need to make sure that you Free all instances contained in your lut and each VStr0D-instance inside that lut once you're done with it to avoid a memory leak (which would also exist with your approach).