[英]Iterating through a Lua table from C++?
我正在嘗試將表從Lua加載到 C++,但無法正確完成。 我通過第一次迭代就好了,但是在第二次調用 lua_next 時它崩潰了。 有任何想法嗎?
Lua文件:
level = { 1, 2, 3, }
C++ 文件 - 首先我這樣做:
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* 並且我正在初始化它並解析文件。
編輯:為了回應 Jesse Beder 的回答,我用記錄器嘗試了這段代碼,但我仍然無法讓它工作。
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;
}
當您調用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);
根據您的第二次編輯進行編輯
由於它在您刪除外部內容時有效,但在您重新添加時無效,我最好的猜測是您以某種方式破壞了堆棧(C++ 堆棧或 lua 堆棧)。 真的仔細看看你的指針,尤其是當你操縱LUA狀態。
閱讀LUA手冊lua_next,你發現直接在key上使用lua_tostring可能會出現問題,那就是你必須檢查key的值,然后決定使用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)
}
希望能幫助到你。
你為什么要在參考手冊的版本末尾做額外的lua_pop(L, 1)
? 我可以看到這可能是一個問題,因為您超出了堆棧深度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.