从 C++ 遍历 Lua 表?

2021-12-30 00:00:00 lua c++ lua-table

我正在尝试将表从 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 ) ){如果(lua_isnumber(L,-1)){int i = (int)lua_tonumber( L, -1 );//使用数字}}lua_pop( L, 1 );

然后我从参考手册中尝试:

lua_getglobal( L, "level" );整数 t = 1;lua_pushnil(L);而(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 );如果(lua_isnumber(L,-1)){int i = (int)lua_tonumber( L, -1 );//使用数字罚款}lua_pop( L, 1 );lua_next( L, 1 );//崩溃等等...

L 自然是一个 lua_State*,我正在初始化它并解析文件.

为了回应 Jesse Beder 的回答,我用记录器尝试了这段代码,但我仍然无法让它工作.

Log::Get().Write( "engine", "stack size: %i", lua_gettop( L ) );lua_getglobal(L, "级别");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 现在为零" );if( lua_istable( L, -2 ) )Log::Get().Write( "engine", "-2 现在是表" );int pred = lua_next( L, -2 );Log::Get().Write( "engine", "pred: %i", pred);而(预测){Log::Get().Write( "engine", "loop stuff" );如果(lua_isnumber(L,-1)){int i = (int)lua_tonumber( L, -1 );//使用数字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 现在是表" );lua_pop( L, 1 );Log::Get().Write( "engine", "stack size: %i", lua_gettop( L ) );if( lua_istable( L, -2 ) )Log::Get().Write( "engine", "-2 现在是表" );pred = lua_next( L, -2 );Log::Get().Write( "engine", "pred: %i", pred);}lua_pop( L, 1 );

给出了这个输出:

堆栈大小:0-1 是一张桌子-1 现在为零-2 现在是表预测:1循环的东西数量:1堆栈大小:3-3 现在是表堆栈大小:2-2 现在是表

你说的一切,杰西,似乎都是正确的.但是还是没能进入下一次迭代.

编辑 2:我试图将确切的代码复制到一个新项目中,跳过所有周围的类和我不想在这里和那里包含的东西.但在这里它没有,它只会在调用 lua_next 时存活下来.

编辑 3:我现在把它缩小了一点.我使用 hge 作为我的 2D 引擎.我把之前的所有代码都放在了函数测试中:

test();//作品if( hge->System_Initiate() ){测试();//失败hge->System_Start();}

据我所知,hge 对 lua 没有任何作用.这里是我做的一个小测试的源代码.hge 1.81 的来源是这里.

编辑 4:问题的大小正在失控,但无济于事.这是我能够将其缩减到的最小代码.

extern "C"{#include #include #include }#include bool frame_func(){返回真;}bool render_func(){返回假;}无效测试(){lua_State *L = lua_open();luaL_openlibs(L);如果(luaL_dofile(L,levels.lua")){lua_pop( L, -1 );返回;}lua_getglobal(L, "级别");lua_pushnil(L);而(lua_next(L,-2)){如果(lua_isnumber(L,-1)){int i = (int)lua_tonumber( L, -1 );//使用数字}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);//测试();//作品if( hge->System_Initiate() ){测试();//失败hge->System_Start();}hge->发布();返回0;}

解决方案

当你调用 lua_next 时,第二个参数应该是表的索引.由于您只是使用

将表格推入堆栈

lua_getglobal(L, "level");

在那次调用之后你的堆栈看起来像

<前>-1:表级别"

(不是 +1,因为堆栈是向下读取的).然后你打电话

lua_pushnil(L);

所以你的堆栈将是

<前>-1:键(无)-2:表级别"

你的表在-2,所以当你调用lua_next时,你应该使用索引-2.最后,每次迭代后,您的堆栈应如下所示:

<前>-1:值-2:键-3:表级别"

所以你想读取值(在 -1 处)然后弹出它(所以只弹出一次),然后调用 lua_next 来获取下一个键.所以这样的事情应该有效:

lua_getglobal(L, "level");lua_pushnil(L);while(lua_next(L, -2)) {//<== 这是你的错误如果(lua_isnumber(L,-1)){int i = (int)lua_tonumber(L, -1);//使用数字}lua_pop(L, 1);}lua_pop(L, 1);

根据您的第二次编辑进行编辑

因为它在您删除外部内容时有效,但在您重新添加时无效,我最好的猜测是您以某种方式破坏了堆栈(C++ 堆栈或 lua 堆栈).真的仔细查看您的指针,尤其是当您操作 lua 状态时.

I'm trying to load tables from Lua to C++ but I'm having trouble getting it right. I'm getting through the first iteration just fine but then at the second call to lua_next it crashes. Any ideas?

Lua file:

level = { 1, 2, 3, }

C++ file - First I did this:

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 );

Then I tried from the reference manual:

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 );

And finally this:

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...

Naturally L is a lua_State* and I'm initializing it and parsing the file okay.

Edit: In response to Jesse Beder answer I tried this code, with a logger, but I still can't get it to work.

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 );

Which gave this output:

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

Everything you said, Jesse, seems to hold true. But it still fails to go to the next iteration.

Edit2: I tried to copy the exact code into a new project, skipping all the surrounding classes and stuff I didn't bother to include here and there it works. But here it doesn't, and it will just survive one call the lua_next.

Edit3: I've narrowed it down a bit further now. I'm using hge as my 2D engine. I put all the previous code in the function test:

test(); //works
if( hge->System_Initiate() )
{       
    test(); //fails
    hge->System_Start();
}

As far as I understand hge doesn't do anything with lua. Here's the source code for a small test I made. The source for hge 1.81 is here.

Edit4: The question size is getting out of control but it can't be helped. This is the smallest code I've been able to reduce it to.

extern "C"
{
    #include <lua/lua.h>
    #include <lua/lualib.h>
    #include <lua/lauxlib.h>
}
#include <hgehge.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;
}

解决方案

When you call lua_next, the second argument should be the index of the table. Since you're just pushing the table onto the stack with

lua_getglobal(L, "level");

after that call your stack will look like

-1: table "level"

(not +1, since the stack is read going down). Then you call

lua_pushnil(L);

so your stack will be

-1: key (nil)
-2: table "level"

Your table is at -2, so when you call lua_next, you should use the index -2. Finally, after each iteration, your stack should look like:

-1: value
-2: key
-3: table "level"

So you want to read the value (at -1) and then pop it (so just pop once), and then call lua_next to get the next key. So something like this should work:

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);

Edit based on your second edit

Since it works when you remove external stuff, but doesn't when you add it back in, my best guess is that you're corrupting the stack somehow (either the C++ stack or the lua stack). Look really carefully at your pointers, especially when you manipulate the lua state.

相关文章