Recent

Author Topic: Array of Objects and Use [SOLVED]  (Read 860 times)

SandyG

  • Jr. Member
  • **
  • Posts: 92
Array of Objects and Use [SOLVED]
« on: August 01, 2024, 05:06:56 am »
Ran into something that I don't think I'm doing correctly. I think I'm not good with this code, I got part of it working then now crashing the IDE when installing the component. I backed out most all of the code except the declarations and publishing, then took out publishing the new props and hard crash on the IDE. I figure it was the array code. What is ODD is that with the array of TDTBandsSettings that seems to work OK as is showing below, but when adding the TDTTextSettings and related Bitmap, it blows up hard when restarting the IDE on a test project with the component. Just want to make sure the way I'm dealing with arrays is that the objects are just references and it's OK to assign and deal with the way I did it.

Being lazy and wanting to move ahead with a component I'm working on I needed a few of the same item for setting properties. These are things that are derived from TPersistant so I can see them in the Object Inspector. I see examples of using TCollection and creating an editor but for now not going to do that as it's a bit more work than I can do.

So I just created individual properties for each one (I hate this, but at least can keep me moving).

I have some declarations in my class and related helpers, cons dtors like this -
(Question below the code block)

Code: Pascal  [Select][+][-]
  1. ...
  2.   TDTCustomThemedGauge = class(TGraphicControl)
  3.   private
  4.     { Private declarations }
  5.  
  6.     FDirty: boolean;
  7. ...
  8.     // Bands
  9.     FBandsSettings: array[0..3] of TDTBandSettings;
  10.     FTextsSettings:  array[0..2] of TDTTextSettings;
  11.  
  12.     // bitmaps
  13.     FTextsBitmaps: array[0..2] of TBGRABitmap;
  14. ...
  15. // these get published
  16.     property TextSettings1: TDTTextSettings read FTextsSettings[0] write SetTextSettings1;
  17.     property TextSettings2: TDTTextSettings read FTextsSettings[1] write SetTextSettings2;
  18.     property TextSettings3: TDTTextSettings read FTextsSettings[2] write SetTextSettings3;
  19.  
  20.     property FrameSettings: TDTFrameSettings read FFrameSettings write SetFrameSettings;
  21.     property ScaleSettings: TDTScaleSettings read FScaleSettings write SetScaleSettings;
  22.     property BandSettings1: TDTBandSettings read FBandsSettings[0] write SetBandSettings1;
  23.     property BandSettings2: TDTBandSettings read FBandsSettings[1] write SetBandSettings2;
  24.     property BandSettings3: TDTBandSettings read FBandsSettings[2] write SetBandSettings3;
  25.     property BandSettings4: TDTBandSettings read FBandsSettings[3] write SetBandSettings4;
  26. ...
  27.  
  28. implementation
  29.  
  30. constructor TDTCustomThemedGauge.Create(AOwner: TComponent);
  31. var
  32.     i: integer;
  33.  
  34. begin
  35.   inherited Create(AOwner);
  36.  
  37.   for i := low(FBandsSettings) to high(FBandsSettings) do
  38.   begin
  39.     FBandsSettings[i] := TDTBandSettings.Create;
  40.     FBandsSettings[i].OnChange := DoChange;
  41.   end;
  42.  
  43.   for i := low(FTextsSettings) to high(FTextsSettings) do
  44.   begin
  45.     FTextsSettings[i] := TDTTextSettings.Create;
  46.     FTextsSettings[i].OnChange := DoChange;
  47.     FTextsBitmaps[i] := TBGRABitmap.Create;
  48.   end;
  49. ...
  50. end;
  51.  
  52. destructor TDTCustomThemedGauge.Destroy;
  53. var
  54.     i: integer;
  55. begin
  56.  
  57.   for i := low(FTextsSettings) to high(FTextsSettings) do
  58.   begin
  59.     FTextsSettings[i].OnChange := nil;
  60.     FTextsSettings[i].FontEx.OnChange := nil;
  61.     FTextsSettings[i].Free;
  62.     FTextsBitmaps[i].Free;
  63.   end;
  64.  
  65.   for i := low(FBandsSettings) to high(FBandsSettings) do
  66.   begin
  67.     FBandsSettings[i].OnChange := nil;
  68.     FBandsSettings[i].Free;
  69.   end;
  70. ...
  71.   inherited Destroy;
  72. end;
  73.  
  74. procedure TDTCustomThemedGauge.SetTextSettings1(AValue: TDTTextSettings);
  75. begin
  76.   if FTextsSettings[0] = AValue then // may not make sense for an object, but wtf
  77.     Exit;
  78.  
  79.   FTextsSettings[0] := AValue;
  80.   FTextsSettings[0].Dirty := True;  // set it, as it will need a repaint
  81.   FAnyTextDirty := True;
  82.  
  83.   DoChange(self);
  84. end;
  85. ...
  86. procedure TDTCustomThemedGauge.SetBandSettings1(AValue: TDTBandSettings);
  87. begin
  88.   if FBandsSettings[0] = AValue then
  89.     Exit;
  90.  
  91.   FBandsSettings[0] := AValue;
  92.   FBandsSettings[0].Dirty := True;
  93.   FAnyBandDirty:= true;
  94.  
  95.   DoChange(self);
  96. end;
  97. ...

