在lua中操作C++对象

2022年4月24日 602点热度 1人点赞 0条评论

前言

在项目中使用C++与Lua互相调用,有的时候需要在lua脚本中,对C++的对象进行一些操作。

最开始的做法时,将这些对象声明为单例,或者是将对象放入单例之中,总之就是把对像放在一个全局可获取的地方,然后调用lua调用时获取对象再进行相应的操作。

不过这样的做法十分不优雅,并且当需要操作的对象类型多起来时,也并没有那么方便。

于是有了这么一个思路,将C++对象的指针转为void 通过函数返回值传递到lua脚本中,当需要对这个对象进行一些操作的时候,在操作函数的参数中将void的指针传递回来,然后使用强制类型转换,获得对象指针。

Lua如何接收指针类型

一开始的想法是将指针地址转换为std::string,这样就可以在lua中直接保存为string类型,然后传递时再将其转换回来。

然后了解了一下Lua的基本数据类型,发现有提供LightUserData这么一种类型用于存储c风格的指针,于是豁然开朗。

userdata

userdata类型分为两种:

  1. 轻量级的userdata被称为light userdata,被用于存储一个c风格的指针,对于这个类型lua虚拟机不会自动垃圾回收,需要用户自己实现内存释放。
  2. 完整的userdata内存由lua虚拟机管理,也就是说会被自动回收。

要实现前述功能,明显使用light userdata会更合理一一些。

然后是关于light userdata压栈跟出栈的函数:

void lua_pushlightuserdata (lua_State *L, void *p);
void *lua_touserdata (lua_State *L, int idx);

参考资料: lua虚拟栈详解 lua与C++互相调用

示例

.h

class LuaLightUserData{

public:
    LuaLightUserData();
    ~LuaLightUserData();
    void doSomeThing();
};

.cpp

int creatLuaLightUserData(lua_State* l){
    void* p = new LuaLightUserData();
    lua_pushlightuserdata(l,p);
    return 1;
}

int deleteLuaLightUserData(lua_State* l){
    void* p = lua_touserdata(l,1);
    auto userData = (LuaLightUserData*)p;
    if(!userData)
    {
        std::cerr << "pointer type is not LuaLightUserData !" << std::endl;
        return 0;
    }
    delete userData;
    return 0;
}
int doSomeThing(lua_State *l){
    void* p = lua_touserdata(l,1);
    auto userData = (LuaLightUserData*)p;
    if(!userData)
    {
        std::cerr << "pointer type is not LuaLightUserData !" << std::endl;
        return 0;
    }
    userData->doSomeThing();
    return 0;
}

LuaLightUserData::LuaLightUserData()
{
    std::cerr << "creat LuaLightUserData" << std::endl;
}

LuaLightUserData::~LuaLightUserData()
{
    std::cerr << "delete LuaLightUserData" << std::endl;
}

void LuaLightUserData::doSomeThing()
{
    std::cerr << "you can do some thing!" << std::endl;
}

main.cpp

    /* 初始化 Lua */
    auto L = luaL_newstate();

    /* 载入Lua基本库 */
    luaL_openlibs(L);

    /* 注册c函数 */
    lua_register(L,"creatLuaLightUserData",creatLuaLightUserData);
    lua_register(L,"deleteLuaLightUserData",deleteLuaLightUserData);
    lua_register(L,"doSomeThing",doSomeThing);

    /* 运行脚本 */
    std::string stdStr = "userData = creatLuaLightUserData();\
                            doSomeThing(userData);\
                            deleteLuaLightUserData(userData);";
    luaL_dostring(L,stdStr.c_str());

运行输出:

image-20220421143328318

注意: C++ delete 一个void* 指针时似乎并不能调用它的动态类型的析构函数,我尝试将析构函数声明为virtual 也是不行的。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论