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
C++ Push Multiple Types Onto Vector
Where in Memory Is Vtable Stored
Opencv Grouprectangles - Getting Grouped and Ungrouped Rectangles
How to Create/Read/Write JSON Files in Qt5
Cannot Open Include File: 'Stdio.H' - Visual Studio Community 2017 - C++ Error
What Is "->" After Function Declaration
How to Use a Std::Valarray to Store/Manipulate a Contiguous 2D Array
Forcing Nvidia Gpu Programmatically in Optimus Laptops
Why "Universal References" Have the Same Syntax as Rvalue References
Default Move Constructor/Assignment and Deleted Copy Constructor/Assignment
How to Use an Enum Value in a Switch Statement in C++