So the question is do these variable declarations instantiate the objects or just create an array of references like I want?

I ran into a situation where I have an IDE crashing bug that I can't quite find and presume it's related to the changes to using an array vs. a single value. Rest of the code was modified to use array indexes as needed. I have rebuilt all related code in the BGRA Libs which this is part of just to make sure. And anything else to make sure it's not a build issue.

Image is of the Object Inspector with it working when ONLY the TDTBandSettings are created, everything seems to work fine, but I'm very suspicious of a False positive.

Lastly any limitations on the number of properties a component can have. A long long time ago Delphi had some small limit as I recall, but any limits of counts properties for a component?

Rolling back code, and going to do it one step at a time, but any advice on the hack job of code to get multiple-props going would be helpful.

Thanks

Sandy
« Last Edit: August 02, 2024, 03:21:03 am by SandyG »

TRon

  • Hero Member
  • *****
  • Posts: 3254
Re: Array of Objects and Use
« Reply #1 on: August 01, 2024, 05:41:45 am »
So the question is do these variable declarations instantiate the objects or just create an array of references like I want?
The arrays are arrays of references.

Which raises eyebrows when seeing something like this:
Code: Pascal  [Select][+][-]
  1.   // create
  2.   for i := low(FTextsSettings) to high(FTextsSettings) do
  3.   begin
  4.     FTextsSettings[i] := TDTTextSettings.Create;
  5.   end
  6.   ...
  7.  
  8.   // destroy
  9.   for i := low(FTextsSettings) to high(FTextsSettings) do
  10.   begin
  11.     FTextsSettings[i].Free;
  12.   end.
  13.  

And at the same time doing this:
Code: Pascal  [Select][+][-]
  1. procedure TDTCustomThemedGauge.SetTextSettings1(AValue: TDTTextSettings);
  2. begin
  3.   if FTextsSettings[0] = AValue then // may not make sense for an object, but wtf
  4.     Exit;
  5.  
  6.   FTextsSettings[0] := AValue;   // reference to oldvalue FTextsSettings[0] is now thrown into oblivion
  7.   ...
  8. end;
  9.  

It might very well be that your bigger project accounts for that but it isn't clear from the posted snippet. And also note that the reference that is gone still has some events coupled as well.
All software is open source (as long as you can read assembler)

SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #2 on: August 01, 2024, 06:14:33 am »
OK, at least it's a reference as I thought.

