Итерирование через таблицу Lua из С++?
Я пытаюсь загрузить таблицы из Lua в С++, но у меня возникли проблемы с этим.
Я получаю первую итерацию в порядке, но при втором вызове
lua_next сбой. Любые идеи?
Файл Lua:
level = { 1, 2, 3, }
Файл С++. Сначала я сделал это:
lua_getglobal( L, "level" );
for( lua_pushnil( L ); lua_next( L, 1 ); lua_pop( L, -2 ) )
{
if( lua_isnumber( L, -1 ) ) {
int i = (int)lua_tonumber( L, -1 );
//use number
}
}
lua_pop( L, 1 );
Затем я попытался из справочного руководства :
lua_getglobal( L, "level" );
int t = 1;
lua_pushnil( L );
while( lua_next( L, t ) ) {
printf( "%s - %s",
lua_typename( L, lua_type( L, -2 ) ),
lua_typename( L, lua_type( L, -1 ) ) );
lua_pop( L, 1 );
}
lua_pop( L, 1 );
И, наконец, это:
lua_getglobal( L, "level" );
lua_pushnil( L );
lua_next( L, 1 );
if( lua_isnumber( L, -1 ) ) {
int i = (int)lua_tonumber( L, -1 );
//use number fine
}
lua_pop( L, 1 );
lua_next( L, 1 ); //crashes
etc...
Естественно L является lua_State *, и я инициализирую его и разбираю файл в порядке.
Edit:
В ответ на ответ Джесси Бедер я пробовал этот код с регистратором, но я все еще не могу заставить его работать.
Log::Get().Write( "engine", "stack size: %i", lua_gettop( L ) );
lua_getglobal(L, "level");
if( lua_istable( L, -1 ) )
Log::Get().Write( "engine", "-1 is a table" );
lua_pushnil(L);
if( lua_isnil( L, -1 ) )
Log::Get().Write( "engine", "-1 is now nil" );
if( lua_istable( L, -2 ) )
Log::Get().Write( "engine", "-2 is now table" );
int pred = lua_next( L, -2 );
Log::Get().Write( "engine", "pred: %i", pred );
while( pred ) {
Log::Get().Write( "engine", "loop stuff" );
if( lua_isnumber( L, -1 ) ) {
int i = (int)lua_tonumber( L, -1 );
//use number
Log::Get().Write( "engine", "num: %i", i );
}
Log::Get().Write( "engine", "stack size: %i", lua_gettop( L ) );
if( lua_istable( L, -3 ) )
Log::Get().Write( "engine", "-3 is now table" );
lua_pop( L, 1 );
Log::Get().Write( "engine", "stack size: %i", lua_gettop( L ) );
if( lua_istable( L, -2 ) )
Log::Get().Write( "engine", "-2 is now table" );
pred = lua_next( L, -2 );
Log::Get().Write( "engine", "pred: %i", pred );
}
lua_pop( L, 1 );
Который дал этот результат:
stack size: 0
-1 is a table
-1 is now nil
-2 is now table
pred: 1
loop stuff
num: 1
stack size: 3
-3 is now table
stack size: 2
-2 is now table
Все, что вы сказали, Джесси, похоже, верно. Но он все равно не переходит к следующей итерации.
Edit2:
Я попытался скопировать точный код в новый проект, пропустив все окружающие классы и прочее, что я не стал включать сюда и там, он работает. Но здесь это не так, и он просто выдержит один вызов lua_next.
Edit3:
Я сузил его немного дальше. Я использую hge как свой 2D-движок.
Я положил весь предыдущий код в тест функции:
test(); //works
if( hge->System_Initiate() )
{
test(); //fails
hge->System_Start();
}
Насколько я понимаю, hge ничего не делает с lua.
Здесь исходный код для небольшого теста, который я сделал. Источником для hge 1.81 является здесь.
Edit4:
Размер вопроса выходит из-под контроля, но ему не помогут. Это самый маленький код, с которым я смог его уменьшить.
extern "C"
{
#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>
}
#include <hge\hge.h>
bool frame_func()
{
return true;
}
bool render_func()
{
return false;
}
void test()
{
lua_State *L = lua_open();
luaL_openlibs( L );
if( luaL_dofile( L, "levels.lua" ) ) {
lua_pop( L, -1 );
return;
}
lua_getglobal(L, "level");
lua_pushnil(L);
while( lua_next( L, -2 ) ) {
if( lua_isnumber( L, -1 ) ) {
int i = (int)lua_tonumber( L, -1 );
//use number
}
lua_pop( L, 1 );
}
lua_pop( L, 1 );
lua_close( L );
}
int main()
{
HGE *hge = hgeCreate( HGE_VERSION );
hge->System_SetState( HGE_FRAMEFUNC, frame_func );
hge->System_SetState( HGE_RENDERFUNC, render_func );
hge->System_SetState( HGE_WINDOWED, true );
hge->System_SetState( HGE_SCREENWIDTH, 800 );
hge->System_SetState( HGE_SCREENHEIGHT, 600 );
hge->System_SetState( HGE_SCREENBPP, 32 );
//test(); //works
if( hge->System_Initiate() )
{
test(); //fails
hge->System_Start();
}
hge->Release();
return 0;
}
Ответы
Ответ 1
Когда вы вызываете lua_next
, второй аргумент должен быть индексом таблицы. Поскольку вы просто нажимаете таблицу на стек с помощью
lua_getglobal(L, "level");
после этого вызов вашего стека будет выглядеть как
-1: table "level"
(не +1
, так как стек считывается вниз). Затем вы вызываете
lua_pushnil(L);
поэтому ваш стек будет
-1: key (nil)
-2: table "level"
Ваша таблица находится в -2
, поэтому, когда вы вызываете lua_next
, вы должны использовать индекс -2
. Наконец, после каждой итерации ваш стек должен выглядеть так:
-1: value
-2: key
-3: table "level"
Итак, вы хотите прочитать значение (в -1
), а затем поместите его (так что просто нажмите один раз), а затем вызовите lua_next
, чтобы получить следующий ключ. Итак, что-то вроде этого должно работать:
lua_getglobal(L, "level");
lua_pushnil(L);
while(lua_next(L, -2)) { // <== here is your mistake
if(lua_isnumber(L, -1)) {
int i = (int)lua_tonumber(L, -1);
//use number
}
lua_pop(L, 1);
}
lua_pop(L, 1);
Изменить на основе вашего второго редактирования
Так как он работает, когда вы удаляете внешний материал, но не добавляете его обратно, я думаю, что вы как-то повредите стек (либо С++-стек, либо стек lua). Посмотрите внимательно на свои указатели, особенно когда вы манипулируете состоянием lua.
Ответ 2
Чтение руководства LUA lua_next вы обнаружите, что проблемы могут возникать при непосредственном использовании lua_tostring по ключу, то есть вам нужно проверить значение ключа, а затем решите использовать lua_tostring или lua_tonumber. Поэтому вы можете попробовать этот код:
std::string key
while(lua_next(L, -2) != 0){ // in your case index may not be -2, check
// uses 'key' (at index -2) and 'value' (at index -1)
if (lua_type(L, -2)==LUA_TSTRING){ // check if key is a string
// you may use key.assign(lua_tostring(L,-2));
}
else if (lua_type(L, -2)==LUA_TNUMBER){ //or if it is a number
// this is likely to be your case since you table level = { 1, 2, 3, }
// don't declare field ID's
// try:
// sprintf(buf,"%g",lua_tonumber(L,-2));
// key.assign(buf);
}
else{
// do some other stuff
}
key.clear();
lua_pop(L,1)
}
Надеюсь, что это поможет.
Ответ 3
Почему вы делаете дополнительный lua_pop(L, 1)
в конце версии из справочного руководства? Я вижу, как это может быть проблемой, поскольку вы выходите за пределы глубины стека.