Recent

Author Topic: How did I get the use of "with" statement wrong?  (Read 1460 times)

vfclists

  • Hero Member
  • *****
  • Posts: 999
    • HowTos Considered Harmful?
How did I get the use of "with" statement wrong?
« on: February 09, 2024, 01:19:34 am »
For some time I have always thought that the with statement used with multiple objects enabled setting a particular property on all of them at one go:

For example if I have 4 Tpanels and I want to change the color of all of them I could so something like,

Code: Pascal  [Select][+][-]
  1. with A, B, C, D do
  2. begin
  3.  Color := Red;
  4. end;

and all of them would be red.

Now I realize that only D would be red.

So if the with statement doesn't work like that what is that kind of syntax for? What are the actual semantics?

What would be the alternative?

Is there a construct like:

Code: Pascal  [Select][+][-]
  1. for p in [A,B,C,D] do
  2. begin
  3.   p.Color := Red;
  4. end;
  5.  

I just checked the documentation and realized the for in construct is present.

Is it possible to iterate of the elements without having to declare a type or an array/list for them first?
« Last Edit: February 09, 2024, 01:36:07 am by vfclists »
Lazarus 3.0/FPC 3.2.2

Fibonacci

  • Sr. Member
  • ****
  • Posts: 352
  • Certified Internal Error Hunter
Re: How did I get the use of "with" statement wrong?
« Reply #1 on: February 09, 2024, 01:28:13 am »
So if the with statement doesn't work like that what is that kind of syntax for? What are the actual semantics?

Well this works

Code: Pascal  [Select][+][-]
  1. type
  2.   trec = record
  3.     str: string;
  4.   end;
  5.  
  6. var
  7.   a, b: trec;
  8.  
  9. begin
  10.   a.str := 'first';
  11.   b.str := 'second';
  12.  
  13.   with a, b do writeln('a is ', a.str, ' and b is ', b.str);
  14.  
  15.   readln;
  16. end.

Which actually makes no sense cos u can do it without "with" :D Time to go to bed
« Last Edit: February 09, 2024, 01:32:13 am by Fibonacci »

BeniBela

  • Hero Member
  • *****
  • Posts: 901
    • homepage
Re: How did I get the use of "with" statement wrong?
« Reply #2 on: February 09, 2024, 01:33:17 am »
Time to go to bed

true, true. it is 1:30 AM here

jamie

  • Hero Member
  • *****
  • Posts: 6032
Re: How did I get the use of "with" statement wrong?
« Reply #3 on: February 09, 2024, 02:14:16 am »
With reference to the "WITH" statement, that in itself is the effect of looking at a Name spaced tree of Identities.

 For example:

  While inside the Encloser and as you reference different names the compiler will first search the last one in the list you specified and if not found, then it will search in the tag prior to and so on, until if finds a name tag you specified.

   If not found in any of those, it will search outside that construct like normal.
 

  So if the member you desire does not exist in D, then E or whatever comes before that gets checked at compile time.

  The first one found is what the compiler takes and goes no further searching right to left, just like searches units.

 
  The IN operator is tricky as it creates copies when possible so making changes will not change the original source.

The only true wisdom is knowing you know nothing

Fibonacci

  • Sr. Member
  • ****
  • Posts: 352
  • Certified Internal Error Hunter
Re: How did I get the use of "with" statement wrong?
« Reply #4 on: February 09, 2024, 02:15:10 am »
I found out one possible use. Now I can go sleep 8)

Code: Pascal  [Select][+][-]
  1. type
  2.   trec1 = record
  3.     str: string;
  4.   end;
  5.  
  6.   trec2 = record
  7.     otherstr: string;
  8.   end;
  9.  
  10. var
  11.   a: trec1;
  12.   b: trec2;
  13.  
  14. begin
  15.   with a, b do begin
  16.     str := 'test str';
  17.     otherstr := 'another str';
  18.   end;
  19.  
  20.   with a, b do writeln('str field is ', str, '; otherstr field is ', otherstr);
  21.  
  22.   readln;
  23. end.

Output: str field is test str; otherstr field is another str

