Lua C API - Returning reference to passed table

Warning.  This is a question where I’m probably not thinking correctly about the problem and thus far have not found an article or similar question+answer on the web.  In fact I know this because this is a fundamental operation.

The problem

I am writing a plugin that takes a table of tables as one of the arguments, processes those sub-tables, and then returns a reference to one of those sub tables.  My issue is quite simply, I can’t figure out how to return a reference to a table.

What I know how to do

  • Traverse the table of tables and ‘identify’ the sub-table I want to return.
  • Create new tables and return those.  No good because I need to return a reference to the original (sub-) table.
  • Return just about any other kind of data.

Plugin Code Showing Basics of Traversal

Here is an example of a function that iterates over a table of tables (passed as first argument) and returns the id field in the (last) sub-table that has the tag field set to ‘1’.

Yes, this is a totally bogus piece of code, but it shows how I’m traversing the table of tables, and simplifies the ‘sub-table’ selection logic for this example.

I’d rather return a reference to the sub-table, but I’m not sure how.  

int getTaggedTableID(lua\_State \*L, int idx ) { lua\_pushnil(L); int curTag = 0; int curID = -1; while (lua\_next(L, idx) != 0) { if (lua\_type(L, VAL) == LUA\_TTABLE) { int top = lua\_gettop(L); lua\_getfield(L, top, "tag"); lua\_getfield(L, top, "id"); int tag = luaL\_checkint(L, -2); int id = luaL\_checkint(L, -1); if(tag == 1) { curID = id; }; lua\_pop(L, 2); //lua\_pop(L, 1); }; lua\_pop(L, 1); } return curID; // Would rather return table reference here } int getTaggedObject(lua\_State \*L) { int curID = -1; curID = getTaggedTableID(L, 1); lua\_pushinteger(L, curID); // Would rather return table reference here return 1; }

Calling this code in lua:

local tbls = { { tag = 0, id = 1 }, { tag = 0, id = 2 }, { tag = 1, id = 3 }, { tag = 0, id = 4 }, { tag = 0, id = 5 }, } local id = objTests.getTaggedObject(tbls) print("getTaggedObject(tbls) returned: ", id )

Prints 3, but I’d rather get a reference to the third table instead.

Note: I’m taking a break and when I come back this may suddenly be clear as day, but if I’m simply thinking of the problem incorrectly and you have a googled reference or references, please feel free to link them here.

Cheers,

Ed

You’ll want to use the lua_pushvalue() function…

   http://pgl.yoyo.org/luai/i/lua_pushvalue

That function pushes a copy of the indexed object to the top of the stack.  And note that this doesn’t do a deep copy of the table either.  Lua tables (as well as strings and functions) are always copied by reference, just like how it works in Lua script.

Ah, that makes sense.  So I should be able to call ‘luaL_ref()’ to get the index that I’ll later use in the call to lua_pushvalue().

luaL\_ref(L, LUA\_REGISTRYINDEX)

I played around with this a bit, but thought I’d gone down the wrong path.

Thanks,

Ed

For your code that returns a table from the Lua registry, another useful function is lua_insert()…

   http://pgl.yoyo.org/luai/i/lua_insert

That function will pop off the Lua object at the top and insert it to a stack index of your choosing. It’s useful if you need to push several objects to the top of the stack such as Lua libraries/plugins/modules and their functions to get to the object you wanted, insert the top-most result object to the bottom of the stack, and then pop off all the object modules and functions.  You wouldn’t use it for your Lua function callbacks (you shouldn’t re-order the received arguments at the top of the stack), but it comes in handy for other cases.

That’s got it!  Thanks Joshua.

int getTaggedTableRef(lua\_State \*L, int idx) { lua\_pushnil(L); int curTag = 0; int curID = -1; while (lua\_next(L, idx) != 0) { if (lua\_type(L, VAL) == LUA\_TTABLE) { int top = lua\_gettop(L); lua\_getfield(L, top, "tag"); int tag = lua\_tointeger(L, -1); lua\_pop(L, 1); if (tag == 1) { lua\_pushvalue(L, top); curID = luaL\_ref(L, LUA\_REGISTRYINDEX); }; }; lua\_pop(L, 1); } return curID; } int getTaggedObject(lua\_State \*L) { int curID = -1; curID = getTaggedTableRef(L, 1); lua\_rawgeti(L, LUA\_REGISTRYINDEX, curID); lua\_pushvalue(L, lua\_gettop(L)); luaL\_unref(L, LUA\_REGISTRYINDEX, curID); return 1; }

Happy to help!  :)

I’ve got one more tip for you.  The below shows you how to loop through the pairs in a Lua table for-each style.  Your way works too, but the below is a little easier to copy-and-paste since it’s all encapsulated in a single for loop statement and it allows you to use continue statements within your loop easily.

for (lua\_pushnil(L); lua\_next(L, index); lua\_pop(L, 1)) { // Fetch the next entry here. }

Note: I’m taking a break and when I come back this may suddenly be clear as day, but if I’m simply thinking of the problem incorrectly and you have a googled reference or references, please feel free to link them here.

Cheers,

Ed

You’ll want to use the lua_pushvalue() function…

   http://pgl.yoyo.org/luai/i/lua_pushvalue

That function pushes a copy of the indexed object to the top of the stack.  And note that this doesn’t do a deep copy of the table either.  Lua tables (as well as strings and functions) are always copied by reference, just like how it works in Lua script.

Ah, that makes sense.  So I should be able to call ‘luaL_ref()’ to get the index that I’ll later use in the call to lua_pushvalue().

luaL\_ref(L, LUA\_REGISTRYINDEX)

I played around with this a bit, but thought I’d gone down the wrong path.

Thanks,

Ed

For your code that returns a table from the Lua registry, another useful function is lua_insert()…

   http://pgl.yoyo.org/luai/i/lua_insert

That function will pop off the Lua object at the top and insert it to a stack index of your choosing. It’s useful if you need to push several objects to the top of the stack such as Lua libraries/plugins/modules and their functions to get to the object you wanted, insert the top-most result object to the bottom of the stack, and then pop off all the object modules and functions.  You wouldn’t use it for your Lua function callbacks (you shouldn’t re-order the received arguments at the top of the stack), but it comes in handy for other cases.

That’s got it!  Thanks Joshua.

int getTaggedTableRef(lua\_State \*L, int idx) { lua\_pushnil(L); int curTag = 0; int curID = -1; while (lua\_next(L, idx) != 0) { if (lua\_type(L, VAL) == LUA\_TTABLE) { int top = lua\_gettop(L); lua\_getfield(L, top, "tag"); int tag = lua\_tointeger(L, -1); lua\_pop(L, 1); if (tag == 1) { lua\_pushvalue(L, top); curID = luaL\_ref(L, LUA\_REGISTRYINDEX); }; }; lua\_pop(L, 1); } return curID; } int getTaggedObject(lua\_State \*L) { int curID = -1; curID = getTaggedTableRef(L, 1); lua\_rawgeti(L, LUA\_REGISTRYINDEX, curID); lua\_pushvalue(L, lua\_gettop(L)); luaL\_unref(L, LUA\_REGISTRYINDEX, curID); return 1; }

Happy to help!  :)

I’ve got one more tip for you.  The below shows you how to loop through the pairs in a Lua table for-each style.  Your way works too, but the below is a little easier to copy-and-paste since it’s all encapsulated in a single for loop statement and it allows you to use continue statements within your loop easily.

for (lua\_pushnil(L); lua\_next(L, index); lua\_pop(L, 1)) { // Fetch the next entry here. }