【C++/Lua联合开发】 (二) Lua调用C++函数
文章目录
- Lua调用C++
- 1. Lua与C++交互次序
- 2. Lua栈的访问下标
- 3. 示例
- 3.1 无参数无返回值
- 3.2 普通参数
- 3.3 数组参数
- 3.4 表类型参数
- 3.4.1 lua_next(L, 1)遍历表:
- 3.4.2 lua_getfield(L, 1, "name")获取表中内容
- 3.5 有返回值的函数
Lua调用C++
-
函数调用
-
参数传递
-
返回值获取
1. Lua与C++交互次序
2. Lua栈的访问下标
3. 示例
3.1 无参数无返回值
int CTest(lua_State* L) {printf("int CTest\n");return 0; //返回值个数
}lua_register(L, "CTest1", CTest);
CTest1()
3.2 普通参数
int CTest(lua_State* L) {cout << "int CTest" << endl;size_t sz = 0;const char* arg1 = lua_tolstring(L, 1, &sz); //从栈底开始取元素并出栈int arg2 = lua_tonumber(L, 2);cout << "arg1: " << arg1 << ", size: " << sz << ", arg2: " << arg2 << endl;return 0;
}lua_register(L, "CTest2", CTest);
CTest2("hello skan", 123)
3.3 数组参数
int CTestArray(lua_State* L) { cout << "int CTestArray" << endl;if (!lua_istable(L, 1)) {cout << "arg1 is not a table" << endl;return 0;}//取得栈上指定索引处对象的“长度”(字符串长度或表的长度)
#if LUA_VERSION_NUM >= 502size_t sz = lua_rawlen(L, 1);
#elsesize_t sz = lua_objlen(L, 1);
#endifcout << "array size: " << sz << endl;for (size_t i = 1; i <= sz; ++i) {lua_pushinteger(L, i);lua_gettable(L, 1);cout << "element " << i << ": " << lua_tostring(L, -1) << endl;lua_pop(L, 1);}return 0;
}
1.调用前准备: 必须手动将两样东西放到 Lua 栈上:- 要查询的 **表(table)**- 要使用的 **键(key)** 2. 栈状态 (调用前): 假设你要执行 `value = my_table[my_key]`。你需要先将 `my_table` 和 `my_key` 压栈。`my_key` 必须在栈顶。栈底 | ... | my_table (在索引 index 处) | ... | my_key (在栈顶, -1) |3. 执行 lua_gettable(L, index):函数会查看 index 参数,找到 my_table。它会弹出 (pop) 栈顶的 my_key。它在 my_table 中查找 my_key 对应的 value。最后,它将查找到的 value 压入 (push) 栈顶。4. 栈状态 (调用后): my_key 被 value 替换掉了。
栈底 | ... | my_table (在索引 index 处) | ... | value (在栈顶, -1) |
3.4 表类型参数
3.4.1 lua_next(L, 1)遍历表:
lua_next(L, 1)
- 从栈顶弹出一个
key
- 从栈指定位置的
table
里取一对key-value, 先将key
入栈再将value
入栈 - 如果第2步成功,返回非0值,否则返回0,并且不向栈中压入任何值。
// Lua C 函数,用于遍历和分析 Lua 表的内容
// 参数: L - Lua 状态机
// 返回值: 0 (不向 Lua 返回任何值)
// 调用示例 (Lua): CTestTable({name="test", age=18, [1]="first"})
int CTestTable(lua_State* L) { cout << "int CTestTable" << endl;// 初始状态: [table] (假设调用时传入一个表作为第一个参数)// 检查第一个参数是否为表类型if (!lua_istable(L, 1)) {cout << "arg1 is not a table" << endl;return 0;}// 当前栈: [table]// 获取表的"数组部分"长度(连续整数索引 1, 2, 3... 的元素个数)// 注意: 这只统计数组部分,不包括哈希部分(字符串键等)
#if LUA_VERSION_NUM >= 502size_t sz = lua_rawlen(L, 1); // Lua 5.2+ 使用 lua_rawlen
#elsesize_t sz = lua_objlen(L, 1); // Lua 5.1 使用 lua_objlen
#endifcout << "table size: " << sz << endl;// 当前栈: [table]// 压入 nil 作为初始键,用于开始遍历表lua_pushnil(L);// 当前栈: [table nil]// lua_next(L, 1) 的工作原理:// 1. 弹出栈顶的键 (第一次是 nil)// 2. 找到表中的下一个键值对// 3. 将键和值依次压入栈// 4. 如果还有下一对,返回非 0;遍历结束返回 0while (lua_next(L, 1) != 0) {// 第一次迭代: [table key1 value1]// 第二次迭代: [table key2 value2]// ...// 注意: 索引 -2 是键,索引 -1 是值cout << "key: " << lua_tostring(L, -2) << ", value: ";// 根据值的类型输出不同的内容if (lua_isstring(L, -1))cout << lua_tostring(L, -1);else if (lua_isnumber(L, -1))cout << lua_tonumber(L, -1);elsecout << lua_typename(L, lua_type(L, -1));cout << endl;// 当前栈: [table key value]// 弹出值,保留键给下一次 lua_next 使用lua_pop(L, 1);// 当前栈: [table key]// 循环继续,lua_next 会:// 1. 弹出当前的 key// 2. 压入下一个 key 和 value (如果有)}// 遍历结束后,lua_next 返回 0 并弹出了最后一个键// 最终栈状态: [table]return 0; // 不向 Lua 返回任何值,栈保持 [table] 状态
}
-- main.lua
local tab = {"001", "002", "003"}
CTestTable(tab)
3.4.2 lua_getfield(L, 1, “name”)获取表中内容
static struct script_ctx *get_ctx(lua_State *L) {// 初始栈: [...]lua_getfield(L, LUA_REGISTRYINDEX, "ctx");// 从注册表中获取 "ctx" 字段// 堆栈: [... ctx_userdata]struct script_ctx *ctx = lua_touserdata(L, -1);lua_pop(L, 1);// 堆栈: [...]mp_assert(ctx);return ctx;
}
3.5 有返回值的函数
- 返回普通类型对象
lua_pushstring(l, str)
lua_pushnumber(l, 123)
// main.cpp
int CTestRet(lua_State* L) { lua_pushstring(L, "return skan");return 1; //取栈顶的第一个元素作为返回值
}
-- main.lua
local ret = CTestRet();
print("ret from CTestRet:" .. ret);
- 返回表对象
// main.cpp
int CTestRet(lua_State* L) { lua_newtable(L); // 创建一个新表,栈: [..., table]// 字符串字段: table.name = "skan"lua_pushstring(L, "skan"); // 栈: [..., table, "skan"]lua_setfield(L, -2, "name"); // 弹出 "skan",设置 table["name"]="skan",表仍在栈上// 数值字段: table.age = 42lua_pushinteger(L, 42); // 栈: [..., table, 42]lua_setfield(L, -2, "age"); // 弹出 42,设置 table["age"]=42// 布尔字段: table.online = truelua_pushboolean(L, 1); // 栈: [..., table, true]lua_setfield(L, -2, "online");// 弹出 true,设置 table["online"]=true// 作为数组部分插入: table[1] = "first", table[2] = "second"lua_pushstring(L, "first"); lua_rawseti(L, -2, 1); // 弹出 "first",设置 table[1]="first"lua_pushstring(L, "second");lua_rawseti(L, -2, 2); // 弹出 "second",设置 table[2]="second"return 1; // 返回栈顶的表(table 必须位于栈顶)
}
-- main.lua
local ret = CTestRet();if ret == nil thenprint("ret is nil")return
endif(type(ret) == "table") thenfor key, value in pairs(ret) doprint("Key: " .. key .. ", Value: " .. value)end
end