好久没有写文章了,前些天在我的资源里面上传了一个有关lua和c++/c交互(一下文章中都用c++代替)问题的文档,小伙伴们说有点难懂,这里我在详细描述一下吧!
看文章之前小伙伴要弄清楚table,metaTable概念。说起metaTable,我忍不住想多说几句了。
既然有了table,为什么还要metaTable呢?假设现在你有一个yourtable,yourtable里面有一个fun函数.本身应该这样调用yourtable.funName(point,1,0),但被别人恶意写成了youtable.funName(io.stdion,1,0) 造成 memory corruption .这时你会想到在c语言中的funName检测指针有效性,那你怎么检测指针是有效的呢?所有才想到把指针和某个变量名一一对应,凡是对指针的调用都通过变量名来检测变量是否存在,聪明的你会想到metaTable存在的价值了吧。说白了,metatable就是一个在系统中注册了一个名字的的特殊table
metaTable,userdata,object point他们之间有什么关系呢?
void * luaL_checkudata(lua_State *L, int index, const char* name):
this function checks whether the object at the given stack position is a userdata with a metatable that matches the given name。其实lua传到c里面的对象指针都可以看做是一个userdata指针,userdata本身就是一个特殊的table,说他特殊是因为具有__index,__gc这两个key值,但不能创建别的key。userdata还有一个特殊的key,这个key可以指向metable。我可能理解的不对,欢迎指正。
如何在C中申请一个metatable或者table,在lua中直接使用?
struct luaL_reg arraylib_reg_m[]=
{
{"getName", getNameInc},
{NULL,NULL},
}
luaL_newmetatable(pLua,"selfTable");
lua_pushstring(pLua,"__index");
lua_pushvalue(pLua,-2);
lua_setTable(pLua,-3);//此时selfTableObject.__index = luaL_getmetatable(pLua,"selfTable");操作之后,栈中只有先前创建的table指针
luaL_openlib(pLua,NULL,arraylib_reg,0);
此时在lua中就可以pLua:getName(roleId);记得在c中把getNameInc函数实现
看文章的始终,都要明白c++和lua交互是通过堆栈协议实现的。堆栈协议是什么呢,不要急,下文会有答案的。
先来了解几个lua api的用法:
lua_getglobal(lua_State *L, string funNameInLua);//调用lua函数之前把要调用的名字放到栈中
lua_push*(lua_State *L, void);//在栈中放入调用参数
lua_pcall(lua_State *L, int param_num,int return_para_num,0)://在c中调用lua
void *lua_newuserdata(lua_State *L, size_t size): //申请一块内存,并且把内存指针放到栈顶
再熬述一个知识点,在lua api中的两个关键字:
__index:当table中找不到某个成员时,会把这个键对应的值当做metatable;例如pUser:getName(roleId)[实际上是pUser.getName(pUser,roleId)的缩写],由于pUser是userdata,上面说了
userdata是一个特殊的table,table操作不存在的key时会访问index,userdata本身就没有键,所以直接访问__index对应的值,__index对应的值又是一个在c中注册了的table,则操作变成selfTableObject.getName(pUser,roleId).selfTableObject上面是一个注册了的table,所以就正常访问到了c中的函数
__gc:垃圾回收元函数,当lua回收table是就会调用它
到此,应该你就了解了lua调用c的全部机制以及详细过程。下文可看可不看。
再纠正一个观点,在lua中调用c中的对象函数,如果不了解的lua api机制或者看过曾经某个大神的代码pUser:GetName()/table.GetName(pUser)//在lua中获取c++中演员对象pUser的name。你肯定会想我是不是只要和c++中使用同一个指针值,在C++中定义一个CUser类,就直接能实现调用操作了?其实不是的,首先lua中不可能知道pUser指针指向的是哪个类,它只知道它指向的是一块未知的内存。怎么实现调用操作呢,这时候metable上场了。不多说,看下面的C++代码:
看metable相关的函数一个:
static int newarray (lua_State *L) {
CUser* pUser = (pUser*)lua_newuserdata(L, sizeof(CUser));
luaL_getmetatable(L, "CUser.pointTable"); //从系统中取得先前已经创建的名字为 CUser.pointTable的table,对象
lua_setmetatable(L, -2);把CUser.pointTable作为pUser的metaTable
return 1; /* new userdatum is already on the stack */
}
lua_State *pLua = lua_Open();
luaL_openlibs(pLua);
luaL_newmetable(pLua, "CUser");
lua_pushstring(pLua, "__gc");
lua_pushfunction(pLua, DestoryCuser);
lua_settable(pLua,-3);//__gc=DestoryCuser,之后会把key和value弹出来
lua_pushstring(pLua,"__index");
lua_pushvalue(pLua,-2);
lua_settable(pLua,-3);//__index=CUser,之后会把栈清空
//下面重点来了,在CUser table中建立了"getName",getNameInC的映射转化
//用struct luaL_reg数组实现更简单,直接luaL_openlib(pLue,"selfTable",regFun,0);
lua_pushstring(pLua,"getName");
lua_pushcfunction(pLua,getNameInC);//在getNameInC中实现通过pUser指针对自身函数getName的调用
lua_settable(pLua,-3);
lua_pop(L,1);
lua_file(pLua,"test.lua")
好了,就说这么多了。如果还有不明白的,那你直接联系我好了,我竭尽全力的吧!
最后
以上就是优秀学姐最近收集整理的关于lua和c++/c交互的全部内容,更多相关lua和c++/c交互内容请搜索靠谱客的其他文章。
发表评论 取消回复