最近研究了一下通过lua_next()函数获取Lua表内容的实现过程,记录一下:
先贴一段从网上看到的lua_next()工作过程,大致如下:
lua_next() 函数的工作过程是:
1) 先从栈顶弹出一个 key;
2) 从栈指定位置的 table 里获取下一对 key-value,然后将 key 入栈,再将 value 入栈;
3) 如果第 2 步成功则返回非 0 值,否则返回 0,并且不向栈中压入任何值。
第一遍看这个过程有点云里雾里,于是写了一个例程来验证。该例程有两个功能,分别读取一个一维Lua表和一个二维Lua表。
读取一维表的函数定义如下:
/* 遍历一维表 */
void table_traverse(lua_State *L, std::string table_name)
{
lua_getglobal(L, table_name.c_str()); //根据表名称获取表
int idx = lua_gettop(L); // 获取表在栈上的索引
lua_pushnil(L); /* table 里第一对 key-value 的前面没有数据,所以先用 lua_pushnil() 压入一个 nil 充当初始 key */
while(lua_next(L, idx))
{
//std::cout << "---Loop start: Stack size = " << lua_gettop(L) << std::endl;
/* 输出key */
if(lua_isnumber(L, -2))
{
std::cout << "key = " << lua_tonumber(L, -2) << ", ";
}
else if(lua_isstring(L, -2))
{
std::cout << "key = " << lua_tostring(L, -2) << ", ";
}
else
{
std::cout << "Error key type!" << std::endl;
}
/* 输出value */
if(lua_isnumber(L, -1))
{
std::cout << "value = " << lua_tonumber(L, -1) << std::endl;
}
else if(lua_isstring(L, -1))
{
std::cout << "value = " << lua_tostring(L, -1) << std::endl;
}
else
{
std::cout << "Error key type!" << std::endl;
}
/* 弹出一个元素,即当前的value */
lua_pop(L, 1);
//std::cout << "---Loop end: Stack size = " << lua_gettop(L) << std::endl;
}
lua_pop(L, 1);
}
读取二维表的函数如下:
/* 遍历二维表 */
void table_traverse_2D(lua_State *L, std::string table_name)
{
lua_getglobal(L, table_name.c_str()); //根据表名称获取表
int idx2D = lua_gettop(L); // 获取总表在栈上的索引
lua_pushnil(L);
while(lua_next(L, idx2D))
{
int idx = lua_gettop(L); // 获取子表在栈上的索引
//std::cout << "------Outside loop start: Stack size = " << lua_gettop(L) << std::endl;
//std::cout << "Is number: " << lua_isnumber(L, -2) << std::endl;
//std::cout << "Is table " << lua_istable(L, -1) << std::endl;
int key_type = lua_type(L, -2);
int value_type = lua_type(L, -1);
std::cout << "Key type: " << lua_typename(L, key_type) << std::endl;
std::cout << "Value type: " << lua_typename(L, value_type) << std::endl;
lua_pushnil(L);
while(lua_next(L, idx))
{
//std::cout << "---Inside loop start: Stack size = " << lua_gettop(L) << std::endl;
/* 输出key */
if(lua_isnumber(L, -2))
{
std::cout << "key = " << lua_tonumber(L, -2) << ", ";
}
else if(lua_isstring(L, -2))
{
std::cout << "key = " << lua_tostring(L, -2) << ", ";
}
else
{
std::cout << "Error key type!" << std::endl;
}
/* 输出value */
if(lua_isnumber(L, -1))
{
std::cout << "value = " << lua_tonumber(L, -1) << std::endl;
}
else if(lua_isstring(L, -1))
{
std::cout << "value = " << lua_tostring(L, -1) << std::endl;
}
else if(lua_istable(L, -1))
{
std::cout << "value is table." << std::endl;
}
else
{
std::cout << "Error key type!" << std::endl;
}
/* 弹出一个元素,即当前的value */
lua_pop(L, 1);
//std::cout << "---Inside loop end: Stack size = " << lua_gettop(L) << std::endl;
}
/* 弹出一个元素,即一个子表的内容 */
lua_pop(L, 1);
//std::cout << "------Outside loop end: Stack size = " << lua_gettop(L) << std::endl;
}
lua_pop(L, 1);
}
程序主体:
#include <iostream>
#include <string>
#include <vector>
#include "lua.hpp"
/* 初始化函数 */
lua_State* script_initialize(const char* script_name)
{
lua_State *pLua = luaL_newstate(); //创建一个Lua虚拟机
if(!pLua)
{
std::cout << "Failed to open Lua!" << std::endl;
return NULL;
}
luaL_openlibs(pLua); //将标准类库加载到Lua虚拟机
luaL_dofile(pLua, script_name); //加载并运行lua文件
return pLua;
}
int main()
{
lua_State *pL = script_initialize("test_table.lua");
if(!pL)
{
std::cout << "Script initialized failed!" << std::endl;
return -1;
}
std::cout << "Script initialized successed!" << std::endl;
table_traverse(pL, "my_table");
std::cout << std::endl << std::endl;
table_traverse_2D(pL, "my_table_2D");
return 0;
}
Lua文件内容如下,含有一个一维表my_table和一个二维表my_table_2D。其中一维表中含有三对key-value,二维表也是有3个1维表构成的。
my_table = {color = "red", shape = "circle", size = 5}
my_table_2D = {{color = "red", shape = "circle", size = 5},
{color = "green", shape = "rectangle", size = 10},
{color = "blue", shape = "triangle", size = 20}
}
运行结果如下:
从打印出来的结果可以看到,对于二维表来说,每一个一维子表也是一对key-value,只不过key是数字索引(默认从1开始),value为table。
如果更进一步细致地打印出每一个循环中的数据类型和数量,则可验证本文最开始的lua_next()工作过程。有兴趣的读者可以打开以上例程中的打印语句,自己验证一下。