lua c read nested tables

1.5k views Asked by At

below is the lua table i need to read from C:

listen = {
    { port = 1234, address = "192.168.1.1", userdata = "liunx" },
    { port = 1235, address = "192.168.1.2", userdata = "liunx1" },
    { port = 1236, address = "192.168.1.3", userdata = "liunx2" }
}

below is the c code:

#include <lua.h>                                /* Always include this when calling Lua */
#include <lauxlib.h>                            /* Always include this when calling Lua */
#include <lualib.h>                             /* Prototype for luaL_openlibs(), */
/*   always include this when calling Lua */

#include <stdlib.h>                             /* For function exit() */
#include <stdio.h>                              /* For input/output */

void bail(lua_State *L, char *msg){
    fprintf(stderr, "\nFATAL ERROR:\n  %s: %s\n\n",
            msg, lua_tostring(L, -1));
    exit(1);
}

int main(void)
{
    lua_State *L;
    L = luaL_newstate();                        /* Create Lua state variable */
    luaL_openlibs(L);                           /* Load Lua libraries */
    if (luaL_loadfile(L, "cfg.lua")) 
        bail(L, "luaL_loadfile() failed");
    if (lua_pcall(L, 0, 0, 0))           
        bail(L, "lua_pcall() failed");
    // how to read???
    lua_getglobal(L, "listen");
    lua_close(L);
    return 0;
}

I want to travel this table which may contain a few number of data in while loop, but really do not know how to do it, so any tips?

Thanks very much for your tips!Below are the worked code:

#include <lua.h>                                /* Always include this when calling Lua */
#include <lauxlib.h>                            /* Always include this when calling Lua */
#include <lualib.h>                             /* Prototype for luaL_openlibs(), */
/*   always include this when calling Lua */

#include <stdlib.h>                             /* For function exit() */
#include <stdio.h>                              /* For input/output */

void bail(lua_State *L, char *msg)
{
    fprintf(stderr, "\nFATAL ERROR:\n  %s: %s\n\n",
            msg, lua_tostring(L, -1));
    exit(1);
}

int main(void)
{
    lua_State *L;

    static struct {
        const char * name;
        int type;
    } fields[] = {
        {"port", LUA_TNUMBER},
        {"address", LUA_TSTRING},
        {"userdata", LUA_TSTRING},
        {NULL, 0}
    };

    L = luaL_newstate();                        /* Create Lua state variable */
    luaL_openlibs(L);                           /* Load Lua libraries */

    if (luaL_loadfile(L, "cfg.lua")) 
        bail(L, "luaL_loadfile() failed");

    if (lua_pcall(L, 0, 0, 0))           
        bail(L, "lua_pcall() failed");

    lua_getglobal(L, "listen");
    luaL_checktype(L, -1, LUA_TTABLE);

    int i;
    for (i = 1; ; i++) {
        lua_rawgeti(L, -1, i);
        if (lua_isnil(L, -1)) {
            lua_pop(L, 1);
            break;
        }
        // an element of the 'listen' table should now be at the top of the stack
        luaL_checktype(L, -1, LUA_TTABLE);
        // read the content of that element
        int field_index;
        for (field_index = 0; (fields[field_index].name != NULL 
                    && fields[field_index].name != NULL); field_index++) {

            lua_getfield(L, -1, fields[field_index].name);
            luaL_checktype(L, -1, fields[field_index].type);
            // you should probably use a function pointer in the fields table.
            // I am using a simple switch/case here
            switch(field_index) {
                case 0:
                    printf("port: %d\n", (int)lua_tonumber(L, -1));
                    // do what you want with port
                    break;
                case 1:
                    printf("address: %s\n", lua_tostring(L, -1));
                    break;
                case 2:
                    // handle userdata
                    printf("userdata: %s\n", lua_tostring(L, -1));
                    break;
            }
            // remove the field value from the top of the stack
            lua_pop(L, 1); 
        }
        // remove the element of the 'listen' table from the top of the stack.
        lua_pop(L, 1);
    }

    lua_close(L);

    return 0;
}
1

There are 1 answers

0
Gilles Gregoire On BEST ANSWER

You are not too far. The key here is to understand how the Lua API use the stack for everything.

Here is an untested code sample which should get you going:

// this will be used to validate our table
static struct {
    const char * name;
    int type;
} fields[] = {
    {"port", LUA_TNUMBER},
    {"address", LUA_TSTRING},
    {"userdata", LUA_TSTRING},
    NULL
};

lua_getglobal(L, "listen");

// the 'listen' table should be at the top of the stack
luaL_checktype(L, -1, LUA_TTABLE);

// iterate the listen table
int i;
for (i = 1; ; i++) {
    lua_rawgeti(L, -1, i);
    // when you get nil, you're done
    if (lua_isnil(L, -1)) {
        lua_pop(L,1);
        break;
    }
    // an element of the 'listen' table should now be at the top of the stack
    luaL_checktype(L, -1, LUA_TTABLE);
    // read the content of that element
    int field_index;
    for (field_index = 0; fields[field_index] != NULL; field_index++) {
        lua_getfield(L, -1, fields[field_index].name);
        luaL_checktype(L, -1, fields[field_index].type);
        // you should probably use a function pointer in the fields table.
        // I am using a simple switch/case here
        switch(field_index) {
        case 0:
            port = lua_tonumber(L, -1);
            // do what you want with port
            break;
        case 1:
            address = lua_tostring(L, -1);
            break;
        case 2:
            // handle userdata
            break;
        }
        // remove the field value from the top of the stack
        lua_pop(L, 1); 
    }
    // remove the element of the 'listen' table from the top of the stack.
    lua_pop(L, 1);
}

I suggest you use those documentations: Lua API table Lua API ref