1. 程式人生 > >C/C++ 中遍歷 Lua table 完整版

C/C++ 中遍歷 Lua table 完整版

    在 C/C++ 中遍歷一個 Lua table用 lua_next 來實現,這個大家都知道。然而,我卻看到很多文章在示範 lua_next 時都只是點到為止,或絕口不提如何獲取 key 值,或直接定義該 table 的 key 都是非匿名的,從而簡單粗暴地使用 lua_tostring 來獲取值。

仔細看看,Lua manual 裡對 lua_next 的說明中最後有一句很重要的話:

While traversing a table, do not call lua_tolstring directly on a key, unless you know that the key is actually a string. Recall that lua_tolstring changes the value at the given index; this confuses the next call to lua_next.

    遍歷 table 的過程中不要直接對處於 -2 位置的 key 做 lua_tostring 操作(還記得這個函式說了它會原地修改棧上的值的吧),除非你確信現在這個 key 就是字串型別,否則下一次執行 lua_next 就等著意外吧。
好吧,來個完整版,其實不就是把 key 拷貝一份到棧上,然後對這份拷貝進行取值麼[1]:

#include <lauxlib.h>
#include <lua.h>
 
void traverse_table(lua_State *L, int index)
{
    lua_pushnil(L); 
    // 現在的棧:-1 => nil; index => table
    while (lua_next(L, index))
    {
        // 現在的棧:-1 => value; -2 => key; index => table
        // 拷貝一份 key 到棧頂,然後對它做 lua_tostring 就不會改變原始的 key 值了
        lua_pushvalue(L, -2);
        // 現在的棧:-1 => key; -2 => value; -3 => key; index => table
        const char* key = lua_tostring(L, -1);
        const char* value = lua_tostring(L, -2);
 
        printf("%s => %s\n", key, value);
 
        // 彈出 value 和拷貝的 key,留下原始的 key 作為下一次 lua_next 的引數
        lua_pop(L, 2);
        // 現在的棧:-1 => key; index => table
    }
    // 現在的棧:index => table (最後 lua_next 返回 0 的時候它已經把上一次留下的 key 給彈出了)
    // 所以棧已經恢復到進入這個函式時的狀態
}
 
int main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
 
    // 假設現在棧上已經有了一個 talbe,內容為 {one=1,[2]='two',three=3},位置在 top
 
    traverse_table(L, -1);
 
    return 0;
}