从 C++ 遍历 Lua 表?

Posted

技术标签:

【中文标题】从 C++ 遍历 Lua 表?【英文标题】:Iterating through a Lua table from C++? 【发布时间】:2010-11-29 03:48:22 【问题描述】:

我正在尝试将表从 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 );

然后我尝试从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 );

最后是这个:

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

杰西,你所说的一切似乎都是正确的。但仍然无法进入下一次迭代。

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

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

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

据我了解,hge 对 lua 没有任何作用。 Here 是我做的一个小测试的源代码。 hge 1.81 的来源是here。

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

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_next的返回值。我没有任何调试信息,我也不知道如何添加它... 第二次编辑是一个提示 - 查看我的回答中的编辑 你在什么时候初始化 lua 状态?如果你把它推到System_Initiate 之后,会发生什么?你什么时候打电话给hgeCreate?我想知道他们是否都使用了一些冲突的内存池。 确实您似乎可以同时使用 lua 和 hge(谷歌“lua hge”),但初始化时可能需要做一些技巧? 我尝试过在前后初始化 lua 状态,但没有结果。 hgeCreate 以前被称为。我不明白为什么,我之前使用过 lua,只是没有使用 lua_next。也许是我做过的其他事情搞砸了一切?但我就是看不到。 【参考方案1】:

当你调用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))   // <== 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 状态时。

【讨论】:

我进一步缩小了范围,还有另一个编辑给你。【参考方案2】:

阅读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)
   

希望对你有帮助。

【讨论】:

【参考方案3】:

您为什么要在参考手册的版本末尾添加额外的lua_pop(L, 1)?我知道这可能是个问题,因为您弹出的堆栈深度超出了堆栈深度。

【讨论】:

我正在从堆栈中删除表格,在手册中只有表格的索引,但您也必须在某个地方推送和弹出它。

以上是关于从 C++ 遍历 Lua 表?的主要内容,如果未能解决你的问题,请参考以下文章

c++ 遍历lua table方法 如图

lua:遍历表中的所有对

在lua表中实现索引从0开始

基于另一个计数值,遍历嵌套表 - LUA

lua 中pairs 和 ipairs区别

写一lua函数,能够遍历打印一个table数据,求代码!