Итерация по таблице Lua из C ++? - PullRequest
       18

Итерация по таблице Lua из C ++?

19 голосов
/ 17 сентября 2009

Я пытаюсь загрузить таблицы из 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 *, и я инициализирую его и хорошо разбираю файл.

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

Ответы [ 3 ]

28 голосов
/ 17 сентября 2009

Когда вы вызываете 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.

1 голос
/ 28 февраля 2014

Чтение руководства по 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)
   }

Надеюсь, это поможет.

0 голосов
/ 17 сентября 2009

Почему вы делаете дополнительные lua_pop(L, 1) в конце версии из справочного руководства? Я вижу, как это может быть проблемой, поскольку вы выталкиваете за пределы стека.

...