Recent

Author Topic: Scope anomolies  (Read 1630 times)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11795
  • Debugger - SynEdit - and more
    • wiki
Re: Scope anomolies
« Reply #15 on: October 16, 2025, 12:20:17 pm »
So if your question is: Can you make TButton.OnClick call an enclosed procedure => then the answer is NO

an yet, when I click the button the procedure is performed???

Yes, but without access to the local vars of the enclosing.

Calling a procedure, any procedure needs 2 things.
- the address of the machine code (that is always just a pointer)
- passing the correct parameters in the correct order and correct "calling convention" (and that includes any hidden params).


The first part works for you. It is easy to get that to work.

The 2nd part does not work.

roger T

  • New Member
  • *
  • Posts: 23
Re: Scope anomolies
« Reply #16 on: October 16, 2025, 12:50:26 pm »
Fair enough. Thank you.
Global variables it is then
Cheers
Roger

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11795
  • Debugger - SynEdit - and more
    • wiki
Re: Scope anomolies
« Reply #17 on: October 16, 2025, 01:59:24 pm »
Just had one more (bad) idea. I.e. I don't recommend it. And I haven't tested it.

And also even if it should work for one Target (CPU + OS) then it may not work on another, or need different code.
It may even change between fpc versions, so when you update you must test, and hope it can be made to work again.


As indicated a nested procedure has a hidden parameter. So for it to work to call it via a reference the value of that hidden pointer probably must be stored along with the "machine code pointer". (just like the self param for a method is stored in TMethod).

That would then happen if you declare
Code: Pascal  [Select][+][-]
  1.     type TMyNestedProc = procedure (Sender: TObject) is nested;
  2. var procptr: TMyNestedProc;

In would then guess, that  "procptr"  has this value....

I don't know the internal format of that variable/type. But if the "hidden parentfp" is in it, then - even if using raw pointer access - it should be possible to extract it.

If you have it, you can store it (temporarily) in the "TMethod.Data" part (which is passed as "self" param).

Then when your proc is called, you would need to restore it. That mean you need a 2nd proc, that does the restoration, that re-builds the variable "procptr", and then calls it.
On Window, x86-64 the parent_fp is in the same pos as "self" would be, and *probably* the same calling convention. So you wouldn't need the wrapper, if you could get it and store it in TMethod.Data.

That is a lot of work. And a lot of danger.

I would certainly not do this myself.

n7800

  • Hero Member
  • *****
  • Posts: 542
Re: Scope anomolies
« Reply #18 on: October 16, 2025, 03:02:04 pm »
2)
use the correct function pointer
Code: Pascal  [Select][+][-]
  1. type TMyNestedProc = procedure (Sender: TObject) is nested;
wich will not require you to do any TMethod type cast

Sorry, I don't understand what this "is nested" declaration is? It doesn't compile even in the FPC trunk.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11795
  • Debugger - SynEdit - and more
    • wiki
Re: Scope anomolies
« Reply #19 on: October 16, 2025, 03:18:33 pm »
2)
use the correct function pointer
Code: Pascal  [Select][+][-]
  1. type TMyNestedProc = procedure (Sender: TObject) is nested;
wich will not require you to do any TMethod type cast

Sorry, I don't understand what this "is nested" declaration is? It doesn't compile even in the FPC trunk.

I forgot, it needs a modeswitch {$modeswitch nestedprocs} or similar.

Allows to take "pointer" to nested procs.

n7800

  • Hero Member
  • *****
  • Posts: 542
Re: Scope anomolies
« Reply #20 on: October 16, 2025, 09:13:43 pm »
I forgot, it needs a modeswitch {$modeswitch nestedprocs} or similar.

Thanks, it's useful to know about "nestedprocvars".

PascalDragon

  • Hero Member
  • *****
  • Posts: 6189
  • Compiler Developer
Re: Scope anomolies
« Reply #21 on: October 16, 2025, 10:42:19 pm »
Lots of ways to skin a cat but not what I was asking.

No, the correct way to assign to method pointer like OnClick is to use a class. Anything else is just asking for trouble.

Aren't I supposed to be able access variables defined in an enclosing procedure

Yes, but not in what you're trying to achieve here.

There are now (may be in fpc trunk) anonymous functions. I don't know what signature they have. So I don't know if you can make it and pass one of them to TButton. No idea.

Anonymous functions match their (internal) signature to what they're assigned to. However even then they need to adhere to the rules of the type they're assigned to, so in this case it means that the anonymous function may only access Self or global variables.

Fair enough. Thank you.
Global variables it is then

No, as I said, a handler class it is. Using global variables will fail you as soon as you go multi threaded.

roger T

  • New Member
  • *
  • Posts: 23
Re: Scope anomolies
« Reply #22 on: October 16, 2025, 10:56:49 pm »
"Lots of ways to skin a cat but not what I was asking.
No, the correct way to assign to method pointer like OnClick is to use a class. Anything else is just asking for trouble."

If the language allows it then it IS a correct way to do it. Asking for trouble ? Is Lazarus such rubbish that it's gonna fall over ??

Fair enough. Thank you.
Global variables it is then
No, as I said, a handler class it is. Using global variables will fail you as soon as you go multi threaded.

