Iterating Through a Lua Table from C++

Iterate through Lua Table

Do not use luaL_checkstring with negative arguments. Use lua_tostring instead.

Also, make sure the stack remains the same after you call a function in the loop: lua_next expects the previous table key at the top of the stack so that it can resume the traversal.

How to iterate items in a table using the C API

You can use lua_next for this. When you initially push nil onto the Lua stack and invoke lua_next in a while-loop, you'll be able to get each key-value pair in the table.

After each successful call to lua_next, the value will be on top of the stack and the key will be right below it. If you want to keep on iterating over all pairs, you'll have to pop the value from the stack but leave the key on. This way, lua_next can determine the next key to visit.

You can also break out of the iteration, by popping both the key and value from the stack and breaking the while loop.

Below you will find a short example matching your table from your question.

void log_color(lua_State* L, const std::string& key) {
auto table_idx = lua_absindex(L, -1);
if (lua_type(L, table_idx) != LUA_TTABLE || lua_rawlen(L, table_idx) != 3) {
lua_pop(L, 2); /* pop table and key */
luaL_error(L, "key %s requires a table with 3 numeric values", key.c_str());
}

auto ttr = lua_rawgeti(L, table_idx, 1);
auto ttg = lua_rawgeti(L, table_idx, 2);
auto ttb = lua_rawgeti(L, table_idx, 3);

if (ttr != LUA_TNUMBER || ttg != LUA_TNUMBER || ttb != LUA_TNUMBER) {
lua_pop(L, 5); /* pop the 3 color values, the table and the key */
luaL_error(L, "the values for key %s should be all numeric", key.c_str());
}

std::cout << "found key " << key << " with rgb value " << lua_tonumber(L, -3) << ", " << lua_tonumber(L, -2) << ", " << lua_tonumber(L, -1) << "\n";
lua_pop(L, 3);
}

int myMaterialFunction(lua_State* L) {
std::string key;

luaL_checktype(L, 1, LUA_TTABLE);

// push nil for lua_next to indicate it needs to pick the first key
lua_pushnil(L);

while (lua_next(L, 1)) {
// key is at -2 on the stack, value at -1. We need to pop the value,
// but leave the key on the stack so that lua_next knows where to
// continue. You can do anything to process them at this point.

// i.e. when the key is not a string, ignore it;
if (lua_type(L, -2) != LUA_TSTRING) {
lua_pop(L, 1); /* pop the value */
continue;
}

// and then start processing the value if its key is a string;
key = lua_tostring(L, -2);
if (key == "color") {
log_color(L, key); /* process a color table */
} else if (key == "specular_intensity" || key == "shininess") {
// process a numeric value
if (lua_type(L, -1) != LUA_TNUMBER) {
lua_pop(L, 2);
luaL_error(L, "key %s requires a numeric value", key.c_str());
} else {
std::cout << "found key " << key << " with value " << lua_tonumber(L, -1) << "\n";
}
}

// pop the value when you're done with it
lua_pop(L, 1);
}

return 0;
}

Iterating over table of tables with the Lua C API

f = {
{ name = "B", type = "U" },
{ name = "E", type = "F" },
}

Is equivalent to:

f = {
[1] = { name = "B", type = "U" },
[2] = { name = "E", type = "F" },
}

When you call lua_tostring on the key Lua changes the numeric index to a string.

const char *key = lua_tostring(L, -2);

lua_tostring uses lua_tolstring and from the manual:

If the value is a number, then lua_tolstring also changes the actual value in the stack to a string. (This change confuses lua_next when lua_tolstring is applied to keys during a table traversal.)

Better to use lua_type to check if the key really is a string since lua_isstring will only tell you if the stack value can be converted into a string. You could also push a copy of the key and call lua_tostring on the copy.

How to loop through Lua table using key, value, and index

Iterator:

function triples(t)   
local function next_triple(tbl, idx)
idx = idx + 1
local k = tbl[idx]
if k ~= nil then
return idx, k, tbl[k]
end
end
return next_triple, t, 0
end

Usage:

local a = {"q", "w", "e", q = 11, w = 22, e = 33}
for i, k, v in triples(a) do
print(i, k, v)
end

Output:

1   q   11
2 w 22
3 e 33

Lua C++ Table Iteration

The bug is in lua_next(L, -2) line, because -2 refers to stack top minus one, which happens here to be the last argument to print.

Use lua_next(L, i) instead.

Upd: Lua stack indexes are subject to float when moving code around at development stage, so the general advice is to pin indexes with sml int t = lua_gettop(L) just after getting/pushing/considering values and to use that t instead of -n (though this specific case appears to be sort of a keystroke bug.)

Iterate through table using selected keys

you can do it this way

   local t = {
{a1 = 11, a2 = 22, c = 3, d = 4},
{a1 = 12, a2 = 23, c = 2, d = 4},
{a1 = 13, a2 = 24, c = 1, d = 4},
{a1 = 14, a2 = 25, c = 0, d = 4},
{a1 = 15, a2 = 26, c = 0, d = 4}
}
local keys_to_iterate = {"a1", "a2"}

for index = 1, #t do
for k = 1, #keys_to_iterate do
if t[index][keys_to_iterate[k]] then
print(keys_to_iterate[k] , t[index][keys_to_iterate[k]])
end
end
end

you can see it here

https://repl.it/repls/CoralIndianredVaporware

Lua : Iterating through nested tables

For the first loop the v variable is the table because you are inserting a table you were returned from the new function
and you're doing self.x = x and self.y = y and since you indexed self and defined self.x and self.y to be 9 and 10 last it prints 9 and 10
you should've done newSeriesPoint.x = x and newSeriesPoint.y = y

Iterate a lua table in c with a custom pair function

What you can do is write a custom next C function that mimics lua_next but works on that ordered table instead having opairs method.

int luaL_orderednext(luaState *L)
{
luaL_checkany(L, -1); // previous key
luaL_checktype(L, -2, LUA_TTABLE); // self
luaL_checktype(L, -3, LUA_TFUNCTION); // iterator
lua_pop(L, 1); // pop the key since
// opair doesn't use it

// iter(self)
lua_pushvalue(L, -2);
lua_pushvalue(L, -2);
lua_call(L, 1, 2);

if(lua_isnil(L, -2))
{
lua_pop(L, 2);
return 0;
}
return 2;
}

You can then use it in C similar to lua_next:

int orderedtraverse(luaState *L)
{
lua_settop(L, 1);
luaL_checktype(L, 1, LUA_TTABLE);

// t:opairs()
lua_getfield(L, 1, "opairs");
lua_pushvalue(L, -2);
lua_call(L, 1, 2);

// iter, self (t), nil
for(lua_pushnil(L); luaL_orderednext(L); lua_pop(L, 1))
{
printf("%s - %s\n",
lua_typename(L, lua_type(L, -2)),
lua_typename(L, lua_type(L, -1)));
}
return 0;
}

Note, I didn't test this but it should work.

How to iterate Lua table from end?

Thank you, @Piglet, for useful link.

local function reversedipairsiter(t, i)
i = i - 1
if i ~= 0 then
return i, t[i]
end
end
function reversedipairs(t)
return reversedipairsiter, t, #t + 1
end

Actually, I figured out an easier way may be to

local mytable = {'a', 'b', 'c'}
for i = #mytable, 1, -1 do
value = mytable[i]
print(i .. ": " .. value)
end


Related Topics



Leave a reply



Submit