So, its like: join all fields of all vars into one and use them. If there are duplicates, use the last field with that name. So if you have eg multiple TPanel's there, you can read/change only the last one. But if you have multiple types, you can "join them" into one and play with the fields, but no duplicates.

@jamie: Winner, you were first

The IN operator is tricky as it creates copies when possible so making changes will not change the original source.

Thats true, so OP, beware, for TPanels it works because its a Class (like Pointer), but internally it makes a copy of the Pointer, which will still point to the TPanel object. If its not a Pointer (or Class), compiler will give you an error that you cant change the value.
« Last Edit: February 09, 2024, 02:24:03 am by Fibonacci »

vfclists

  • Hero Member
  • *****
  • Posts: 999
    • HowTos Considered Harmful?
Re: How did I get the use of "with" statement wrong?
« Reply #5 on: February 09, 2024, 12:44:42 pm »
Is this code safe and free of any potential gotchas?

Code: Pascal  [Select][+][-]
  1.   procedure SetPanelSize;
  2.     var
  3.       panels: TPanelList;
  4.       panel: TPanel;
  5.   begin
  6.     panels := [panel1, panel2, panel3];
  7.     for panel in panels do
  8.     begin
  9.       TMyRollout(panel.Parent).ExpandedSize := 400;
  10.     end;
  11.   end;
  12.  
Lazarus 3.0/FPC 3.2.2

Ten_Mile_Hike

  • New Member
  • *
  • Posts: 41
Re: How did I get the use of "with" statement wrong?
« Reply #6 on: February 11, 2024, 03:41:50 am »
Code: Text  [Select][+][-]
  1.  
  2. I have always used one of the following 3 procedures
  3.  
  4.  
  5. procedure TForm1.Button1Click(Sender: TObject);
  6. var
  7.   b: TButton;
  8. begin
  9.   for b in [button1, button2, button3] do b.Caption := 'B1';
  10. end;
  11.  
  12. procedure TForm1.Button2Click(Sender: TObject);
  13. var
  14.   x: integer;
  15. begin
  16.   for x := 0 to componentcount - 1 do
  17.     if Components[x] is TButton then (Components[x] as TButton).Caption := 'B2';
  18. end;
  19.  
  20. procedure TForm1.Button3Click(Sender: TObject);
  21. var
  22.   x: integer;
  23.   ButtonList, PanelList: TObjectList;
  24. begin
  25.   ButtonList := TObjectList.Create;
  26.   PanelList  := TObjectList.Create;
  27.   for x := 0 to componentcount - 1 do
  28.     case Components[x].ClassName of
  29.       'TButton': ButtonList.Add(Components[x]);
  30.       'TPanel': PanelList.Add(Components[x]);
  31.     end;
  32.   for x := 0 to ButtonList.Count - 1 do (ButtonList[x] as TButton).Caption := 'B3';
  33.   for x := 0 to PanelList.Count  - 1 do (PanelList[x] as TPanel  ).Caption := 'P3';
  34. end;                

ccrause

  • Hero Member
  • *****
  • Posts: 840
Re: How did I get the use of "with" statement wrong?
« Reply #7 on: February 11, 2024, 07:27:23 am »
Is this code safe and free of any potential gotchas?

Code: Pascal  [Select][+][-]
  1.   procedure SetPanelSize;
  2.     var
  3.       panels: TPanelList;
  4.       panel: TPanel;
  5.   begin
  6.     panels := [panel1, panel2, panel3];
  7.     for panel in panels do
  8.     begin
  9.       TMyRollout(panel.Parent).ExpandedSize := 400;
  10.     end;
  11.   end;
  12.  

One potential gotcha is if (say in future modifications) one of the panels in the panels list is modified such that its parent is not of type TMyRollout.  To guard against such future surprises, build in a safety check:
Code: Pascal  [Select][+][-]
  1.   for panel in panels do
  2.   begin
  3.     assert(panel is TMyRollout, 'Panel ' + Panel.Name + ' does not have parent TMyRollout');
  4.     TMyRollout(panel.Parent).ExpandedSize := 400;
  5.   end;

There may be other gotchas such as a non-existing panel (panel not yet created, panel already freed etc.), but this depends on how the panels list is maintained.

