Invoke argument count limit?

BlitzMax Forums/BlitzMax Programming/Invoke argument count limit?

LT(Posted 2016) [#1]
I've ran into a strange issue with Invoke and I'm just wondering if anyone has encountered the same thing..? Two TMethods, calling Invoke() and passing arrays of Int (as String) for arguments like so...

[ "0", "0", "-1", "0", "0", "-1" ]

[ "1", "1", "0", "0", "0", "0", "0", "0", "-1", "-1", "-1", "0", "0", "0", "0" ]

First one works; second one throws an error in the _Call function in reflection.bmx.

For Local i=0 Until args.length
If Int Ptr(sp)>=Int Ptr(q)+8 Throw "ERROR" <<<<< This line...
sp=_Push( sp,argTypes[i],args[i] )
Next

The only obvious difference is that one has more arguments, which leads me to believe there's an argument count limitation somewhere. Otherwise, I have no clue.

Anyone know what is happening here?


Brucey(Posted 2016) [#2]
There's a maximum limit of 8 arguments.

You can see it in the large case statements in _call() in reflection.bmx :-)

I suppose Mark thought it was a reasonable limit.

Another option might be to pass a Type with fields representing all your arguments.


LT(Posted 2016) [#3]
Okay, but there are no large case statements in the reflection.bmx file I'm using. Not one...

Thanks for your response, but I'm very surprised that would be considered a reasonable limit.

EDIT: Increasing the limit wasn't too hard, but now my reflection.bmx is out of synch. :/


Yasha(Posted 2016) [#4]
Look at how the method is actually called. It's through a BlitzMax function pointer declared as `f` and cast from raw pointer type. The number and type of arguments is part of the type signature of the variable in BlitzMax, making it fixed at compile-time. The only way to implement this without some arg count limit would be to write _Call in assembly, rather than BlitzMax or C. (Arguably a better way to do things since the existing implementation makes assumptions about the behaviour of the underlying assembly anyway.)

More arguments will imply some performance hit, although my guess is that upping the count to 16 or even 128 would probably be lost in the noise compared to all the other stuff Invoke does.

Maybe Mark held to the idea that functions having that many arguments indicates bad design... maybe consider a fluent interface instead?


LT(Posted 2016) [#5]
IMO, making assumptions about how things are used and imposing hard limits IS generally bad design, but I don't fault Mark too much in this case. Obviously, this implementation required a limit for the reasons you stated (I changed mine to 16). There are no comments in the version of reflection.bmx that I'm using that indicate the hard limit.

I wouldn't use reflection at all if performance was a concern. My engine offers functions for constructing objects from Lua and setting a hard limit on argument count seems like a bad idea. That's not a problem on the Lua side, but I wanted something on the Blitz side to test the procedures. It has to be able to look up a method based on name (which is already slow) and then call it with however many arguments the user wants. It's not up to me to decide that for them.


Derron(Posted 2016) [#6]
Best to do that is to wrap your arguments in a custom type (like suggested by Brucey already):

TLuaArguments
TLuaResult
...

Each of them could then contain lists, maps, fields ...


In Lua you could create a TLuaArguments-instance and then via helper methods you expose adding arguments

bla = CreateLuaArgument()
bla.AddNumber(1, 123)
bla.AddString(2, "test")
bla.AddObject(3, anotherobject)
CallBlitzMaxFuncXY(bla)

first number in the brackets = argument index

This of course can get simulated from Blitz too.

bye
Ron


grable(Posted 2016) [#7]
Ive wanted to do this for a long time, and after reading this i finally caved in :p

Check out BRL.Reflection: Pointer Support.
Specifically callmethod.s in the top post, and how to use it in _Call.

There are some changes to _Call though, so need to remove the If clause with PointerTypeId and FunctionTypeId if using it with vanilla reflection.


LT(Posted 2016) [#8]
after reading this I finally caved in
Hey thanks, grable! Just tried it and it worked without a hitch. :)

I'm not quite understanding your last comment about PointerTypeId. I'm using vanilla and it worked without changing anything.

Since this change is x86-only, what would happen if someone tried the test on a different OS?

wrap your arguments in a custom type
Seems like extra work on the Lua side. Right now most of my Lua exposed functions are wrappers for FFI calls. It was my understanding that this is the fastest method and that was validated in my tests.

On the Blitz side, I don't know how I could call methods from types defined in outside modules without using Invoke.


grable(Posted 2016) [#9]
I'm not quite understanding your last comment about PointerTypeId. I'm using vanilla and it worked without changing anything.
My modified reflection supports pointers and function types and other stuff, so copying the _Call function without removing those would not work.
Hence my comment. But i suspect you just replaced the entire mod, in which case you can ignore it ;)


LT(Posted 2016) [#10]
Ah, okay. That makes sense.