Итерация по Lua Table - PullRequest
       13

Итерация по Lua Table

15 голосов
/ 26 мая 2011

Я пытаюсь перебрать таблицу 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?

Ответы [ 4 ]

31 голосов
/ 26 мая 2011

Вам нужно посмотреть 2 вещи:

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

Ваша функция будет работать только для отрицательных значений index. Вы правы, что index--; будет гарантировать, что index по-прежнему будет указывать на таблицу после нажатия клавиши, но только если index был отрицательным (то есть относительно вершины стека). Если 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
}
5 голосов
/ 26 мая 2011

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

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

2 голосов
/ 26 мая 2011

Из руководства :

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

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

luaL_checkstring звонки lua_tolstring.

0 голосов
/ 03 мая 2016

См. Также пример из документации для 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 для получения информации о модификации таблицы во время ее обхода.

...