Yes, I have to think the same thing that if an entire object reference is assigned and not removed and destroyed might be a problem. In my case the created objects (TDTBandSettings or TDTTextSettings) are only created and destroyed internally by the ctor and dtor. And I think since these are based on TPersistant the copy operator does it by a deep copy not just moving the reference when copying, which I hope is the same when passing it to the SetXXXSetting calls. But could be way off, and I guess even the user can mess up the reference since the property is public.

So things like

Code: Pascal  [Select][+][-]
  1.    
  2.  
  3. // The only internal place were the objects are created are in the  ctor and dtor but I guess
  4. // the user could mess the references by assigning unless TPersistent is doing a deep copy not just overwriting the reference.
  5.  
  6.     property TextSettings1: TDTTextSettings read FTextsSettings[0] write SetTextSettings1;
  7. // and
  8.     procedure TDTCustomThemedGauge.SetTextSettings1(AValue: TDTTextSettings);
  9.     begin
  10.       if FTextsSettings[0] = AValue then // Does this compare the reference or since TPersistent actually hit each property individually??????
  11.         Exit;
  12.      
  13.       FTextsSettings[0] := AValue;   // reference to oldvalue FTextsSettings[0] is now thrown into oblivion ?? Is this true for TPersistant I thought deep copy not reference copy if I have the right terminology?????
  14.       ...
  15.     end;
  16.  
     

Any better/safer way to do the Assign and Comparison? My hopes that the TPersistent was helping with all the behind the scene magic. I could be way off in that the ':=' in the case of a TPersistent is the same as using Assign() and I'll bet the ':=' is not an overloaded operator in this case (have to look now).

If only I could publish an array of my own types without work to the Object Inspector, would be a lot cleaner (maybe still same issues?)

Sandy

TRon

  • Hero Member
  • *****
  • Posts: 3254
Re: Array of Objects and Use
« Reply #3 on: August 01, 2024, 06:19:57 am »
And I think since these are based on TPersistant the copy operator does it by a deep copy not just moving the reference when copying, which I hope is the same when passing it to the SetXXXSetting calls. But could be way off, and I guess even the user can mess up the reference since the property is public.
You have to implement the assign and/or assignto methods of the persistent class and use that. That is assuming that you do not have some management or other operator in your bigger project that makes the copy of the internals behind the scenes.
All software is open source (as long as you can read assembler)

SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #4 on: August 01, 2024, 06:25:22 am »
Answering some of my own questions...

TPersistent assignment

https://www.freepascal.org/docs-html/rtl/classes/tpersistent.assign.html

Looks also virtual, so I think if I expected it to work I would have to do the deep copy work...

So internally since the references are always the same for each array index, but externally if a users does an assignment, the old reference must be first cleaned of events, and possibly free'ed after the assignment. Seems really sketchy if done. A small note in the sample code with an Assign() might be in order.

But can't think of why someone would want to do it that way...

Still sorting it out by rolling code back, and it seems I have things published correctly with the individual properties with no paint code or related logic, so my issues may be not related to the references as I thought.

Plowing forward to see what breaks as I put code back in... very small bits at a time!

Thanks for you help and teaching!

Sandy

SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #5 on: August 01, 2024, 06:31:31 am »
And I think since these are based on TPersistant the copy operator does it by a deep copy not just moving the reference when copying, which I hope is the same when passing it to the SetXXXSetting calls. But could be way off, and I guess even the user can mess up the reference since the property is public.
You have to implement the assign and/or assignto methods of the persistent class and use that. That is assuming that you do not have some management or other operator in your bigger project that makes the copy of the internals behind the scenes.
No other use of it other than getting the properties in front of the user and stuff into an array for easy of processing in a paint routine. So Assign() and related implementation might not even be needed. Might be a good thing to just remove the same object check in the SetXXX() functions as it's really not needed and could be problematic since a property change with the object passing in with the same as an existing reference would never set the DIRTY flag.

Will read and play and mash F9 a lot I'm feeling :)

Again Thank you for causing some thought here.

Sandy

TRon

  • Hero Member
  • *****
  • Posts: 3254
