文章目录
- Lua调用C++
-
- [1. Lua与C++交互次序](#1. Lua与C++交互次序)
- [2. Lua栈的访问下标](#2. Lua栈的访问下标)
- [3. 示例](#3. 示例)
-
- [3.1 无参数无返回值](#3.1 无参数无返回值)
- [3.2 普通参数](#3.2 普通参数)
- [3.3 数组参数](#3.3 数组参数)
- [3.4 表类型参数](#3.4 表类型参数)
-
- [3.4.1 lua_next(L, 1)遍历表:](#3.4.1 lua_next(L, 1)遍历表:)
- [3.4.2 lua_getfield(L, 1, "name")获取表中内容](#3.4.2 lua_getfield(L, 1, "name")获取表中内容)
- [3.5 有返回值的函数](#3.5 有返回值的函数)
Lua调用C++
-
函数调用
-
参数传递
-
返回值获取
1. Lua与C++交互次序

2. Lua栈的访问下标

3. 示例
3.1 无参数无返回值
cpp
int CTest(lua_State* L) {
printf("int CTest\n");
return 0; //返回值个数
}
lua_register(L, "CTest1", CTest);
lua
CTest1()
3.2 普通参数
cpp
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);
lua
CTest2("hello skan", 123)
3.3 数组参数
cpp
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 >= 502
size_t sz = lua_rawlen(L, 1);
#else
size_t sz = lua_objlen(L, 1);
#endif
cout << "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,并且不向栈中压入任何值。
cpp
// 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 >= 502
size_t sz = lua_rawlen(L, 1); // Lua 5.2+ 使用 lua_rawlen
#else
size_t sz = lua_objlen(L, 1); // Lua 5.1 使用 lua_objlen
#endif
cout << "table size: " << sz << endl;
// 当前栈: [table]
// 压入 nil 作为初始键,用于开始遍历表
lua_pushnil(L);
// 当前栈: [table nil]
// lua_next(L, 1) 的工作原理:
// 1. 弹出栈顶的键 (第一次是 nil)
// 2. 找到表中的下一个键值对
// 3. 将键和值依次压入栈
// 4. 如果还有下一对,返回非 0;遍历结束返回 0
while (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);
else
cout << 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] 状态
}
lua
-- main.lua
local tab = {"001", "002", "003"}
CTestTable(tab)
3.4.2 lua_getfield(L, 1, "name")获取表中内容
cpp
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)
cpp
// main.cpp
int CTestRet(lua_State* L) {
lua_pushstring(L, "return skan");
return 1; //取栈顶的第一个元素作为返回值
}
lua
-- main.lua
local ret = CTestRet();
print("ret from CTestRet:" .. ret);
- 返回表对象
cpp
// 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 = 42
lua_pushinteger(L, 42); // 栈: [..., table, 42]
lua_setfield(L, -2, "age"); // 弹出 42,设置 table["age"]=42
// 布尔字段: table.online = true
lua_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 必须位于栈顶)
}
lua
-- main.lua
local ret = CTestRet();
if ret == nil then
print("ret is nil")
return
end
if(type(ret) == "table") then
for key, value in pairs(ret) do
print("Key: " .. key .. ", Value: " .. value)
end
end