前言
在项目中使用C++与Lua互相调用,有的时候需要在lua脚本中,对C++的对象进行一些操作。
最开始的做法时,将这些对象声明为单例,或者是将对象放入单例之中,总之就是把对像放在一个全局可获取的地方,然后调用lua调用时获取对象再进行相应的操作。
不过这样的做法十分不优雅,并且当需要操作的对象类型多起来时,也并没有那么方便。
于是有了这么一个思路,将C++对象的指针转为void 通过函数返回值传递到lua脚本中,当需要对这个对象进行一些操作的时候,在操作函数的参数中将void的指针传递回来,然后使用强制类型转换,获得对象指针。
Lua如何接收指针类型
一开始的想法是将指针地址转换为std::string,这样就可以在lua中直接保存为string类型,然后传递时再将其转换回来。
然后了解了一下Lua的基本数据类型,发现有提供LightUserData这么一种类型用于存储c风格的指针,于是豁然开朗。
userdata
userdata类型分为两种:
- 轻量级的userdata被称为light userdata,被用于存储一个c风格的指针,对于这个类型lua虚拟机不会自动垃圾回收,需要用户自己实现内存释放。
- 完整的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());
运行输出:
注意: C++ delete 一个void* 指针时似乎并不能调用它的动态类型的析构函数,我尝试将析构函数声明为virtual 也是不行的。
文章评论