首先是最简单的 Lua 为 C/C++ 程序变量赋值,类似史前的 INI 配置文件。
width = 640
height = 480
这样的赋值即设置全局变量,本质上就是在全局表中添加字段。
在 C/C++ 中,Lua其实并不是直接去改变变量的值,而是宿主程序通过「读取脚本中设置的全局变量到栈、类型检查、从栈上取值」几步去主动查询。
int w, h;
if (luaL_loadfile(L, fname) || // 读取文件,将内容作为一个函数压栈
lua_pcall(L, 0, 0, 0)) // 执行栈顶函数,0个参数、0个返回值、无出错处理函数(出错时直接把错误信息压栈)
error();
lua_getglobal(L, "width"); // 将全局变量 width 压栈
lua_getglobal(L, "height"); // 将全局变量 height 压栈
if (!lua_isnumber(L, -2)) // 自顶向下第二个元素是否为数字
error();
if (!lua_isnumber(L, -1)) // 自顶向下第一个元素是否为数字
error();
w = lua_tointeger(L, -2); // 自顶向下第二个元素转为整型返回
h = lua_tointeger(L, -1); // 自顶向下第一个元素转为整型返回
读取表的字段的操作也是类似,只不过细节上比较麻烦,有点让我想起在汇编里调戏各种寄存器:
score = { chinese=80, english=85 }
int chinese, english;
if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0))
error();
lua_getglobal(L, "score"); // 全局变量 score 压栈
lua_pushstring(L, "chinese"); // 字符串 math 压栈
lua_gettable(L, -2); // 以自顶向下第二个元素为表、第一个元素为索引取值,弹栈,将该值压栈
if (!lua_isnumber(L, -1)) // 栈顶元素是否为数字
error();
chinese = lua_tointeger(L, -2);
lua_pop(L, 1); // 弹出一个元素 (此时栈顶为 score 变量)
lua_getfield(L, -1, "english"); // Lua5.1开始提供该函数简化七八两行
if (!lua_isnumber(L, -1))
error();
english = lua_tointeger(L, -2);
lua_pop(L, 1); // 如果就此结束,这一行弹不弹都无所谓了
前面说过,设置全局变量本质就是在全局表中添加字段,所以 lua_getglobal
函数本质是从全局表中读取字段。没错,lua_getglobal
本身就是一个宏:
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
宏 LUA_GLOBALSINDEX
指明的就是全局表的索引。
于是基本上今天就看了这么多……