V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kanhongj
V2EX  ›  Lua

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

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

    主要目标

    希望能够将 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   ·   实用小工具   ·   1780 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:26 · PVG 00:26 · LAX 08:26 · JFK 11:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.