MaxLua and arrays

BlitzMax Forums/BlitzMax Programming/MaxLua and arrays

dan_upright(Posted 2014) [#1]
Can anyone explain to me how passing arrays between blitz and lua works? If I return an array from a blitz function, I can call that function from lua, then pass the result to another blitz function that expects an array and it works fine. What I can't figure out is how to do anything with the array inbetween those two points. I thought it'd get passed as a table but apparently it's userdata and I can't figure out what to do with that.


Derron(Posted 2014) [#2]


Arrays seem to get packed in a "table". This is like it is done in maxlua.mod (except the 1based approach).

I just tested it ... and it did not work ...ok so I checked what wents wrong: When invoking a blitzmax function/method from within LUA ... there is a bug avoiding running lua_pusharray:

Open up maxlua.bmx and search for "Method _invoke":



It might be needed to add that same thing to other portions of the code too (if you eg. invoke lua functions from blitzmax - and some of the function params are arrays) . But: this would make things just "readable" (as it converts the data to a table/pair of "key,value").


To make it "modifyable" we would need to read/modify the userdata it would send without the above modification:
http://www.lua.org/pil/28.1.html

Another optionis the following: provide the array as a field of something, so the "NewIndex"-function gets called. Currently it does not handle "arrays", so we would have to do this on our own. Means you would have to read the size of the array and then for each entry read from the stack and add this to the array.


---
Instead of adding "array" handling, it would also be possible to extend "lua_unboxobject" and "lua_boxobject" (c code, I am not that firm in that) so they handle "BBARRAY" too.

Seems nilium (or noelCower) has done this similar in his LuGI-module:
https://github.com/nilium/lugi.mod/blob/master/core.mod/lgcore.cpp


I do not know if that works with "local variables" too ...or if this all is restricted to types and their children.


EDIT: I forgot to mention something: To directly manipulate "variables" it might be needed to add specific functions to LUA, so you can access them with "array[index]" in lua, and modifications will then be triggering functions correctly. For an example how nilium has done this, search for
static int lugi_newindex_array(lua_State *state)
in the linked lgcore.cpp-file.

When linking the Blitzmax data with the Lua data you send a copy of your data (exception for "objects" there you send a memory pointer + "information"). I think if you send an array as "userdata", you cannot use them as "table" in lua but must provide your own "custom" functionality (array.Get(), array.Set() ...). These custom functionalities also contain definitions what functions of blitzmax have to get called in the case of modifications (eg. a custom "newIndex" function - newIndex is getting called as soon as a value gets changed).




PS: currently I avoid the array thing with having functions like "GetItemCount()", "GetItemAtPosition(position)" :D.

Hope that helps somehow.

bye
Ron


dan_upright(Posted 2014) [#3]
Thanks for the explainations mate, I'll keep this thread bookmarked in case I want to do a better solution at a later date but for now I went with:
Type ArrayWrapper
	Method Create:String[] (sz:Int)
		Local arr:String[] = New String[sz]
		Return arr
	End Method
	
	Method Get:String(arr:String[], index:Int)
		Return arr[index]
	End Method
	
	Method Set(arr:String[], index:Int, value:String)
		arr[index] = value
	End Method
	
	Method Size:Int(arr:String[])
		Return arr.Length
	End Method
End Type

LuaRegisterObject(New ArrayWrapper, "Array")



dan_upright(Posted 2014) [#4]
On a somewhat related note, if I set a blitzmax object to nil in a lua script, I get the dreaded EXCEPTION_ACCESS_VIOLATION message. The object itself is fine - I can assign other values to it without any errors, just not nil. How do I do this without resorting to a SetObjectNull() function?

The error occurs in MaxLua's NewIndex function, if that helps.


Derron(Posted 2014) [#5]
Open up "maxlua.bmx", search for the "NewIndex"-function.

replace
		Select fld.TypeId()
		Case IntTypeId, ShortTypeId, ByteTypeId, LongTypeId
			fld.SetInt obj,lua_tointeger( L,3 )
		Case FloatTypeId
			fld.SetFloat obj,lua_tonumber( L,3 )
		Case DoubleTypeId
			fld.SetDouble obj,lua_tonumber( L,3 )
		Case StringTypeId
			fld.SetString obj,lua_tostring( L,3 )
		Default
			fld.Set obj,lua_unboxobject( L,3 )
		End Select


with
		If lua_isnil(L, 3)
			Select fld.TypeId()
				Case IntTypeId, ShortTypeId, ByteTypeId, LongTypeId, FloatTypeId, DoubleTypeId, StringTypeId
					'SetInt/SetFloat/...all convert to a string
					'"null" is 0/0.0/"" for primitive types in BlitzMax
					fld.SetString(obj, "")
				Default
					fld.Set(obj, null)
			End Select
		Else
			Select fld.TypeId()
			Case IntTypeId, ShortTypeId, ByteTypeId, LongTypeId
				fld.SetInt obj,lua_tointeger( L,3 )
			Case FloatTypeId
				fld.SetFloat obj,lua_tonumber( L,3 )
			Case DoubleTypeId
				fld.SetDouble obj,lua_tonumber( L,3 )
			Case StringTypeId
				fld.SetString obj,lua_tostring( L,3 )
			Default
				fld.Set obj,lua_unboxobject( L,3 )
			End Select
		EndIf



if you change it, just also change this:

from
Function lua_pushobject( L:Byte Ptr,obj:Object )
	lua_boxobject L,obj
	lua_rawgeti L,LUA_REGISTRYINDEX,ObjMetaTable()
	lua_setmetatable L,-2
End Function


to

Function lua_pushobject( L:Byte Ptr,obj:Object )
	'convert BlitzMax "null"-objects to lua compatible "nil" values
	If obj = null
		lua_pushnil(L)
	Else
		lua_boxobject L,obj
		lua_rawgeti L,LUA_REGISTRYINDEX,ObjMetaTable()
		lua_setmetatable L,-2
	EndIf
End Function


So "null objects" you send to lua could be correctly recognized as being "nil".



PS: similar to this "pushObject"-call one would have to handle "pushArray" if done with a "array-table", instead of "ObjMetaTable" you would then have to send the ArrayMetaTable() you had created before. This metatable would contain other function pointers for "index, newIndex, __set, __get, ...".


bye
Ron


dan_upright(Posted 2014) [#6]
Cheers mate, that's a big help. =]