【C++/Lua联合开发】 (二) Lua调用C++函数

文章目录

  • 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)

  1. 从栈顶弹出一个key
  2. 从栈指定位置的table里取一对key-value , 先将key入栈再将value入栈
  3. 如果第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 有返回值的函数

  1. 返回普通类型对象
    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);
  1. 返回表对象
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
相关推荐
CoderCodingNo4 小时前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法
一个不知名程序员www4 小时前
算法学习入门---双指针(C++)
c++·算法
Maple_land5 小时前
常见Linux环境变量深度解析
linux·运维·服务器·c++·centos
Larry_Yanan5 小时前
QML学习笔记(四十四)QML与C++交互:对QML对象设置objectName
开发语言·c++·笔记·qt·学习·ui·交互
烛阴5 小时前
Lua 模块的完整入门指南
前端·lua
Want5955 小时前
C/C++大雪纷飞①
c语言·开发语言·c++
Mr_WangAndy5 小时前
C++设计模式_行为型模式_策略模式Strategy
c++·设计模式·策略模式·依赖倒置原则
LoveXming6 小时前
Chapter11—适配器模式
c++·设计模式·适配器模式·开闭原则
Benny_Tang6 小时前
题解:P7989 [USACO21DEC] Bracelet Crossings G
c++·算法