• Lua与C交互API接口总结


    1. 常见Lua相关的C API

    压入元素

    // cpp
    void lua_pushnil(lua_State *L);
    void lua_pushboolean(lua_State *L, int bool);
    void lua_pushnumber(lua_State *L, lua_Number n);
    void lua_pushinteger(lua_State *L, lua_Integer n);
    void lua_pushlstring(lua_State *L, const char* s, size_t len);
    void lua_pushstring(lua_State *L, const char* s);
    void lua_pushfunction(lua_State *L, lua_CFunction fn);
    

    查询元素

    // cpp
    int lua_is***(lua_State *L, int index); // 检查lua数据类型 nil number string table等
    
    // lua_type 函数是 Lua C API 中用于获取指定索引处的值的类型的函数。它返回一个表示值类型的整数,并且不会改变堆栈上的内容。
    /* 如果索引处的值存在,则返回该值的类型,以整数形式表示。返回值为以下预定义的常量之一:
    LUA_TNIL:空值。
    LUA_TBOOLEAN:布尔值。
    LUA_TLIGHTUSERDATA:轻量用户数据。
    LUA_TNUMBER:数字。
    LUA_TSTRING:字符串。
    LUA_TTABLE:表。
    LUA_TFUNCTION:函数。
    LUA_TUSERDATA:用户数据。
    LUA_TTHREAD:线程(协程)。
    如果索引处的值不存在,则返回 LUA_TNONE。
    */
    int lua_type(lua_State *L, int index);
    
    

    获取元素

    // cpp
    // to* 相关操作并不会改变lua栈,只是从栈中index出取值并转成c变量类型
    
    int lua_toboolean(lua_State *L, int index); // 从栈中获取bool值
    
    const char *lua_tostring(lua_State *L, int index);
    
    // len:用于存储字符串长度的指针(可选参数)。如果为 NULL,则不返回字符串长度
    const char *lua_tolstring(lua_State *L, int index, size_t *len);
    
    // lua_Integer 通常在 Lua 的头文件(如 lua.h 或 luaconf.h)中定义
    // typedef long long lua_Integer
    lua_Integer lua_tointeger(lua_State *L, int index);
    
    // lua_Number 通常在 Lua 的头文件(如 lua.h 或 luaconf.h)中定义
    // typedef double lua_Number;
    lua_Number lua_tonumber(lua_State *L, int index);
    
    

    检查元素

    // cpp
    int luaL_checkinteger(lua_State *L, int arg); 
    
    lua_Number luaL_checknumber(lua_State *L, int arg);
    
    const char* luaL_checkstring(lua_State *L, int arg);
    
    int luaL_checkboolean(lua_State *L, int arg);
    
    // t:要检查的类型,在 Lua 中表示为预定义的宏,例如 LUA_TNUMBER、LUA_TSTRING 等。
    void luaL_checktype(lua_State *L, int arg, int t);
    

    栈的相关数据操作

    // cpp
    lua_pop(lua_State *L, int n); // 弹出栈顶的 n 个值。
    
    lua_gettop(lua_State *L); // 返回堆栈的栈顶索引(但不修改堆栈)。
    
    lua_settop(lua_State *L, int index);// 修改栈元素数量,减小栈则会丢弃多余部分元素,增大会push nil值
    
    lua_remove(lua_State *L, int index); // 移除指定索引处的值,并将上面的所有值下移。
    
    // 用于将指定索引处的值复制到堆栈顶部。它不会删除原始值,而是将其复制一份并推入堆栈。
    void lua_pushvalue(lua_State *L, int index);
    
    // 用于将栈顶的值弹出,并将其设置为lua的全局变量
    void lua_setglobal(lua_State *L, const char *name);
    
    // 用于将lua全局变量的值推入堆栈
    void lua_getglobal(lua_State *L, const char *name);
    
    // 用于将栈顶的值弹出,并将其设置为index处的表中指定字段的值
    /* lua_setfield 从栈顶弹出一个值,并将其设置为表中指定字段的值。
    这个操作相当于在 Lua 中执行 t[k] = value,其中 t 是栈中索引为 index 的表,k 是字段名,value 是栈顶的值。*/
    void lua_setfield(lua_State *L, int index, const char *k);
    
    // 用于从指定索引处的表中获取一个字段的值,并将其推入堆栈
    void lua_getfield(lua_State *L, int index, const char *k);
    

    2. C调用Lua

    核心调用函数

    void lua_call(lua_State *L, int nargs, int nresults); // 相对lua_pcall来说至少了错误处理函数
    
    int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);
    /* lua_State *L:指向 Lua 状态的指针。
    int nargs:要传递给 Lua 函数的参数数量。
    int nresults:Lua 函数预期返回的结果数量。
    int errfunc:错误处理函数在堆栈中的索引。通常为 0,表示没有错误处理函数。
    
    0:调用成功。
    非零值:调用失败,对应于不同的错误代码,例如 LUA_ERRRUN、LUA_ERRMEM、LUA_ERRERR 等。
    
    lua_call和lua_pcall 执行时在堆栈上进行以下操作:
    从栈顶开始,依次向下计算 nargs 个元素,这些元素表示传递给函数的参数,会依次弹出这几个参数。
    在参数之后的那个元素应该是要调用的函数。
    弹出函数本身,调用函数,并期望 nresults 个返回值,压入 nresults 个返回值。
    */
    
    

    示例

    -- Lua
    function add(a, b)
        return a + b
    end
    
    // cpp
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        lua_State *L = luaL_newstate();  // 创建新的 Lua 状态
        luaL_openlibs(L);                // 打开标准库
    
        if (luaL_dofile(L, "script.lua")) {  // 加载并执行 Lua 脚本
            fprintf(stderr, "Failed to load script: %s\n", lua_tostring(L, -1));
            return 1;
        }
    
        lua_getglobal(L, "add");  // 将全局函数 "add" 压入堆栈
    
        if (!lua_isfunction(L, -1)) {
            fprintf(stderr, "'add' is not a function\n");
            return 1;
        }
    
        lua_pushnumber(L, 10);  // 压入第一个参数
        lua_pushnumber(L, 20);  // 压入第二个参数
    
    	// lua_call(L, 2, 1);  // 调用函数,传递 2 个参数,期望 1 个结果
        if (lua_pcall(L, 2, 1, 0) != 0) {  // 调用函数,传递 2 个参数,期望 1 个结果
            fprintf(stderr, "Error calling 'add': %s\n", lua_tostring(L, -1));
            return 1;
        }
    
        if (lua_isnumber(L, -1)) {
            double result = lua_tonumber(L, -1);
            printf("Result: %f\n", result);  // 打印结果
        } else {
            fprintf(stderr, "Function 'add' did not return a number\n");
        }
    
        lua_pop(L, 1);  // 从堆栈中移除结果
        lua_close(L);   // 关闭 Lua 状态
    
        return 0;
    }
    
    

    3. Lua调用C

    Lua调用C函数时,必须遵守int FunctionName(lua_State *L)类型去定义并实现,其中int返回值表示参数个数。当每个Lua调用C函数时,会自动在内部维护一个私有局部栈,因此我们去参数时直接从1取就可以,且在调用时与结束调用时无需考虑栈的清理问题。

    1. C函数注册到Lua(lua_register)

    // lua_register 是 Lua C API 中的一个宏,用于将 C 函数注册为 Lua 全局函数
    
    /* 这个宏实际上是 lua_pushcfunction 和 lua_setglobal 两个函数的组合:
    lua_pushcfunction(L, f):将 C 函数 f 压入 Lua 堆栈。
    lua_setglobal(L, n):将堆栈顶部的值(即刚刚压入的 C 函数 f)设置为全局变量 n。*/
    
    #define lua_register(L, n, f) \
        (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
    
    

    示例

    -- script.lua
    local result = add(10, 20)
    print("Result of add(10, 20):", result)
    
    // cpp
    #include 
    #include 
    
    // C++ 函数
    int add(lua_State* L) {
        int a = luaL_checkinteger(L, 1);
        int b = luaL_checkinteger(L, 2);
        lua_pushinteger(L, a + b);
        return 1;
    }
    
    // 将 C++ 函数注册到 Lua
    void register_functions(lua_State* L) {
        lua_register(L, "add", add);
    }
    
    int main() {
        lua_State* L = luaL_newstate();  // 创建新的 Lua 状态
        luaL_openlibs(L);                // 打开标准库
    
        register_functions(L);  // 注册 C++ 函数
    
        if (luaL_dofile(L, "script.lua")) {  // 执行 Lua 脚本
            std::cerr << "Failed to load script: " << lua_tostring(L, -1) << std::endl;
            lua_pop(L, 1);  // 从堆栈中移除错误消息
        }
    
        lua_close(L);  // 关闭 Lua 状态
        return 0;
    }
    

    2. 批量注册(luaL_Reg)

    // cpp
    typedef struct luaL_Reg {
      const char *name; // 函数的名字
      lua_CFunction func; // 对应的 C 函数
    } luaL_Reg;
    
    void luaL_newlib (lua_State *L, const luaL_Reg l[]);
    /*这个实际上是 luaL_newlibtable 和 luaL_setfuncs两个函数的组合:
    
    void luaL_newlibtable(lua_State *L, const luaL_Reg l[]); 
    其中l为指向 luaL_Reg 结构体数组的指针,该数组定义了库中的函数。
    luaL_newlibtable 是 Lua C API 提供的一个函数,用于创建一个新的空表,
    这个表的大小预分配为注册表中定义的库函数数量。
    这在创建一个新的 Lua 库时特别有用,因为它可以预先分配合适的空间以容纳所有的库函数,避免动态扩展表的开销。
    
    void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup);
    l:指向 luaL_Reg 结构体数组的指针,该数组定义了库中的函数。
    nup:传递给每个函数的 upvalue 数量。通常为 0。
    luaL_setfuncs用于将 C 函数数组注册到一个 Lua 表中。
    */
    

    示例

    // cpp
    #include 
    #include 
    
    // C 函数:加法
    int add(lua_State* L) {
        int a = luaL_checkinteger(L, 1);
        int b = luaL_checkinteger(L, 2);
        lua_pushinteger(L, a + b);
        return 1;
    }
    
    // C 函数:减法
    int sub(lua_State* L) {
        int a = luaL_checkinteger(L, 1);
        int b = luaL_checkinteger(L, 2);
        lua_pushinteger(L, a - b);
        return 1;
    }
    
    // 创建 Lua 库
    extern "C" int luaopen_mylib(lua_State* L) {
        luaL_Reg mylib[] = {
            {"add", add},
            {"sub", sub},
            {NULL, NULL}
        };
        luaL_newlib(L, mylib);
        return 1;
    }
    
    int main() {
        lua_State* L = luaL_newstate();
        luaL_openlibs(L);
    
        luaopen_mylib(L);  // 手动打开库(如果需要)
    
        if (luaL_dofile(L, "script.lua")) {
            std::cerr << "Failed to load script: " << lua_tostring(L, -1) << std::endl;
            lua_pop(L, 1);
        }
    
        lua_close(L);
        return 0;
    }
    
    -- script.lua
    local mylib = require("mylib")
    
    local result_add = mylib.add(10, 20)
    print("Result of add(10, 20):", result_add)
    
    local result_sub = mylib.sub(20, 10)
    print("Result of sub(20, 10):", result_sub)
    
  • 相关阅读:
    DataFrame和list之间相互转换:df.values.tolist()和pd.DataFrame()
    java实现pdf文件添加水印,下载到浏览器
    sublime text 格式化json快捷键配置
    MR混合现实在军事课堂教学中的应用演示
    编程技巧│php 自定义安装扩展
    内核中oops 错误解析以及问题定位
    知道 Redis RDB 这些细节,可以少踩很多坑
    HUD—6287,口算训练,思维,素因子分解,lower_bound, upper_bound
    javaFx+google chrome测试下载视频
    接口开放太麻烦?试试阿里云API网关吧
  • 原文地址:https://blog.csdn.net/lzh824359508/article/details/139580432