前言
在项目中使用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 也是不行的。
文章评论