Love the way people wanna tell you how to code. Don't think I mentioned anything about multi threading. And really If you don't want to use variables outside a procedure then don't use variable outside a procedure

PascalDragon

  • Hero Member
  • *****
  • Posts: 6189
  • Compiler Developer
Re: Scope anomolies
« Reply #23 on: October 16, 2025, 11:31:42 pm »
"Lots of ways to skin a cat but not what I was asking.
No, the correct way to assign to method pointer like OnClick is to use a class. Anything else is just asking for trouble."

If the language allows it then it IS a correct way to do it. Asking for trouble ? Is Lazarus such rubbish that it's gonna fall over ??

This has nothing to do with “rubbish”, but that you're trying to force something that's simply wrong. Something compiling does not necessarily mean that it's correct. And assigning a nested function to a method pointer is not correct, but due to the typecasts you're telling the compiler that it should treat it as correct, but this won't change that it's wrong and will lead to errorrs at runtime.

Love the way people wanna tell you how to code. Don't think I mentioned anything about multi threading. And really If you don't want to use variables outside a procedure then don't use variable outside a procedure

A method pointer takes a pointer to a method which is a member of a class and since you then need to have a class instance anyway it makes simply no sense to not just put any state you need into that instance.

roger T

  • New Member
  • *
  • Posts: 23
Re: Scope anomolies
« Reply #24 on: October 17, 2025, 02:37:42 am »
Here's an interesting way to achieve what I wanted (which is really just having the variables in close proximity to the code - don't give a rats whether they are local or not, I just wont use them anywhere else) - Seems you can stick VAR anywhere in the code and as long as its not in a procedure it's global. Who knew eh !

Code: Pascal  [Select][+][-]
  1. //SAVE AS
  2. //
  3. //Can be saved with Linux/Windows/MacOS end of line characters
  4.  
  5. var
  6.   wnd           : TForm;
  7.   lbl           :       TLabel;
  8.   edt           :       TEdit;
  9.   rad           :       TRadioGroup;
  10.   btn           :       TButton;
  11.   EventPtr      : TNotifyEvent;
  12.  
  13. procedure SaveFileAs();
  14.  
  15.   procedure buttonClick(Sender : TObject);
  16.   begin
  17.  
  18.     assignFile(outFile, edt.Text);
  19.     rewrite(outFile);
  20.     Y := 0;
  21.     repeat
  22.       begin
  23.         case rad.ItemIndex of
  24.           0 : Write(outFile, FocusedSynEdit.Lines[Y] + char($0A));
  25.           1 : Write(outFile, FocusedSynEdit.Lines[Y] + Char($0D) + char($0A));
  26.           2 : Write(outFile, FocusedSynEdit.Lines[Y] + char($0A));
  27.         end; // END CASE
  28.         inc(Y);
  29.       end
  30.     until Y > FocusedSynEdit.Lines.Count;
  31.     close(outfile);
  32.     wnd.close;
  33.   end;
  34.  
  35. [begin
  36.   wnd                   := TForm.Create(Form1);
  37.   wnd.SetBounds         (150, 150, 500, 150);
  38.   wnd.Caption                   := 'Save As';
  39.  
  40.   lbl                           := TLabel.Create(wnd);
  41.   lbl.parent                    := wnd;        
  42.   lbl.SetBounds         (25,27,50,15);
  43.   lbl.Caption                   := 'Save As';
  44.  
  45.   edt                           := TEdit.Create(wnd);
  46.   edt.parent                    := wnd;        
  47.   edt.SetBounds         (75,25,400,15);
  48.   edt.Text                              := files[Form1.Edit_PageControl.ActivePageIndex].Name;
  49.  
  50.   rad                           := TRadioGroup.Create(wnd);
  51.   rad.parent                    := wnd;                        
  52.   rad.SetBounds         (75,60,100,75);
  53.   rad.Items.Add('Linux');
  54.   rad.Items.Add('Windows');
  55.   rad.Items.Add('MacOS');
  56.   {$IFDEF LINUX}
  57.     rad.ItemIndex       := 0;
  58.   {$ENDIF}
  59.   {$IFDEF WINDOWS}
  60.     rad.ItemIndex       := 1;
  61.   {$ENDIF}
  62.   {$IFDEF DARWIN}
  63.     rad.ItemIndex       := 2;
  64.   {$ENDIF}
  65.  
  66.   btn                           := TButton.Create(wnd);
  67.   btn.parent                    := wnd;
  68.   btn.Caption                   := 'Okay';
  69.   btn.SetBounds         (415, 75, 60, 24);
  70.   TMethod(EventPtr).Code := @buttonClick;
  71.   TMethod(EventPtr).Data := nil;                       
  72.   btn.OnClick := EventPtr;
  73.  
  74.   X := 0;
  75.   Str1 := files[Form1.Edit_PageControl.ActivePageIndex].Name;
  76.  
  77.   wnd.ShowModal;
  78.  
  79. end;
  80.  
  81.  
« Last Edit: October 17, 2025, 02:41:48 am by roger T »

 

TinyPortal © 2005-2018