Reflection: Invoking functions

BlitzMax Forums/BlitzMax Programming/Reflection: Invoking functions

Today I wanted to use the new reflection features of BMax. However, to my surprise, I wasn't able to find support for regular functions.

Is there a way to make it work, or the possibility to add it in future?

Please don't tell me to use function pointers. In my app, I get to know the function arguments at runtime, thus I can't use function pointers because the prototypes are hardcoded.
Something like the object-array of the Invoke-Method which is passed as a chain of arguments would be perfect for me.

Thanks in advance!

i'm interested too in calling static method (functions) by refletcion. Reading the documentation seems it isn't supported.....

There is support for neither Functions nor Global fields using BRL.Reflection.

Could you use a Method that does nothing but calls the function? Of course, you still need an object and it's not always possible (or a good idea) to create a dummy object for calling it...

I think that's about the only way to call a function of an unknown type, since fields of a function type are invisible to reflection.

Unfortunately, besides the fact that this kind of workaround would be horrible, I still wouldn't know the arguments of the function which I would put in the method.

maybe something like this?

SuperStrict

Type TTest
	Field x:Int=0
	Global globalX:Int=0

	Method CallsetGlobalX:Int(x:Int) 
		Return TTest.setGlobalX(x)
	EndMethod
	
	Function setGlobalX:Int(x:Int)
		globalX = x; Return globalX
	EndFunction
EndType

Print TTest.globalX

Local staticCall:String="Call"
Local obj:TTest  = New TTest
Local id:TTypeId= TTypeId.ForObject( obj )

id.FindMethod(staticCall + "setGlobalX").Invoke obj , ["25"]

Print TTest.globalX


Unfortunately, besides the fact that this kind of workaround would be horrible, I still wouldn't know the arguments of the function which I would put in the method.


Yeah, it is horrible. Do you really need a function in the first place? Could it be a method instead? I don't think there's a good way to do what you are trying to do.

OT: why is my post older then otus one but it's before?
EDIT: not now but when time was in minutes...

@johnnyfreak: Thank you for your code. It's nice to see that it can be done theoretically this way. Sadly, I still woudln't like to implement it this way.

I'm designing some kind of extremly basic script stuff, where functions should be called from external files, like:

script:
function(1, 2, 3)


The implementation should look something (!) like this:

bmax:
Function Func(x:Int, y:Int, z:Int)

 Print x+y+z

End Function

tScript.AddFunc(Byte Ptr(Func), "function(int x, int y, int z)")


Of course I could do it in a different way with only one type of function with defined arguments. But a solution with reflection is a 100 times superior!

Is it technically harder to implement reflection on functions? :/ I don't understand why it it only for methods!

You may take a look to BriskVM. It is a comercial max module that covers all this stuff in a wonderful way.

As a programmer. I'm aware of differenct script engines.

If I would like to use one, I would use one and would not create forum topics because I'm stuck on my own one.

Ok, next time I'll remember that as a programer you don't need help.

*pwned*

back to topic: I'm struggling with the same problem. Creating dummies to call methods that rather should be functions is horrible. There has to be an elegant workaround. I'll keep trying.

Okay, I just came up with a solution to the problem in my case, but it's not universal: I created another type, which contains the former globals I want access to as fields. All original type objects link to objects of that new type, which (in my case) are no dummies, because they serve other purposes as well and aren't deleted after field/method access.

How about a reflection extension in the next official patch?

Is it technically harder to implement reflection on functions? :/ I don't understand why it it only for methods!


I imagine it's because of the way functions are handled in BlitzMax. They are a type of "object" that can be assigned to variables - unlike methods.

