Execute a function contained in a variable

Just starting out? Need help? Post your questions and find answers here.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Execute a function contained in a variable

Post by charvista »

I was wondering if this is possible...
Imagine that I have a lot of procedures, and I would like to input myself the name of the procedure to execute.
The name of the procedure will then be stored in a variable. How to call the procedure contained in the variable?
The example illustrates how I would like that it works...
Any ideas?

Code: Select all

Win = OpenWindow(#PB_Any, 0, 0, 800, 100, "Title", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)

    T=TextGadget(#PB_Any, 120,56,100,20,"Procedure Name:")
    ProcName = StringGadget(#PB_Any, 260, 50, 300, 25, "")
    
    BtnOk = ButtonGadget(#PB_Any, 650, 50, 100, 30, "Execute")
    
    Repeat
        Event = WaitWindowEvent()
        Select Event
            Case #PB_Event_CloseWindow
                Quit = #True
        EndSelect
        Select EvGad
           Case BtnOk
              Proc.s = GetGadgetText(ProcName)
              ;CallFunction(Proc.s())
              Quit = #True
        EndSelect
    Until Quit
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
Crusiatus Black
Enthusiast
Enthusiast
Posts: 389
Joined: Mon May 12, 2008 1:25 pm
Location: The Netherlands
Contact:

Re: Execute a function contained in a variable

Post by Crusiatus Black »

Put all procedures you want to be called from a stringgadget in a linkedlist,
the linkedlist is linked to a structure containing the procedure address and
a lowercase string for a name. That's my nooby way of solving it, I bet there
are pb users here who come up with some awesome stuff :p

Code: Select all

Structure STRPROCENTRY
   lpProcAddress.l
   szProcName.s
EndStructure

NewList SG_Procedures.STRPROCENTRY()
Procedure SG_CallProc(szName.s)
    Shared SG_Procedures.STRPROCENTRY()
    ForEach SG_Procedures()
        If LCase(szName) = SG_Procedures()\szProcName
            CallFunctionFast(SG_Procedures()\lpProcAddress)
        EndIf
    Next
EndProcedure
Macro AddProcedure(A,B)
   AddElement(SG_Procedures())
   SG_Procedures()\lpProcAddress = A
   SG_Procedures()\szProcName = LCase(B)
EndMacro

Procedure HelloWorld()
    MessageRequester("Hello", "World")
EndProcedure

AddProcedure(@HelloWorld(), "HelloWorld()")

SG_CallProc("HELLOworld()")

Anyhow, why do you want to do such thing? Just out of curiousity.
Image
Bas Groothedde,
Imagine Programming

I live in a philosophical paradoxal randome filled with enigma's!
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Execute a function contained in a variable

Post by charvista »

Crusiatus Black,

Your code is excellent, as I could make it working well. See example below. Thank you!
The reason I am asking this is that I am building a kind of desktop, like the Windows desktop, where there are icons. My idea is identical to Windows: right click to add an icon, then tell the program name to be executed when the icon is clicked. In the example, I have made a simple input for easiness to the forum, but in my application it will be choosen from a list. The result is the same: the procedure name is stored in a variable. I was then wondering how to launch it. An external .EXE program is not a problem because you use RunProgram(Proc.s) but when it is an internal procedure it does not work the same way.

Code: Select all

    Structure STRPROCENTRY
       lpProcAddress.l
       szProcName.s
    EndStructure

    NewList SG_Procedures.STRPROCENTRY()
    
    Procedure SG_CallProc(szName.s)
        Shared SG_Procedures.STRPROCENTRY()
        ForEach SG_Procedures()
            If LCase(szName) = SG_Procedures()\szProcName
                CallFunctionFast(SG_Procedures()\lpProcAddress)
            EndIf
        Next
    EndProcedure
    
    Macro AddProcedure(A,B)
       AddElement(SG_Procedures())
       SG_Procedures()\lpProcAddress = A
       SG_Procedures()\szProcName = LCase(B)
    EndMacro

    Procedure HelloWorld()
        MessageRequester("Hello", "World")
    EndProcedure
    
    Procedure GoodbyeEarth()
        MessageRequester("Goodbye", "Earth")
    EndProcedure
     
    AddProcedure(@HelloWorld(), "HelloWorld()")
    AddProcedure(@GoodbyeEarth(), "GoodbyeEarth()")


    Win = OpenWindow(#PB_Any, 0, 0, 800, 100, "Title", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)

    T=TextGadget(#PB_Any, 120,56,100,20,"Procedure Name:")
    ProcName = StringGadget(#PB_Any, 260, 50, 300, 25, "")
    
    BtnOk = ButtonGadget(#PB_Any, 650, 50, 100, 30, "Execute")
    
    Repeat
        Event = WaitWindowEvent()
        Select Event
            Case #PB_Event_CloseWindow
                Quit = #True
            Case #PB_Event_Gadget
                Select EventGadget()
                    Case BtnOk
                        Proc.s = GetGadgetText(ProcName)
                        SG_CallProc(Proc.s)
                  EndSelect
        EndSelect
    Until Quit
Note that I added a procedure GoodbyeEarth() to test it well. I now hope to be able to add parameters as well.
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
Crusiatus Black
Enthusiast
Enthusiast
Posts: 389
Joined: Mon May 12, 2008 1:25 pm
Location: The Netherlands
Contact:

Re: Execute a function contained in a variable

Post by Crusiatus Black »

charvista wrote: Note that I added a procedure GoodbyeEarth() to test it well. I now hope to be able to add parameters as well.
Hmm, I know of the unlimited argument operator (...) in lua, and then you can use
unpack(arg) to pass these arguments on to another function, but there is no such
thing in PureBasic I think. I wouldn't know how you would dynamically pass arguments
to procedures when you don't know how many arguments are required in a procedure.

You aren't wrapping PB's native procedures I hope? :p
Image
Bas Groothedde,
Imagine Programming

I live in a philosophical paradoxal randome filled with enigma's!
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Execute a function contained in a variable

Post by charvista »

If I can limit it to two fixed arguments, then I will be more than happy. Dynamic arguments would be a big plus, of course.
My idea is that, if I cannot pass arguments, then I will try to find workarounds, like global variables. But I am not that far.
Thanks to your code, I now know that it is possible to execute a procedure (with no param) from a variable. It is a big step in the right direction!
I prefer to use native codings whenever possible, to maximize the chances of portability (crossplatform) between Windows, Linux and OS.X if ever it might be necessary. But most interesting codings found in the forum are Windows' API, so it's hard to resist... :wink:
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
Perkin
Enthusiast
Enthusiast
Posts: 504
Joined: Thu Jul 03, 2008 10:13 pm
Location: Kent, UK

Re: Execute a function contained in a variable

Post by Perkin »

How about something like a structure holding pointers to various variables and passing the structure to the routine
Something like...

Code: Select all

Structure Paramstructs
  numpars.i
  *param1
  *param2
  *param3
; continue on until maximum used params
EndStructure
then pass this sturcture to your routine.
%101010 = $2A = 42
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Execute a function contained in a variable

Post by charvista »

I have welcomed your idea, Perkin.
However I am not sure how to implement it......
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
Perkin
Enthusiast
Enthusiast
Posts: 504
Joined: Thu Jul 03, 2008 10:13 pm
Location: Kent, UK

Re: Execute a function contained in a variable

Post by Perkin »

charvista wrote:I have welcomed your idea, Perkin.
However I am not sure how to implement it......
That's usually the hard part :wink:

Define the structure as a Global, then fill the params with required var/pointers to existing vars before you call your procedure, then in the procedures you can retrieve the vars from the Global structure.

Any help?
%101010 = $2A = 42
User avatar
Melissa
User
User
Posts: 71
Joined: Tue Sep 22, 2009 3:13 pm

Re: Execute a function contained in a variable

Post by Melissa »

http://www.purebasic.com/documentation/map/index.html
Concerning pass of arguments - it could be done by manual copying'em onto stack. Just try disassembling your .EXEs to get how compiler does that.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Execute a function contained in a variable

Post by charvista »

I have seen the help for the Structure command and it looks simpler than I first thought.
I will then use your idea with Global Structure or I will use a single var/pointer with the parameters separated with a specific delimiter, which can then easily be retrieved with StringField().

@Melissa
Using Maps? Yes, that's also a good idea. In the help I see Country("US")="United States" which can be replaced with Arg("1")="ParamValue1" in my case. So far, no problem. But how could you retrieve "United States" from the MapKey "US"? In other words, how could I retrieve "ParamValue1" from MapKey "1"?
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
Melissa
User
User
Posts: 71
Joined: Tue Sep 22, 2009 3:13 pm

Re: Execute a function contained in a variable

Post by Melissa »

But how could you retrieve "United States" from the MapKey "US"? In other words,
RetrieverVar.S = Map("US")

...ALSO some hints for parameter passing:

Code: Select all

; Very-very brief sample.
Procedure Test(A.l, b.l)
Debug Str(A) + " : " + Str(B)
EndProcedure

Macro PassInt(Buffer, Val)
Buffer\I = Val : Buffer + SizeOf(Integer)
EndMacro

EnableASM
Global *ProcPtr = @Test()
; Vanilla (boring) way:
Global TestVar = 1 ; Variables could be used too.
PUSH 2 ; B.l parameter
PUSH TestVar ; A.l parameter
Call *ProcPtr ; You extracting it from list anyways.
; ...or, more likely...
Global BufferSize = SizeOf(Long) * 2 ; Params
Global *PBuffer = AllocateMemory(BufferSize)
Global *Passer.Integer = *PBuffer
PassInt(*Passer, 3) ; A.l parameter
PassInt(*Passer, 4) ; B.l parameter
; No local variables could be referenced now !
SUB ESP, BufferSize ; Stack correction.
MOV *Passer, ESP ; Retrieveing ESP...
CopyMemory(*PBuffer, *Passer, BufferSize)
Call *ProcPtr ; One more call...
; Stack restored, local variables is here again.
FreeMemory(*PBuffer) ; To be sure.
DisableASM
Last edited by Melissa on Mon Apr 05, 2010 4:27 pm, edited 3 times in total.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Execute a function contained in a variable

Post by charvista »

But how could you retrieve "United States" from the MapKey "US"?
RetrieverVar.S = Map("US")
This does not work, as there is no Map() command:
(I'm using PB 4.50 beta 2)

Code: Select all

  NewMap Country.s()

  Country("US") = "United States"
  Country("FR") = "France"
  Country("GE") = "Germany"

  ForEach Country()
    Debug MapKey(Country())
  Next
  
    RetrieverVar.S = Map("US")
  Debug RetrieverVar.S
Thank you for the small example for passing variables using Assembler (so it must be very fast!). No offense, but I prefer Perkin's solution, partially because I have no knowledge in Assembler.
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
Melissa
User
User
Posts: 71
Joined: Tue Sep 22, 2009 3:13 pm

Re: Execute a function contained in a variable

Post by Melissa »

RetrieverVar.S = Map("US")
...OK, if you put it that way: RetrieverVar.S = Country("US")
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Execute a function contained in a variable

Post by charvista »

Thank you Melissa, it was too simple :mrgreen:
Now I see that you were referring Map() as the name of the map, in this case Country().
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: Execute a function contained in a variable

Post by Trond »

The name of the procedure will then be stored in a variable. How to call the procedure contained in the variable?
The procedure names are not stored in the executable.
Post Reply