在宿主语言中操作lua的表跟在lua语言中的操作实际上很相似。
在lua中我们多数时候是调用 pairs/ipairs 来迭代,不会直接使用 next 函数。但在宿主语言中只有 next,所以有必要讲下 lua 中 next 的用法:
- 置 local k, v = nil,即先用 nil 调用 next 开始迭代;
- 调用 k,v = next(table, k);
- 如果 k ~= nil,则代表 k 和 v 是一对有效值;否则转到结束;
- 使用 k, v;
- 回到第2步,用此时的 k 再次调用 k, v = next(table, k) 获取下一组值;
- 结束。
写成代码有点像下面这样:
local a = { key1 = "value1", key2 = "value2", } local k, v while true do k, v = next(a, k) if not k then break end print(k .. ': ' .. v) end
在宿主语言(这里的C/C++)中操作也非常相似:
#include <cstdio>
#include <dll/lua.hpp>
static const char* get_code() {
return "return {"
" key1 = 'value1',"
" key2 = 'value2',"
"}";
}
static void iterate(lua_State* L, int index) {
// 为了使被操作的表在栈顶,我们需要作一些操作来确保
bool ontop = index == -1;
// 如果不在栈顶上,就压入此表的一份引用到栈顶
if(!ontop)
lua_pushvalue(L, index);
// 现在的栈是这样的:-1 => table
// 好了,现在表已经在栈顶上了,像前面操作 next 的方式一样操作
// 1. 先压入 nil 调用 next 开始迭代
lua_pushnil(L);
// 现在的栈是这样的:-1 => nil, -2 => table
// 2. 现在循环调用 next 来获取下一组键值
// 第1个参数待查询的键,第2个参数是表的索引
// 如果没有可迭代的元素了,lua_next 会返回 0
while(lua_next(L, -2)) {
// 现在的栈是这样的:-1 => value, -2 => key, -3 => table
// 3. 值已经拿到了,可以使用了。但还有一点需要注意:
// 如果 key 不是字符串的话,不能使用 lua_tostring,原因请看
// 官方文档:http://www.lua.org/manual/5.3/manual.html#lua_next
// 如果真的想用 lua_tostring 的话,可以先压入一份 key 的拷贝。
// 我这里为了简单起见,使用了字符串,就不需要再考虑了。
const char* key = lua_tostring(L, -2);
const char* val = lua_tostring(L, -1);
printf("%s => %s\n", key, val);
// 现在完后,干掉 value 保留 key,为下一次迭代作准备
lua_pop(L, 1);
// 现在的栈是这样的:-1 => key, -2 => table
// 已经回到开始遍历时的栈帧了,可以下一次遍历了
}
// lua_next 不止会返回0,而且还会帮我们把最后一个 key 给弹出去,只保留最初那个表
// 现在的栈是这样的:-1 => table
// 好了,让一切回到最初
if(!ontop)
lua_pop(L, 1);
}
int main() {
int rc;
lua_State* L;
L = luaL_newstate();
luaL_openlibs(L);
// 编译代码,示例代码只是简地返回一个表
// 若编译成功,以匿名函数的形式返回此代码的执行码
rc = luaL_loadstring(L, get_code()) == LUA_OK;
// 所以这里调用 pcall 来调用此匿名函数
// 若成功,栈顶上是上面我们返回的具有两个键值的表
rc = rc && lua_pcall(L, 0, 1, NULL) == LUA_OK;
if(rc) {
// 这里我把迭代函数给独立出去了,有一些原因:
// 在调用 next 函数迭代表的键值时,需要该表在栈顶(索引为-1)才行。
// 但这里的栈结构很简单,只有一个元素,我们即将操作的表,但很多时候,
// 表不一定是在栈顶。所以得写一个更通用的算法。
iterate(L, -1);
}
lua_close(L);
return 0;
}