Как получить набор метатаблиц с помощью lua из Lua C API

Луа:

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);
    }
}

Как узнать, есть ли метатаблица, когда хост-клиент выполняет итерацию по таблице? А как тогда получить метатаблицу?


person John Doe    schedule 16.03.2021    source источник
comment
lua_getmetatable?   -  person Joseph Sible-Reinstate Monica    schedule 16.03.2021


Ответы (1)


Таблица представляет собой дерево, и вам нужно пройти по дереву в итеративном режиме. В Lua уже есть реализация стека, так что это упрощает работу.

  • При входе стек имеет таблицу наверху, вы поместите элемент nil, так как lua_next() будет потреблять один элемент из стека перед проверкой таблицы. Таким образом, стек будет выглядеть как table -> nil.
  • Затем мы вызываем lua_next(), который использует один элемент из стека и добавит две новые пары ключ-значение из таблицы. Стек будет выглядеть как table -> key -> value. Если следующего элемента нет, возвращаемое значение вызова равно 0.
  • Если возвращаемое значение равно 1, а значение в стеке является вложенной таблицей, вы поместите nil в стек, поэтому теперь стек будет выглядеть как table -> key -> table -> nil. Теперь вы почти как в начале, поэтому с циклом вы начнете обход вложенной таблицы.
  • если возвращаемое значение равно 1, и если значение не является таблицей, к вашим материалам со значением
  • если возвращаемое значение равно 0, мы можем проверить, была ли это метатаблица. После проверки вы извлечете значение и проверите, является ли стек table -> key или any -> 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;
}
person jordanvrtanoski    schedule 17.03.2021