Итерация через таблицу Lua

Я пытаюсь выполнить итерацию по таблице lua, но я продолжаю получать эту ошибку:

invalid key to 'next'

Я знаю, что индекс начинается с -8, и я знаю, что там есть таблица, потому что он получает первое (и единственное) значение в нем. Тем не менее, он пытается зацикливаться снова, хотя я знаю, что в таблице есть только одна строка.

if (lua_istable(L, index))
{
    lua_pushnil(L);

    // This is needed for it to even get the first value
    index--;

    while (lua_next(L, index) != 0)
    {
        const char *item = luaL_checkstring(L, -1);
        lua_pop(L, 1);

        printf("%s\n", item);
    }
}
else
{
    luaL_typerror(L, index, "string table");
}

Любая помощь будет оценена.

Это отлично работает, когда я использую положительный индекс (если я не удаляю 1 из него)

Изменить: Я заметил, что я не получаю эту ошибку, если я оставляю значение элемента отдельно. Только когда я начну читать значение элемента, я получу эту ошибку. Когда у меня есть значение из таблицы, я вызываю другую функцию Lua, может ли это быть нарушено lua_next?

Ответы

Ответ 1

Не используйте luaL_checkstring с отрицательными аргументами. Вместо этого используйте lua_tostring.

Кроме того, убедитесь, что после вызова функции в цикле стек остается неизменным: lua_next ожидает предыдущего ключа таблицы в верхней части стека, чтобы он мог продолжить обход.

Ответ 2

Есть 2 вещи, которые вам нужно посмотреть:

  • убедитесь, что исходный ключ оставлен в стеке перед следующим вызовом lua_next. luaL_checkstring преобразует нестрочные ключи в строки (поскольку результирующая строка не находится в таблице, она становится недопустимым ключом.) Это проще всего сделать, передав luaL_checkstring копию ключа вместо оригинала.
  • убедитесь, что вы сохранили структуру стека (т.е. поместите столько значений, сколько вы нажимаете) на каждом проходе через цикл

Ваша функция будет работать только для отрицательных значений index. Вы правы, что index--; гарантирует, что index все еще указывает на таблицу после нажатия клавиши, но только если index был отрицательным (т.е. относительно вершины стека). Если index является абсолютным или pseudo index, то это приведет к тому, что он укажет на неправильный элемент. Самое легкое обходное решение - нажать другую ссылку на таблицу на вершину стека.

Здесь минимальная программа C для демонстрации:

#include <lauxlib.h>
#include <lua.h>

static void iterate_and_print(lua_State *L, int index);

int main(int ac, char **av)
{
   lua_State *L = luaL_newstate();
   luaL_openlibs(L);

   // Create a table and put it on the top of the stack
   luaL_loadstring(L, "return {one=1,[2]='two',three=3}");
   lua_call(L, 0, 1);

   iterate_and_print(L, -1);
   return 0;
}

static void iterate_and_print(lua_State *L, int index)
{
    // Push another reference to the table on top of the stack (so we know
    // where it is, and this function can work for negative, positive and
    // pseudo indices
    lua_pushvalue(L, index);
    // stack now contains: -1 => table
    lua_pushnil(L);
    // stack now contains: -1 => nil; -2 => table
    while (lua_next(L, -2))
    {
        // stack now contains: -1 => value; -2 => key; -3 => table
        // copy the key so that lua_tostring does not modify the original
        lua_pushvalue(L, -2);
        // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
        const char *key = lua_tostring(L, -1);
        const char *value = lua_tostring(L, -2);
        printf("%s => %s\n", key, value);
        // pop value + copy of key, leaving original key
        lua_pop(L, 2);
        // stack now contains: -1 => key; -2 => table
    }
    // stack now contains: -1 => table (when lua_next returns 0 it pops the key
    // but does not push anything.)
    // Pop table
    lua_pop(L, 1);
    // Stack is now the same as it was on entry to this function
}

Ответ 3

Из manual:

const char *lua_tolstring (lua_State *L, int index, size_t *len);

Преобразует значение Lua в данный допустимый индекс для строки C. Если len не является NULL, он также устанавливает * len с длина строки. Значение Lua должно быть строкой или числом; в противном случае, функция возвращает NULL. Если значение - это число, затем lua_tolstring также изменяет фактическое значение в стека к строке. (Это изменение смущает lua_next, когда lua_tolstring применяется к клавишам во время таблицы ВТП.)

luaL_checkstring вызывает lua_tolstring.

Ответ 4

См. также пример из документов для lua_next, здесь выдержки:

int lua_next (lua_State *L, int index);

Набирает ключ из стека и выталкивает пару ключ-значение из таблицы по данному индексу ( "следующая" пара после заданного ключа). Если в таблице больше нет элементов, то lua_next возвращает 0 (и ничего не толкает).

Типичный обход выглядит следующим образом:

/* table is in the stack at index 't' */
 lua_pushnil(L);  /* first key */
 while (lua_next(L, t) != 0) {
   /* uses 'key' (at index -2) and 'value' (at index -1) */
   printf("%s - %s\n",
          lua_typename(L, lua_type(L, -2)),
          lua_typename(L, lua_type(L, -1)));
   /* removes 'value'; keeps 'key' for next iteration */
   lua_pop(L, 1);
 }

При перемещении таблицы не вызывайте lua_tolstring непосредственно на ключ, если только вы не знаете, что ключ является фактически строкой. Напомним, что lua_tolstring может изменить значение по данному индексу; это смущает следующий вызов lua_next.

См. функцию next для предостережений об изменении таблицы во время ее обхода.