一、栈
Lua 和 C 之间的通讯主要组件是无处不在的虚拟栈,两者间的数据交换都是通过这个栈进行。
栈中可以保存 Lua 任意类型的值。
1、Lua 和 C 之间的数据交互存在的差异
- Lua 是动态类型,C 是静态类型,两者不匹配
- Lua 是自动内存管理,C 是手动内存管理
2、垃圾收集器
Lua 与 C/C++ 的数据交互,都是通过在一方压栈(存入数据),在另一方出栈(获取数据),所以栈是能感知到数据的。
而栈是 LuaState 的一部分,所以垃圾收集器能够知道 C/C++ 正在使用哪些数据,也就知道该如何管理内存。
3、栈操作原则
Lua 的栈严格遵守 LIFO (Last in first out,后进先出) , Lua 中只有栈顶的部分会发生改变。
C 语言代码则有更大的自由度,可以检视栈中的任何一个元素,甚至可以在栈的任意位置插入或删除元素。
二、C++ 压栈
针对每一种能用 C 语言直接表示的 Lua 数据类型,C API 中都有一个对应的压栈函数
下面是 C++ 所有的压栈函数
C API 函数 | 描述 |
---|---|
void (lua_pushnil) (lua_State *L); | 将 nil 压栈 |
void (lua_pushnumber) (lua_State *L, lua_Number n); | 将 双精度浮点数 压栈 |
void (lua_pushinteger) (lua_State *L, lua_Integer n); | 将 整型数 压栈 |
void (lua_pushboolean) (lua_State *L, int b); | 将 布尔值 压栈 |
const char *(lua_pushstring) (lua_State *L, const char *s); | 将 字符串(以 “\0” 结尾) 压栈 |
const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); | 将 字符串(会结合 “\0” 和长度的参数决定字符串的长度) 压栈 |
const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp); | 将 字符串(格式化字符串,接收可变参数) 压栈 |
const char *(lua_pushfstring) (lua_State *L, const char *fmt, …); | 将 字符串(格式化字符串) 压栈 |
void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); | 将 C 函数 压栈 |
void (lua_pushlightuserdata) (lua_State *L, void *p); | 将 用户数据 压栈 |
int (lua_pushthread) (lua_State *L); | 将 线程状态(即 lua_State ) 压栈 |
举个例子:
lua_pushcclosure
和lua_pushlightuserdata
后续文章进行分享
使用以上的所有 api ,进行相应数据类型的压栈
lua_pushnil(L);
lua_pushnumber(L, 0.1);
lua_pushnumber(L, 0);
lua_pushinteger(L, 10);
lua_pushboolean(L, 1);
// lua_pushstring 会在 "\0" 终止
lua_pushstring(L, "lua_pushstring hello 江澎涌\0jiang peng yong");
// lua_pushlstring 同样会在 "\0" 终止,但会结合考虑长度参数
const char *sayHello = "lua_pushlstring hello jiang peng yong.\0 会被忽略的字符";
lua_pushlstring(L, sayHello, strlen(sayHello) - 6);
// lua_pushvfstring
const char *name = "jiang peng yong";
int age = 29;
showInfo(L, name, age);
lua_pushfstring(L, "lua_pushfstring name: %s, age: %d", name, age);
lua_pushthread(L);
showInfo
函数如下所示,为了能演示 va_list
和 lua_pushvfstring
的结合使用
void showInfo(lua_State *L, ...) {
va_list args;
// 用于开启遍历可变参数,第二个参数是可变参数列表的前一参数
va_start(args, L);
// 在这里可以使用 va_list 对象 args
lua_pushvfstring(L, "lua_pushvfstring name: %s, age: %d", args);
va_end(args);
}
最后通过以下的函数将栈打印出来,就一目了然了
这一函数主要使用了以下的 api ,先有一个大概了解,接下来的小节会更加详细的讲解
lua_gettop
获取栈中元素个数lua_typename
获取类型名称lua_toxxx
获取栈对应索引的元素值,并转为相应类型 xxx
void stackDump(lua_State *L) {
int i;
// 获取栈中元素个数
int top = lua_gettop(L);
printf("栈顶\n");
for (i = top; i >= 1; i--) {
// 元素类型
int t = lua_type(L, i);
// 元素类型名称
printf("^ typename: %s, ", lua_typename(L, t));
switch (t) {
case LUA_TSTRING: // 字符串类型
printf("value: '%s'", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: // 布尔类型
printf(lua_toboolean(L, i) ? "value: true" : "value: false");
break;
case LUA_TNUMBER: // 数值类型
if (lua_isinteger(L, i)) {
// 是否整型
printf("value(integer): %lld", lua_tointeger(L, i));
} else {
// 浮点类型
printf("value(number): %g", lua_tonumber(L, i));
}
break;
default: // 其他类型
// 类型名称
printf("value: %s", lua_typename(L, t));
}
printf(" \n");
}
printf("栈底\n");
printf("\n");
}
打印之后,刚才一系列操作的栈内容如下:
栈顶
^ typename: thread, value: thread
^ typename: string, value: 'lua_pushfstring name: jiang peng yong, age: 29'
^ typename: string, value: 'lua_pushvfstring name: jiang peng yong, age: 29'
^ typename: string, value: 'lua_pushlstring hello jiang peng'
^ typename: string, value