Thaddy

  • Hero Member
  • *****
  • Posts: 14013
  • Probably until I exterminate Putin.
Re: How did I get the use of "with" statement wrong?
« Reply #8 on: February 11, 2024, 01:25:25 pm »
and all of them would be red.
Now I realize that only D would be red.
If the with statement has multiple options that satisfy acondition or property, only the last one in the the list will be executed.
This is documented: https://www.freepascal.org/docs-html/ref/refsu62.html
The example is similar to what you asked, the answer is similar to mine.
Specialize a type, not a var.

Kays

  • Hero Member
  • *****
  • Posts: 568
  • Whasup!?
    • KaiBurghardt.de
Re: How did I get the use of "with" statement wrong?
« Reply #9 on: February 11, 2024, 09:07:50 pm »
[…] So if the with statement doesn't work like that what is that kind of syntax for? What are the actual semantics?
The authoritative source is the Reference Guide, but you can read also the wiki article.
[…] What would be the alternative? […] Is it possible to iterate of the elements without having to declare a type or an array/list for them first?
You could implement your own procedure that accepts a tea color and a variable number of tea panels so you can call:
Code: Pascal  [Select][+][-]
  1. setColor(red, A, B, C, D)
Yours Sincerely
Kai Burghardt

TRon

  • Hero Member
  • *****
  • Posts: 2278
Re: How did I get the use of "with" statement wrong?
« Reply #10 on: February 11, 2024, 09:17:38 pm »
You could implement your own procedure that accepts a tea color and a variable number of tea panels so you can call:
Code: Pascal  [Select][+][-]
  1. setColor(red, A, B, C, D)
Why on earth TS wished to circumvent the use of declaring a type or creating an array/list is beyond me. The following is imho much easier.

Code: Pascal  [Select][+][-]
  1. procedure xxx.SetColor(color: TColor; panels: array of TPanel);
  2. begin
  3.   // safeguard use of the correct TPanel
  4. end;
  5.  
  6. procedure xxx.dodo;
  7. begin
  8.   SetColor(clRed, [Panel1, Panel2, Panel3]);
  9. end;
  10.  

... or more in line with the original code from TS:
Code: Pascal  [Select][+][-]
  1. procedure deedee;
  2. var
  3.   panel: TPanel;
  4. begin
  5.   for panel in [panel1, panel2, panel3] do
  6.     if panel.parent is TMyRollout
  7.      then TMyRollout(panel.Parent).ExpandedSize := 400;
  8. end;
  9.  
.. which still requires the creation of the array (and as already suggested by OP self and Ten_Mile_Hike)
« Last Edit: February 11, 2024, 09:42:53 pm by TRon »

jamie

  • Hero Member
  • *****
  • Posts: 6032
Re: How did I get the use of "with" statement wrong?
« Reply #11 on: February 11, 2024, 09:22:40 pm »
There are several ways to do this, A TLIST comes to mind, that can hold any form of object.
I suppose you could even use a generic to define a Class type within the array of items.

 Then you have the TCollection which is geared towards managing items for something like a drag and drop system etc.

The list goes on, really but using SETs or the like is messy for this.
The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 2278
Re: How did I get the use of "with" statement wrong?
« Reply #12 on: February 11, 2024, 09:43:22 pm »
The list goes on, really but using SETs or the like is messy for this.
Which set's jamie ?

jamie

  • Hero Member
  • *****
  • Posts: 6032
Re: How did I get the use of "with" statement wrong?
« Reply #13 on: February 11, 2024, 11:34:14 pm »
I meant the use of the [...] "Open Arrays" and simple sets where the compile needs to build dynamically each time you call it.

At least that is what it looked like the last time I checked out the ASM code, otherwise, how would it know the current state?

Putting objects in some sort of managed code where the items are already loaded for example and all you need to do is scan it to reference the items. The build in Enumerator are included in most of those classes that does that.
The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 2278
Re: How did I get the use of "with" statement wrong?
« Reply #14 on: February 13, 2024, 08:30:24 am »
@jamie:
Fair enough. A misunderstanding on my part on what you meant with your initial statement. Thank you for elaborating.

 

TinyPortal © 2005-2018