有关 Lua 调用 C++ 编译动态库程序 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kanhongj
V2EX    Lua

有关 Lua 调用 C++ 编译动态库程序

  •  
  •   kanhongj 2022-01-06 11:08:39 +08:00 4550 次点击
    这是一个创建于 1422 天前的主题,其中的信息可能已经有所发展或是发生改变。

    主要目标

    希望能够将 cpp 文件编译成动态库, 以 lua 作为主要运行逻辑(main 函数)

    希望解决的问题

    1. 刚入门 Lua 不太明白,若编译了一个 cpp 文件里包含了一个 Worker 对象,Lua 中如何调用生成这个对象并根据公有函数对私有变量进行操作。
    2. 查找到的一些示例代码,大多是将 main 逻辑放在 cpp 文件里,生成元表作为全局变量传入 Lua 脚本进行操作。但是将 Lua 作为程序运行入口,C++ 库作为辅助函数,又该如何操作。
    3. Lua 调试工具各位 Lua 大佬能否介绍一下,或者推荐一些调试方法,lua 的 debug 调试使用我还是有点懵。

    自身尝试代码

    稍微有点多,感谢耐心观看

    classstudy.h

    #pragma once extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } #include <string> class Worker{ public: Worker(); ~Worker(); int SetName(std::string &name); int SetAge(int &age); int SetHight(float &hight); std::string GetName(); int GetAge(); float GetHight(); private: std::string name; int age; float hight; }; extern "C" int luaopen_cstudy(lua_State *L); 

    classstudy.cpp

    #include "classstudy.h" #include <iostream> /* Class Worker 的函数定义省略 */ static int CreateNewWorker(lua_State *L){ //ligth_userdata // int n = luaL_checkany(L, 1); Worker **w1 = (Worker**)lua_newuserdata(L, sizeof(Worker*)); *w1 = new Worker(); if(luaL_getmetatable(L, "WorkerClass") == false){ printf("getmetatable nil\n"); } lua_setmetatable(L, -2); return 1; } static int SetWorkerName(lua_State *L){ luaL_checktype(L, -1, LUA_TSTRING); std::string var_name = lua_tostring(L, -1); printf("%s\n", var_name.c_str()); Worker **w1 = (Worker**)lua_newuserdata(L, sizeof(Worker*)); luaL_argcheck(L, w1 != NULL, 1, "invalid user data"); (*w1)->SetName(var_name); return 0; } static int SetWorkerAge(lua_State *L){ luaL_checktype(L, -1, LUA_TNUMBER); int var_age = lua_tointeger(L, -1); Worker **w1 = (Worker**)lua_newuserdata(L, sizeof(Worker*)); luaL_argcheck(L, w1 != NULL, 1, "invalid user data"); (*w1)->SetAge(var_age); return 0; } static int SetWorkerHight(lua_State *L){ luaL_checktype(L, -1, LUA_TNUMBER); float var_hight = lua_tonumber(L, -1); Worker **w1 = (Worker**)lua_newuserdata(L, sizeof(Worker*)); luaL_argcheck(L, w1 != NULL, 1, "invalid user data"); (*w1)->SetHight(var_hight); return 0; } static int GetWorkerInfo(lua_State *L){ Worker **w1 = (Worker**)lua_newuserdata(L, sizeof(Worker*)); luaL_argcheck(L, w1 != NULL, 1, "invalid user data"); printf("Name: %s\n", ((*w1)->GetName()).c_str()); printf("Age: %d\n", (*w1)->GetAge()); printf("Hight: %f\n", (*w1)->GetHight()); return 0; } static int DestoryInfo(lua_State* L) { // 释放对象 delete *(Worker**)lua_topointer(L, 1); return 0; } const static luaL_Reg mylib[] = { // {"NewWorker", CreateNewWorker}, {"SetName", SetWorkerName}, {"SetAge", SetWorkerAge}, {"SetHight", SetWorkerHight}, {"PrintInfo", GetWorkerInfo}, {NULL,NULL} }; int luaopen_cstudy(lua_State *L){ // C\++对象 = 私有数据 + 类(公共数据 + 公共方法) // Lua Table = 私有数据 + 元表(元数据 + 元函数) // luaL_newlib(L, mylib); lua_pushcfunction(L, CreateNewWorker); // 注册用于创建类的全局函数 lua_setglobal(L, "CWorker"); luaL_newmetatable(L, "WorkerClass"); // 设置自身 lua_pushstring(L, "__gc"); lua_pushcfunction(L, DestoryInfo); lua_settable(L, -3); // 设置元表 lua_pushstring(L, "__index"); // 设置元表为自己 lua_pushvalue(L, -2); lua_settable(L, -3); lua_pushstring(L, "SetName"); lua_pushcfunction(L, SetWorkerName); lua_settable(L, -3); lua_pushstring(L, "SetAge"); lua_pushcfunction(L, SetWorkerAge); lua_settable(L, -3); lua_pushstring(L, "SetHight"); lua_pushcfunction(L, SetWorkerHight); lua_settable(L, -3); lua_pushstring(L, "PrintInfo"); lua_pushcfunction(L, GetWorkerInfo); lua_settable(L, -3); // lua_pop(L, 1); return 1; } 

    classstudy.lua

    local csl = require("cstudy") local Worker = CWorker() print(debug.getregistry()) Worker.SetName("hello") Worker.PrintInfo() -- csl.SetAge(23) -- csl:SetHight(180.0) -- csl:PrintInfo() 
    16 条回复    2023-06-25 01:10:53 +08:00
    anytk
        1
    anytk  
       2022-01-06 11:32:58 +08:00
    元表是对象,也就是你的 worker 的元表,包含对象的方法,在方法中检查第一个参数为对应元表名的 userdata, 其他为参数。
    模块本身只提供创造对象的方法和必要全局参数。
    参考:

    ```c
    #include <lua.h>
    #include <lauxlib.h>

    static int xxx_read(lua_State *L)
    {
    struct XXX **xxx = luaL_checkudata(L, 1, "xxx.xxx");
    // ...
    }

    static int xxx_write(lua_State *L)
    {
    struct XXX **xxx = luaL_checkudata(L, 1, "xxx.xxx");
    // ...
    }

    static int xxx_close(lua_State *L)
    {
    struct XXX **xxx = luaL_checkudata(L, 1, "xxx.xxx");
    if (*xxx) {
    // ...
    }
    return 0;
    }

    static luaL_Reg XXX_METHODS[] = {
    { "read", xxx_read },
    { "write", xxx_write },
    { "close", xxx_close },
    { "__gc", xxx_close },
    { NULL, NULL },
    };

    static int xxx_new(lua_State *L)
    {
    lua_Integer type = luaL_checkinteger(L, 1);
    const char *url = luaL_checkstring(L, 2);

    int err = 0;
    struct XXX *xxx = create_xxx(...);
    if (!xxx) {
    lua_pushnil(L);
    return 1;
    } else {
    struct XXX **udata = lua_newuserdata(L, sizeof(struct XXX *));
    *udata = NULL;
    luaL_setmetatable(L, "xxx.xxx");
    *udata = xxx;
    return 1;
    }
    }

    static luaL_Reg XXX_LIBS[] = {
    { "new", xxx_new },
    { NULL, NULL },
    };

    int luaopen_xxx.xxx(lua_State *L)
    {
    luaL_newmetatable(L, "xxx.xxx");
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    luaL_setfuncs(L, XXX_METHODS, 0);
    lua_pop(L, 1);
    luaL_newlib(L, XXX_LIBS);
    return 1;
    }

    ```
    fengjianxinghun
        2
    fengjianxinghun  
       2022-01-06 11:36:50 +08:00
    何必这么麻烦,改用 luajit 的 ffi 走起。
    ysc3839
        3
    ysc3839  
       2022-01-06 11:41:50 +08:00
    既然是 C++ 的话推荐找一个封装好的库,lua 使用栈传递参数,手写很麻烦还容易出错。
    kanhongj
        4
    kanhongj  
    OP
       2022-01-06 11:52:03 +08:00
    @anytk 谢谢,我一会尝试看看
    kanhongj
        5
    kanhongj  
    OP
       2022-01-06 12:26:08 +08:00
    @fengjianxinghun 这个我后续看看
    @ysc3839 我一开始是想着热更新去的,如果用库,还是需要重新编译啊
    guo4224
        6
    guo4224  
       2022-01-06 12:27:45 +08:00
    ysc3839
        7
    ysc3839  
       2022-01-06 12:36:43 +08:00 via Android
    @kanhongj 啥意思?我说的是用封装好的调用 lua api 的库,lua 的 C API 使用栈传递参数,手写很麻烦还容易出错。
    paoqi2048
        8
    paoqi2048  
       2022-01-06 13:22:26 +08:00
    不是有一堆 binding 吗?如果是出于学习的目的就当我没说
    kanhongj
        9
    kanhongj  
    OP
       2022-01-07 10:22:26 +08:00
    @paoqi2048 刚接触 lua, 我还没了解到这些 binding, 请问有什么推荐学习的文章吗?
    @ysc3839 嗯,记下了
    kanhongj
        10
    kanhongj  
    OP
       2022-01-07 10:31:16 +08:00
    @anytk 这个示例我尝试成功了,受教了,但是还是有些疑惑的地方:

    1. luaopen_xxx.xxx 函数里边,为什么生成了一个 xxx.xxx 表,且设定了元表,又给 pop 出去了, 这样 new_xxx 方法里边的 luaL_setmetatable(L, "xxx.xxx") 怎么找到的。

    2. lua 代码里又一个 local csl = require("xxx.xxx") 这里的 csl 包含了一个 new 方法调用,csl 是上文的 metatable , 还是 luaL_newlib ?
    kanhongj
        11
    kanhongj  
    OP
       2022-01-07 10:37:03 +08:00
    @kanhongj 这个是我的 lua 代码:

    ```lua

    local csl = require("cstudy")
    local Worker = csl.new()
    Worker:SetName("hello")
    -- Worker:PrintInfo()
    Worker:SetAge(23)
    Worker:SetHight(180.0)
    Worker:PrintInfo()

    ```
    kanhongj
        12
    kanhongj  
    OP
       2022-01-07 10:38:02 +08:00
    @kanhongj 这个是我的 lua 代码:

    ```lua

    local csl = require("cstudy")
    local Worker = csl.new()
    Worker:SetName("hello")
    -- Worker:PrintInfo()
    Worker:SetAge(23)
    Worker:SetHight(180.0)
    Worker:PrintInfo()

    ```
    anytk
        13
    anytk  
       2022-01-07 11:19:25 +08:00   1
    @kanhongj
    1. 元表是给对象准备的,设置 __index 实现面向对象,所有的方法都绑定到元表中,对象的方法通过 __index 去执行元表内的方法。luaL_newmetatable 这个 api 会将元表加到 REGISTRY 中,这是个全局 table , 所以设定好后,这个元表就可以 pop 掉了,因为 REGISTRY 保存了引用,所以不用担心被回收。
    luaL_setmetatable API 同样是从 REGISTRY 中用名称获取元表并绑定对象,可以看看这几个 luaL_ 开头的 api 注释。
    2. new 方法是 luaL_newlib 出来的模块 table 方法,不是元表的。
    anytk
        14
    anytk  
       2022-01-07 11:22:16 +08:00   1
    @kanhongj lua 的 c api 编程,其实就是利用 api 去仿照 lua 语法语义组织执行语句,尤其是 gc 相关。
    kanhongj
        15
    kanhongj  
    OP
       2022-01-07 15:12:52 +08:00 via iPhone
    @anytk 好的,我明了了,谢谢
    asuraa
        16
    asuraa  
       2023-06-25 01:10:53 +08:00
    太麻烦了 为啥不用 Luabridge
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     884 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 1246ms UTC 22:12 PVG 06:12 LAX 14:12 JFK 17:12
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86