Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over table of tables with the Lua C API

I'm trying to iterate over a table of tables in Lua and output:

  • The key of each table.
  • The key / value pair of each entry in each table.

Here is the code:

void print_table(lua_State *L)
{
    lua_pushnil(L);
    while(lua_next(L, -2) != 0) {
    const char *key = lua_tostring(L, -2);
            if(lua_isstring(L, -1))
                printf("%s = %s", key, lua_tostring(L, -1));
            else if(lua_isnumber(L, -1))
                printf("%s = %d", key, lua_tonumber(L, -1));
            else if(lua_istable(L, -1)) {
                printf("%s", key);
                PrintTable(L);
            }
            lua_pop(L, 1);
        }
    }
}

And here is an example of one of a table I'm trying to output:

s = {
        p = {
            n = "D",
            g = "1",
        },
        d = {
            l = "N",
            p = "N",
            u = "O",
            po = 100,
        },
        e = {
            {
                n = "B",
                l = "P",
                p = "P",
                u = "P",
                po = "P",
                pa = {
                    v = "4",
                    a = "U",
                    f = {
                        { name = "U", type = "U" },
                        { name = "A", type = "I" },
                        { name = "A", type = "I" },
                        { name = "P", type = "U" },
                        { name = "P", type = "U" },
                        { name = "P", type = "I" },
                        { name = "T", type = "U" },
                        { name = "D", type = "U" },
                        { name = "D", type = "I" },
                        { name = "S", type = "I" },
                        { name = "C", type = "U" },
                        { name = "G", type = "U" },
                        { name = "C", type = "F" },
                        { name = "C", type = "U" },
                    },
                },
                c = {
                    v = "1",
                    a = "",
                    f = {
                        { name = "B", type = "U" },
                        { name = "E", type = "F" },
                    },
                },
            },
        },
    }

The function crashes in the line:

while(lua_next(L, -2) != 0)

due to an invalid index. The line of script that causes the crash is:

{ name = "B", type = "U" },

I have to admit I'm not massively familiar with the stack in Lua, I've tried searching for similar answers and was unable to find any. Anyone know what I'm doing wrong?

Thanks!

Added working version in case anyone is interested:

void print_table(lua_State *L)
    {
        if ((lua_type(L, -2) == LUA_TSTRING))
            printf("%s", lua_tostring(L, -2));

        lua_pushnil(L);
        while(lua_next(L, -2) != 0) {
            if(lua_isstring(L, -1))
                printf("%s = %s", lua_tostring(L, -2), lua_tostring(L, -1));
            else if(lua_isnumber(L, -1))
                printf("%s = %d", lua_tostring(L, -2), lua_tonumber(L, -1));
            else if(lua_istable(L, -1)) {
                print_table(L);
            }
            lua_pop(L, 1);
        }
    }
like image 865
lamma Avatar asked Mar 26 '15 20:03

lamma


1 Answers

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.

like image 176
Adam Avatar answered Oct 12 '22 06:10

Adam