2011-05-26 125 views
13

我想通过一个Lua表进行迭代,但我不断收到此错误:迭代通过的Lua表

invalid key to 'next' 

我知道,指数-8开始了,我知道有一个表存在,因为它获取第一个(也是唯一的)值。但是,即使我知道表中只有一个字符串,它仍会尝试循环。

if (lua_istable(L, index)) 
{ 
    lua_pushnil(L); 

    // This is needed for it to even get the first value 
    index--; 

    while (lua_next(L, index) != 0) 
    { 
     const char *item = luaL_checkstring(L, -1); 
     lua_pop(L, 1); 

     printf("%s\n", item); 
    } 
} 
else 
{ 
    luaL_typerror(L, index, "string table"); 
} 

任何帮助,将不胜感激。

当我使用一个正指数(只要我不从中取出1)这工作得很好

编辑:我注意到,我没有,如果得到这个错误我只留下物品的价值。只有当我开始阅读项目的价值时,我会得到这个错误。当我从表中获得价值时,我会调用另一个Lua函数,这是否会扰乱lua_next?

回答

5

请勿使用带有否定参数的luaL_checkstring。改为使用lua_tostring

另外,确保在循环中调用一个函数后堆栈保持不变:lua_next需要堆栈顶部的上一个表键,以便它可以恢复遍历。

+0

是,经过前后倾倒栈的内容,我注意到当我调用这个函数时还有剩下的东西。 – 2011-05-26 13:17:36

2

manual

const char *lua_tolstring (lua_State *L, int index, size_t *len); 

Converts the Lua value at the given acceptable index to a C string. If len is not NULL, it also sets *len with the string length. The Lua value must be a string or a number; otherwise, the function returns NULL. If the value is a number, then lua_tolstring also changes the actual value in the stack to a string. (This change confuses lua_next when lua_tolstring is applied to keys during a table traversal.)

luaL_checkstring电话lua_tolstring

+0

对不起,正如引言所述,这仅适用于该键,并且您正在对该值运行此操作。 – BMitch 2011-05-26 13:37:36

30

有你需要注意两件事情:

  • 保证原有关键是lua_next下一次调用之前留在堆栈上。 luaL_checkstring会将非字符串键转换为字符串(因为生成的字符串不在表中,所以它变成无效键)。这通过传递luaL_checkstring副本而不是原始键来实现。
  • 确保您保留堆栈结构(即你推弹出尽可能多的值)每次通过循环

你的功能将只针对index负值工作。 index--;将确保index在按下按键后仍指向表格,但只有在index为负数(即相对于堆栈顶部)时才是正确的。如果index是绝对索引或伪索引,则它会导致它指向错误的项目。最简单的解决方法是将另一个对表的引用推入栈顶。

这里有一个最小的C程序来演示:

#include <lauxlib.h> 
#include <lua.h> 

static void iterate_and_print(lua_State *L, int index); 

int main(int ac, char **av) 
{ 
    lua_State *L = luaL_newstate(); 
    luaL_openlibs(L); 

    // Create a table and put it on the top of the stack 
    luaL_loadstring(L, "return {one=1,[2]='two',three=3}"); 
    lua_call(L, 0, 1); 

    iterate_and_print(L, -1); 
    return 0; 
} 

static void iterate_and_print(lua_State *L, int index) 
{ 
    // Push another reference to the table on top of the stack (so we know 
    // where it is, and this function can work for negative, positive and 
    // pseudo indices 
    lua_pushvalue(L, index); 
    // stack now contains: -1 => table 
    lua_pushnil(L); 
    // stack now contains: -1 => nil; -2 => table 
    while (lua_next(L, -2)) 
    { 
     // stack now contains: -1 => value; -2 => key; -3 => table 
     // copy the key so that lua_tostring does not modify the original 
     lua_pushvalue(L, -2); 
     // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table 
     const char *key = lua_tostring(L, -1); 
     const char *value = lua_tostring(L, -2); 
     printf("%s => %s\n", key, value); 
     // pop value + copy of key, leaving original key 
     lua_pop(L, 2); 
     // stack now contains: -1 => key; -2 => table 
    } 
    // stack now contains: -1 => table (when lua_next returns 0 it pops the key 
    // but does not push anything.) 
    // Pop table 
    lua_pop(L, 1); 
    // Stack is now the same as it was on entry to this function 
} 
+0

'luaL_checkstring'确实接受否定指数,但如果它失败了,它会中止并给出错误信息,因为它是为了检查函数参数,而不是用于一般转换。 – lhf 2011-05-26 18:53:10

+0

@lhf,好吧,除了在问题的代码示例中使用'luaL_checkstring'似乎没有任何优势。用'lua_tostring'代替它。 – finnw 2011-05-26 19:41:00

-1

又见例如,从文档为lua_next,这里摘录:

int lua_next (lua_State *L, int index);

Pops a key from the stack, and pushes a key–value pair from the table at the given index (the "next" pair after the given key). If there are no more elements in the table, then lua_next returns 0 (and pushes nothing).

A typical traversal looks like this:

/* table is in the stack at index 't' */ 
lua_pushnil(L); /* first key */ 
while (lua_next(L, t) != 0) { 
    /* uses 'key' (at index -2) and 'value' (at index -1) */ 
    printf("%s - %s\n", 
      lua_typename(L, lua_type(L, -2)), 
      lua_typename(L, lua_type(L, -1))); 
    /* removes 'value'; keeps 'key' for next iteration */ 
    lua_pop(L, 1); 
} 

While traversing a table, do not call lua_tolstring directly on a key, unless you know that the key is actually a string. Recall that lua_tolstring may change the value at the given index; this confuses the next call to lua_next .

See function next for the caveats of modifying the table during its traversal.