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