("Object", because they don't derive from Object.)

How about a reflection extension in the next official patch?


That would be nice!

How about this little workaround/hack?

SuperStrict
Framework brl.standardio
Import brl.reflection

Function testFunction:String(str:String)
	Return "testFunction(~q"+str+"~q)"
EndFunction

Type TTestType
	Method testMethod:String(str:String)
		Return "testMethod(~q"+str+"~q)"
	EndMethod
EndType

Local functionDelegate:TDelegate=New TFunctionDelegate.create(testFunction,StringTypeId,[StringTypeId])
Local methodDelegate:TDelegate=New TMethodDelegate.create(New TTestType,"testMethod")

Print String(functionDelegate.call(["test"]))
Print String(methodDelegate.call(["test"]))

Rem
	Definitions
EndRem

Type TDelegate Abstract
	Method call:Object(args:Object[]=Null) Abstract
EndType

Type TMethodDelegate Extends TDelegate
	Field this:Object
	Field methodPtr:TMethod

	Method Delete()
		this=Null
		methodPtr=Null
	EndMethod

	Method Create:TDelegate(this:Object,name:String)
		If Not this Return Null
		Local id:TTypeId=TTypeId.ForObject(this)
		self.methodPtr=id.findMethod(name)
		If Not self.methodPtr Return Null
		self.this=this
		Return Self
	EndMethod

	Method call:Object(args:Object[]=Null)
		Return methodPtr.invoke(this,args)
	EndMethod
EndType

Type TFunctionDelegate Extends TDelegate
	Field functionPtr:Byte Ptr
	Field returnType:TTypeId
	Field argTypes:TTypeId[]

	Method Delete()
		functionPtr=Null
		returnType=Null
		argTypes=Null
	EndMethod

	Method Create:TFunctionDelegate(functionPtr:Byte Ptr,returnType:TTypeId,argTypes:TTypeId[])
		self.functionPtr=functionPtr
		self.returnType=returnType
		self.argTypes=argTypes
		Return Self
	EndMethod
	
	Method call:Object(args:Object[]=Null)
		Return _Call(functionPtr,returnType,args,argTypes)
	EndMethod
EndType

Private ' brl.reflection

Extern
	Function bbRefPushObject(p:Byte Ptr,obj:Object)
	Function bbRefGetObjectClass(obj:Object)
	Function bbRefGetSuperClass(class:Int)
EndExtern

Function _Push:Byte Ptr( sp:Byte Ptr,typeId:TTypeId,value:Object )
	Select typeId
	Case ByteTypeId,ShortTypeId,IntTypeId
		(Int Ptr sp)[0]=value.ToString().ToInt()
		Return sp+4
	Case LongTypeId
		(Long Ptr sp)[0]=value.ToString().ToLong()
		Return sp+8
	Case FloatTypeId
		(Float Ptr sp)[0]=value.ToString().ToFloat()
		Return sp+4
	Case DoubleTypeId
		(Double Ptr sp)[0]=value.ToString().ToDouble()
		Return sp+8
	Case StringTypeId
		If Not value value=""
		bbRefPushObject sp,value
		Return sp+4
	Default
		If value
			Local c:Int=typeId._class
			Local t:Int=bbRefGetObjectClass( value )
			While t And t<>c
				t=bbRefGetSuperClass( t )
			Wend
			If Not t Throw "ERROR"
		EndIf
		bbRefPushObject sp,value
		Return sp+4
	End Select
End Function

Function _Call:Object( p:Byte Ptr,typeId:TTypeId,args:Object[],argTypes:TTypeId[] )
	Local q:Int[10],sp:Byte Ptr=q
	If typeId=LongTypeId sp:+8
	For Local i:Int=0 Until args.length
		If Int Ptr(sp)>=Int Ptr(q)+8 Throw "ERROR"
		sp=_Push( sp,argTypes[i],args[i] )
	Next
	If Int Ptr(sp)>Int Ptr(q)+8 Throw "ERROR"
	Select typeId
	Case ByteTypeId,ShortTypeId,IntTypeId
		Local f(p0:Int,p1:Int,p2:Int,p3:Int,p4:Int,p5:Int,p6:Int,p7:Int)=p
		Return String.FromInt( f( q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7] ) )
	Case LongTypeId
		Throw "TODO"
	Case FloatTypeId
		Local f:Float(p0:Int,p1:Int,p2:Int,p3:Int,p4:Int,p5:Int,p6:Int,p7:Int)=p
		Return String.FromFloat( f( q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7] ) )
	Case DoubleTypeId
		Local f:Double(p0:Int,p1:Int,p2:Int,p3:Int,p4:Int,p5:Int,p6:Int,p7:Int)=p
		Return String.FromDouble( f( q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7] ) )
	Default
		Local f:Object(p0:Int,p1:Int,p2:Int,p3:Int,p4:Int,p5:Int,p6:Int,p7:Int)=p
		Return f( q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7] )
	End Select
EndFunction


Just had to remove the obj-reference-push in the _Call Function from the Reflection Module.


- FOODy

How about use attributes to create entries for functions, globals and const?
That can even be automated through a pretty simple precompiler.

@FOODy: This looks pretty interesting. Will have a look at it!

@Dreamora: Don't really get your point. :/