Passing tables back from Java to Lua

I wouldn’t call myself a Java programmer by any stretch, and I’m struggling immensely to understand this one…

Within a native plugin, creating a basic class to return a string or integer etc is easy enough:

package plugin.somePlugin; import com.naef.jnlua.LuaState; public class returnSomething implements com.naef.jnlua.NamedJavaFunction { // This reports a class name back to Lua during the initiation phase. @Override public String getName() { return "returnSomething"; } // This is what actually gets invoked by the Lua call @Override public int invoke(final LuaState luaState) { luaState.pushString("Something"); return 1; } }

I follow enough of the underlying mechanics to understand that pushString is submitting to some kind of an event queue that the Lua function making the call can interpret as a response, and that you can push multiple values back this way within the same routine as that same response. I don’t fully understand how, but I get that it happens.

Now, what I’m trying to get my head around is how to return table data. More precisely, a 3D assoc array.of data.

I’ve crawled an endless number of examples and forum posts, and as far as I understand it you can create Lua tables directly through luaState calls, passing key->val pairs in reverse order via pushString/pushBoolean/etc and setField calls. So something like this:

package plugin.somePlugin; import com.naef.jnlua.LuaState; public class returnSomething implements com.naef.jnlua.NamedJavaFunction { // This reports a class name back to Lua during the initiation phase. @Override public String getName() { return "returnSomething"; } // This is what actually gets invoked by the Lua call @Override public int invoke(final LuaState luaState) { luaState.newTable(); Integer tableIndex = luaState.getTop(); luaState.pushString("Some value"); luaState.setField(tableIndex, "some key"); luaState.pushString("Another value"); luaState.setField(tableIndex, "another key"); luaState.pushString("Some other value"); luaState.setField(tableIndex, "some other key"); tableIndex = luaState.getTop(); luaState.pushString("Some value on row 2"); luaState.setField(tableIndex, "some key"); luaState.pushString("Another value on row 2"); luaState.setField(tableIndex, "another key"); luaState.pushString("Some other value on row 2"); luaState.setField(tableIndex, "some other key"); return 1; } }

But this doesn’t seem to be working…? I’m guessing I’ve totally misunderstood what getTop() does, or I’m missing some kind of a finalise call at the end to actually push the queue back. Either way, currently when calling this function from within Lua, the app just locks up. No errors, it just hangs.

As always, all help is very much appreciated please.

Hmm…

Almost there, and understanding how this works a little better now. For anybody stumbling onto this post trying to figure this stuff out, I’ll try to be helpful with the comment lines…

My code is now as follows (actually it isn’t, I’m just rehashing into minimalist scripts to post here):

package plugin.somePlugin; import com.naef.jnlua.LuaState; public class returnSomething implements com.naef.jnlua.NamedJavaFunction { // This reports a class name back to Lua during the initiation phase. @Override public String getName() { return "returnSomething"; } // This is what actually gets invoked by the Lua call @Override public int invoke(final LuaState luaState) { // Creates a table in the stack luaState.newTable(); // Gets the stack ID of this table Integer tableIndex = luaState.getTop(); // Just something to increment as an ID for each table row Integer rowId = 0; // Creates another table on the stack for an individual row of data luaState.newTable(); // Gets the stack ID of this table Integer tableRowIndex = luaState.getTop(); // Creates some columns in this data row luaState.pushString("Some value"); luaState.setField(tableRowIndex, "some key"); luaState.pushString("Another value"); luaState.setField(tableRowIndex, "another key"); luaState.pushString("Some other value"); luaState.setField(tableRowIndex, "some other key"); // Increment the rowID to create a new field for within the parent table rowID++; // Sets this entire inner table as a new field on the outer // For some reason this has to be a string though? luaState.setField(tableIndex, "" + rowID) // Onto the next row. Exactly the same as above, but with different values // Obviously this would be in a loop of some sort really, in the real world... luaState.newTable(); tableRowIndex = luaState.getTop(); luaState.pushString("Some value on row 2"); luaState.setField(tableRowIndex, "some key"); luaState.pushString("Another value on row 2"); luaState.setField(tableRowIndex, "another key"); luaState.pushString("Some other value on row 2"); luaState.setField(tableRowIndex, "some other key"); rowID++; luaState.setField(tableIndex, "" + rowID) return 1; } }

And within Lua if I now do this:

local somePlugin = require "plugin.somePlugin" local someData = somePlugin.returnSomething() print(someData["2"]["some key"])

I see “Some value on row 2” in the console. Brilliant.

I’m still struggling with one more thing though. I’d prefer to be able to index this as someData[2][“some key”] rather than the outer index needing to be a string. setField() seems to require string values though?

suggest you switch from setfield() to settable() for full control - have table on top of stack, push a value, push a key (of any supported type, including number), call settable(), done.  ottomh as “clue” only, and may have key, value order reversed - consult docs for actual implementation details.

Thanks Dave, that’s done the trick perfectly! I wasn’t sure what the purpose of setTable was before, but now that ‘the stack’ is making sense, the documentation seems more understandable too :smirk:

Hmm…

Almost there, and understanding how this works a little better now. For anybody stumbling onto this post trying to figure this stuff out, I’ll try to be helpful with the comment lines…

My code is now as follows (actually it isn’t, I’m just rehashing into minimalist scripts to post here):

package plugin.somePlugin; import com.naef.jnlua.LuaState; public class returnSomething implements com.naef.jnlua.NamedJavaFunction { // This reports a class name back to Lua during the initiation phase. @Override public String getName() { return "returnSomething"; } // This is what actually gets invoked by the Lua call @Override public int invoke(final LuaState luaState) { // Creates a table in the stack luaState.newTable(); // Gets the stack ID of this table Integer tableIndex = luaState.getTop(); // Just something to increment as an ID for each table row Integer rowId = 0; // Creates another table on the stack for an individual row of data luaState.newTable(); // Gets the stack ID of this table Integer tableRowIndex = luaState.getTop(); // Creates some columns in this data row luaState.pushString("Some value"); luaState.setField(tableRowIndex, "some key"); luaState.pushString("Another value"); luaState.setField(tableRowIndex, "another key"); luaState.pushString("Some other value"); luaState.setField(tableRowIndex, "some other key"); // Increment the rowID to create a new field for within the parent table rowID++; // Sets this entire inner table as a new field on the outer // For some reason this has to be a string though? luaState.setField(tableIndex, "" + rowID) // Onto the next row. Exactly the same as above, but with different values // Obviously this would be in a loop of some sort really, in the real world... luaState.newTable(); tableRowIndex = luaState.getTop(); luaState.pushString("Some value on row 2"); luaState.setField(tableRowIndex, "some key"); luaState.pushString("Another value on row 2"); luaState.setField(tableRowIndex, "another key"); luaState.pushString("Some other value on row 2"); luaState.setField(tableRowIndex, "some other key"); rowID++; luaState.setField(tableIndex, "" + rowID) return 1; } }

And within Lua if I now do this:

local somePlugin = require "plugin.somePlugin" local someData = somePlugin.returnSomething() print(someData["2"]["some key"])

I see “Some value on row 2” in the console. Brilliant.

I’m still struggling with one more thing though. I’d prefer to be able to index this as someData[2][“some key”] rather than the outer index needing to be a string. setField() seems to require string values though?

suggest you switch from setfield() to settable() for full control - have table on top of stack, push a value, push a key (of any supported type, including number), call settable(), done.  ottomh as “clue” only, and may have key, value order reversed - consult docs for actual implementation details.

Thanks Dave, that’s done the trick perfectly! I wasn’t sure what the purpose of setTable was before, but now that ‘the stack’ is making sense, the documentation seems more understandable too :smirk: