HELP: How do I write a function pointer to memory?

BlitzMax Forums/BlitzMax Programming/HELP: How do I write a function pointer to memory?

ImaginaryHuman(Posted 2004) [#1]
Hi folks.

I'm trying to put a function pointer in memory at a given address. The code I'm trying to use to do this is a slight rework of code taken from BlitzMax's PokeInt command. However, it doesn't compile and complains of some problem not being able to convert types. I can't figure out what's wrong with it? Can you help?

myfuncitonsbuf is an address in memory - a byte pointer
offset is an offset in bytes from that base address - an integer
funct1() is previously defined as a function - a function pointer


(Int Ptr(myfunctionsbuf+offset))[0]=Int(funct1) ' Something wrong with this line


I haven't used type casting much so I am a bit confused.

Please don't evaluate whether or not I should be trying to do this, I DO need to do it this way, I just need to know how to make it work. Thanks.

I wondered if maybe it's a problem because the base address of the buffer is a byte pointer and should be an integer? Or maybe I am changing to the wrong types?

Thanks for your help.


marksibly(Posted 2004) [#2]
Hi,

You'll need to convert the function pointer to a byte pointer first.

my_ints[n]=Int ( Byte Ptr funct1 )

To cast back, do the opposite:

Local my_func()=Byte Ptr( my_ints[n] )

My guess is it would be far more sensible to do whatever you're doing using an object/method, but whatever!


ImaginaryHuman(Posted 2004) [#3]
So it's okay to use function pointers and byte pointers interchangably?

So to store a function pointer in ram, where mybuf is a byte pointer, I could do:

(Int Ptr(mybuf))[0]=Int(BytePtr(funct1))

and to call the function directly I could do:

(Byte Ptr(Int Ptr(mybuf))[0])(Param)

????????

Can I do:

mybuf[0]=Int(BytePtr(funct1))

where mybuf is a byte pointer, not a pointer to an array? and...

mybuf[0](Param)

???????? or does it need all that byte-ptr conversion stuff?


ImaginaryHuman(Posted 2004) [#4]
Also, is using mybuf[0] where mybuf is a byte pointer, the same speed as doing mybuf[0] where mybuf is a pointer to an array?


ImaginaryHuman(Posted 2004) [#5]
I tried the above example code.

This works:


offset[0]=Int(Byte Ptr(funct1))


but this doesn't:


tempfunct=Byte Ptr(offset[0])



The compiler says "Unable to convert from Byte Ptr to Int(Int)".

It allows conversion from a function pointer to a byte pointer, but it doesn't seem to allow it the other way around?


ImaginaryHuman(Posted 2004) [#6]
Ok, here's the latest scoop. BlitzMax doesn't seem to allow you to convert anything TO a function pointer. You can cast a function pointer to a byte pointer, and a byte pointer to an int pointer, but it won't allow you to put any kind of pointer in a function pointer.

So, what I came up with is a way of avoiding the `type` problem and changing the function pointer using memory access. It's easy enough to store the function pointers in a bank's memory by converting them to int pointers and writing them out (with [0] - making sure to write an int using an int ptr!).

Then to read them back and excecute them, since Blitz won't accept making a function pointer equate to anything other than another function pointer, I get the address of the function pointer itself, an identifier/variable, which actually works using VarPtr(functionname). Then I sneak behind Blitz's back and directly copy the pointer (the temporary function pointer must be a *Global* in memory otherwise this isn't going to work - if it's in a Local register we wouldn't know which register it's in and we wouldn't be able to write to that register directly) .. I copy the function pointer directly in memory using int ptr's to the function pointer's *address* (location in mem of the function pointer variable), and then simply call the temporary function (which has now been changed to point elsewhere) with any parameters defined.

It works, but, well, a little bit longwinded and it took quite a lot of messing around to get it right (not Blitz's fault). You have to make absolutely certain that the full integer value of the function pointer is being moved around, plus have to make sure the function pointer is in memory. That's not ideal, since it means it has to be read from ram, written to ram, then accessed from ram again when calling it - Local's would be nicer. But if Blitz would let you cast an int ptr or a byte ptr to a function pointer it would be a lot less work and could go in Local's. I guess you can't have a casting command "Function Ptr()" because the compiler recognizes `Function` as a reserved word that defines a function. Hmmmmm :-) Changeable?

Anyway, this program works, lets you store function pointers in a bank and execute them from the bank (with a slight interpretation). It might be faster to just use an array of function pointers and access them with funct[i](counter), compared to this `solution`, but hey, banks can be loaded and saved.


Rem

Using function pointers to change which function is called dynamically at runtime

With an bank of function pointers. You can convert function pointers into Byte Pointers, but apparently not the reverse.
So instead, I got the address of the function pointer as if it is a variable using VarPtr(), then I sneak behind the
function pointer's type and write its address directly. Then it works.

End Rem

Local myfunctions:TBank=CreateBank(10*4)
Local myfunctionsbuf:Byte Ptr
myfunctionsbuf=BankBuf(myfunctions) 'Address of where function pointers will be stored and retrieved, in a bank

Function funct1(param:Int)
	Print "You're in function 1, passed counter parameter="+param
End Function

Function funct2(param:Int)
	Print "You're in function 2, passed counter parameter="+param
End Function

Local counter:Int
Local offset:Int Ptr=Int Ptr(myfunctionsbuf)
Print "Original functions are at: "+Int(Byte Ptr(funct1))+" "+Int(Byte Ptr(funct2))
For counter=0 To 9 Step 2 'Decide which functions to call
	offset[0]=Int(Byte Ptr(funct1))
	offset[4]=Int(Byte Ptr(funct2))
	offset:+8
Next

Global tempfunct(param:Int) 'Needs to be in memory, not a Local, otherwise can't be remapped!
Local where:Int Ptr=Int Ptr(VarPtr(tempfunct)) 'Need it to be an Int Ptr
offset=Int Ptr(myfunctionsbuf)
For counter=0 To 9
	where[0]=offset[0] 'Copy function-pointer in memory
	'Print "Remapped to: "+where[0]
	tempfunct(counter) 'Call remapped function
	offset:+4
Next




ImaginaryHuman(Posted 2004) [#7]
Also this leads to an almost-as-good-as solution to the Goto technique that I was envisioning also - where I wanted to be able to change label pointers so that I could jump around to a given sequence of labels, changeable at runtime ... which wouldn't work of course....

Using the above technique of copying the function pointer in memory behind the back of the type checking, you could have a bunch of function pointer (variables), not necessarily in an array, and remap them as above - they don't even need to be in a bank, then call all the functions. ie


Local funct1()
Local funct2()
Local funct3()
Local funct4()
Local funct5()

'Then use the pointer mem-copy as above to remap these to several specified functions (by number)

'Then call them with:

funct1(); funct2(); funct3(); funct4(); funct5()


The only overhead I see would be 2 mem accesses to handle the `gosub` that is a function call (on powerpc anyway), plus the gosub itself and the final jump back - 4 instructions, compared to 1 instruction with the goto method (given that there are no function parameters). That's not too shabby and would let you hardwire/rewire a sequence of function calls without having to get an array involved. :-D Then the sequence can act like a programmable circuit (albeit the program flow is a little bit diverted). Hey, Mark if you include function inlining in the BlitzMax compiler maybe I won't even need to worry about that ;-)

Note to self: Maybe use a `functionaddress[]` array to store all the function pointers of the real functions, then just set these dynamic functions to the appropriate numbered function pointer taken from the array - where the number of the array index is taken from a `bytecode` - where bytecodes directly map to corresponding functions.