如何从Lua C API获取由Lua设置的元表

3

Lua:

a = {
    b = "c",
    d = {
        e = "f",
        g = "h"    
    }
}
setmetatable(a.d, {__ismt = true})

cfun(a) --call C function to iterate over table a

C语言:
int cfun(lua_State *L)
{
    lua_pushnil(L);
    while (lua_next(L, -2) != 0)
    {
        // iterate over table

        lua_pop(L, 1);
    }
}

在主机客户端遍历表时,如何知道是否存在元表?然后如何获取元表?


2
lua_getmetatable? - Joseph Sible-Reinstate Monica
1个回答

1
表格以树的形式呈现,需要以迭代模式遍历该树。Lua已经有了堆栈实现,这使得工作更加容易。
  • 进入时,栈顶是表,你需要推入一个nil元素,因为lua_next()在检查表之前会从栈中消耗一个元素。所以栈看起来像是table -> nil
  • 接下来,我们调用lua_next(),它将从栈中消耗一个元素,并将两个新的键值对添加到表中。栈看起来像是table -> key -> value。如果没有下一个元素,调用的返回值为0。
  • 如果返回值为1,并且栈上的值是嵌套表,则需要在栈上推入nil。现在栈看起来像是table -> key -> table -> nil。现在你几乎回到了开始的位置,所以通过循环,你将开始遍历嵌套表。
  • 如果返回值为1,并且值不是表,则处理该值。
  • 如果返回值为0,我们可以检查这是否是元表。检查后,你将弹出该值并检查栈是否为table -> keyany -> key。如果栈上第二个元素不是表,则已经完成遍历,可以退出循环。

以下是实现该算法的C代码。我保留了printf以帮助调试,但应将printf()删除。

static int cfun(lua_State *L)
{
    luaL_checktype(L, 1, LUA_TTABLE);
    lua_pushnil(L);        // Add extra space for the first lua_next to pop
    int loop=1;
    do {
        if ( lua_next(L,-2) != 0 ) {
            if (lua_istable(L,-1)) {
                printf("Table [%s] \n", lua_tostring(L, -2));
                lua_pushnil(L); // Start iterating this sub-table
            } else {
                // The Key and Value are on the stack. We can get their type
                printf("(%s - %s)\n", 
                    lua_tostring(L, -2),
                    lua_typename(L, lua_type(L, -1)));
                lua_pop(L,1);
            }
        } else {
            printf("table finished, still on stack (%s -> %s -> %s)\n",
                    lua_typename(L, lua_type(L, -3)),
                    lua_typename(L, lua_type(L, -2)),
                    lua_typename(L, lua_type(L, -1)));
            if (lua_getmetatable(L,-1)) {
                // The table has metatable. Now the metatable is on stack
                printf("Metatable detected\n");
                lua_pop(L,1); // remove the metatable from stack
            }
            lua_pop(L,1); // Pop the current table from stack
            if (!lua_istable(L, -2)) {
                loop = 0; // No more tables on stack, breaking the loop
            }
        }
    } while (loop);
    lua_pop(L,1); // Clear the last element
    lua_pushnumber(L,0); // Return 0
    return 1;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接