Re: Array of Objects and Use
« Reply #6 on: August 01, 2024, 06:33:29 am »
Looks also virtual, so I think if I expected it to work I would have to do the deep copy work...
Correct. You implement those methods depending on your use-case and in that code you check the source/destination for those classes you wish to 'support' and are 'assign' compatible. For those you implement code for a 'deep' copy (trying to stay as close to your terminology).


Quote
So internally since the references are always the same for each array index, but externally if a users does an assignment, the old reference must be first cleaned of events, and possibly free'ed after the assignment.
For a normal assignment, yes.

Quote
Seems really sketchy if done. A small note in the sample code with an Assign() might be in order.
If you use assign or assignto then you 'change' the fields/values of the existing class to match that of the other class. There there is no need to free up anything because the instance of the original class will still be the same (only the content of its fields/values changed)

Quote
But can't think of why someone would want to do it that way...
I am not sure if you correctly understood the difference between assigment operator and the assign/assignto methods of a persistent class.
All software is open source (as long as you can read assembler)

SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #7 on: August 01, 2024, 06:50:25 am »
Thanks, yes I have to read up a bit more on the operators, I think I got it, as Assign() just moves over properties to the existing object (after I implement it), vs. the ':=' will trash the reference by just copying that. Have to re-read the page again.

And yes, if only changing properties nothing needs to be done, but if someone externally assigns a NEW Object, then it's a bad deal. Can't see why but the interface has it published so it's possible. Hope that makes sense.

Internally the reference never changes even when assigned back to the specific array element. Externally that may not hold true if someone does something bad.

Sandy

TRon

  • Hero Member
  • *****
  • Posts: 3254
Re: Array of Objects and Use
« Reply #8 on: August 01, 2024, 06:58:44 am »
Thanks, yes I have to read up a bit more on the operators, I think I got it, as Assign() just moves over properties to the existing object (after I implement it), vs. the ':=' will trash the reference by just copying that. Have to re-read the page again.
You got it !  :)


Quote
And yes, if only changing properties nothing needs to be done, but if someone externally assigns a NEW Object, then it's a bad deal. Can't see why but the interface has it published so it's possible. Hope that makes sense.

Internally the reference never changes even when assigned back to the specific array element. Externally that may not hold true if someone does something bad.
You can't prevent everything but you can take some measures. It depends on the use case.

Took me a while but Remy Lebeau answered a SO question about properties here, perhaps it can be of help for you. Other than that I seem unable to locate a detailed example. Ofc the source code of Lazarus is full of such implementations but that might perhaps be a bit overkill for you atm.
All software is open source (as long as you can read assembler)

SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #9 on: August 01, 2024, 07:48:09 am »
Looked at the SO example, totally makes sense. I put a note in my test code where I actually do the bad way of creating a new object, and setting it. It did work fine as I was at least smart enough to also copy the only event for the object that needed setting doChange().

Thanks again for the direction, going keep adding back code till it breaks again, and I'm guessing now it's something that I botched up in the Paint routine, so being extra slow as I add things back in.

Will see how it goes!

Sandy


SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #10 on: August 01, 2024, 09:40:34 am »
For closure...

Found a bug in that I didn't delete an unused but declared object , but was used in a the paint routine. Didn't catch it by a compile time since it was technically used and created but the bitmap it represented was never initialized. Dumb, but all working now with some more knowledge at hand.

Sandy

TRon

  • Hero Member
  • *****
  • Posts: 3254
Re: Array of Objects and Use
« Reply #11 on: August 02, 2024, 03:10:07 am »
Good to hear that you got things fixed SandyG.

Thank you for reporting back.

If your original issue is solved then please consider marking your thread as resolved , see here.
All software is open source (as long as you can read assembler)

SandyG

  • Jr. Member
  • **
  • Posts: 92
Re: Array of Objects and Use
« Reply #12 on: August 02, 2024, 03:19:50 am »
Will Do!

 

TinyPortal © 2005-2018