Page 1 of 2
Execute a function contained in a variable
Posted: Sun Apr 04, 2010 10:20 pm
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
Re: Execute a function contained in a variable
Posted: Sun Apr 04, 2010 10:58 pm
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.
Re: Execute a function contained in a variable
Posted: Sun Apr 04, 2010 11:38 pm
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.
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 6:51 am
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
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 12:46 pm
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...

Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 12:53 pm
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.
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 1:27 pm
by charvista
I have welcomed your idea, Perkin.
However I am not sure how to implement it......
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 1:33 pm
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
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?
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 1:35 pm
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.
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 2:31 pm
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"?
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 2:50 pm
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
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 3:14 pm
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.
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 3:24 pm
by Melissa
RetrieverVar.S = Map("US")
...OK, if you put it that way: RetrieverVar.S = Country("US")
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 3:49 pm
by charvista
Thank you Melissa, it was too simple
Now I see that you were referring Map() as the name of the map, in this case Country().
Re: Execute a function contained in a variable
Posted: Mon Apr 05, 2010 8:16 pm
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.