简体   繁体   English

从 C++ 遍历 Lua 表?

[英]Iterating through a Lua table from C++?

I'm trying to load tables from Lua to C++ but I'm having trouble getting it right.我正在尝试将表从Lua加载到 C++,但无法正确完成。 I'm getting through the first iteration just fine but then at the second call to lua_next it crashes.我通过第一次迭代就好了,但是在第二次调用 lua_next 时它崩溃了。 Any ideas?有任何想法吗?

Lua file: Lua文件:

level = { 1, 2, 3, }

C++ file - First I did this: 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 );

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.自然 L 是一个 lua_State* 并且我正在初始化它并解析文件。

Edit: In response to Jesse Beder answer I tried this code, with a logger, but I still can't get it to work.编辑:为了回应 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 );

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. Edit2:我试图将确切的代码复制到一个新项目中,跳过所有周围的类和我不想在这里和那里包含的东西。 But here it doesn't, and it will just survive one call the lua_next.但在这里它没有,它只会在一次调用 lua_next 后幸存下来。

Edit3: I've narrowed it down a bit further now. Edit3:我现在把它缩小了一点。 I'm using hge as my 2D engine.我使用hge作为我的 2D 引擎。 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.据我了解,hge 对 lua 没有任何作用。 Here 's the source code for a small test I made. 是我做的一个小测试的源代码。 The source for hge 1.81 is here . hge 1.81 的来源在这里

Edit4: The question size is getting out of control but it can't be helped. Edit4:问题大小失控,但无济于事 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 <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;
}

When you call lua_next , the second argument should be the index of the table.当您调用lua_next ,第二个参数应该是表的索引。 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). (不是+1 ,因为堆栈是向下读取的)。 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 .你的表是-2 ,所以当你调用lua_next ,你应该使用索引-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.所以你想读取值(在-1 )然后弹出它(所以只弹出一次),然后调用lua_next来获取下一个键。 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).由于它在您删除外部内容时有效,但在您重新添加时无效,我最好的猜测是您以某种方式破坏了堆栈(C++ 堆栈或 lua 堆栈)。 Look really carefully at your pointers, especially when you manipulate the lua state.真的仔细看看你的指针,尤其是当你操纵LUA状态。

Reading LUA manual lua_next you find that problems may arise on using lua_tostring on a key directly, that is you have to check the value of the key and then decide to use lua_tostring or lua_tonumber.阅读LUA手册lua_next,你发现直接在key上使用lua_tostring可能会出现问题,那就是你必须检查key的值,然后决定使用lua_tostring或lua_tonumber。 So you could try this code:所以你可以试试这个代码:

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

Hope it helps.希望能帮助到你。

Why are you doing the extra lua_pop(L, 1) at the end of the version from the reference manual?你为什么要在参考手册的版本末尾做额外的lua_pop(L, 1) I can see how that could be a problem, since you're popping beyond the stack depth.我可以看到这可能是一个问题,因为您超出了堆栈深度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM