Lua相关学习参考链接:点击打开链接
Lua调用C++类要点:
1. 为此类建立一个全局表,表名为类名tbClass;
lua_newtable(L);
int methods = lua_gettop(L);
lua_pushstring(L, T::className);
lua_pushvalue(L, methods);
lua_settable(L, LUA_GLOBALSINDEX);
2.注册一个key为T::className的metatable,并制定其中的一些成员,用于之后生成的userdata。
// 这个表用于userdata(T的对象)的metatable
luaL_newmetatable(L, T::className);
int metatable = lua_gettop(L);
// metatable["__index"] = tbClass
lua_pushliteral(L, "__index");
lua_pushvalue(L, methods);
lua_settable(L, metatable);
// metatable["__tostring"] = tostring_T
lua_pushliteral(L, "__tostring");
lua_pushcfunction(L, tostring_T);
lua_settable(L, metatable);
// metatable["__gc"] = gc_T
lua_pushliteral(L, "__gc");
lua_pushcfunction(L, gc_T);
lua_settable(L, metatable);
3. 为此表指定成员,每个成员的key为类的成员函数名,Value为一个带有闭包的统一函数。比如tbClass[FunName] = thunk,之后可以根据闭包得到具体是调用到哪个函数。闭包中有函数名和相应函数的组合结构(以lightuserdata的形式赋给闭包)。这些类成员函数参数都必须包括lua_State,因为它需要的参数都会在lua堆栈中。
// 为tbClass填充成员函数
for (RegType *l = T::methods; l->name; l++)
{
/* edited by Snaily: shouldn't it be const RegType *l ... ? */
lua_pushstring(L, l->name);
// 把(函数名,函数地址)pair以lightuserdata的形式作为C closure的upvalue入栈
lua_pushlightuserdata(L, (void*)l);
// 把一个新的C closure 压入堆栈。为upvalue的个数,并指定回调函数统一为thunk lua_pushcclosure(L, thunk, 1);
// tbClass[FunName] = Function
lua_settable(L, methods);
}
4.创建C对象给脚本使用b = Account.new(Account, 30); new是tbClass下的一个函数(另外指定的,不会掉到thunk,这一句会调用到C的一个函数,里面会生成一个C对象,然后创建一个userdata 用于关联到这个新生成的C对象。最后为这个userdata绑定上我们上面注册为T::classname的metatable。因为定制了metatable的__index成员,所以当userdata找不到的成员会去调用__index,因为之前我们把__index绑定到tbClass,所以也会调用到tbClass的相应成员。
// 创建一个新的T对象,并创建一个基于userdataType的userdata,其中保护了指向T对象的指针
static int new_T(lua_State *L)
{
lua_remove(L, 1); // use classname:new(), instead of classname.new()
T *obj = new T(L); // call constructor for T objects
userdataType *ud =
static_cast<userdataType*>(lua_newuserdata(L, sizeof(userdataType)));
ud->pT = obj; // store pointer to object in userdata
luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
lua_setmetatable(L, -2);
return 1; // userdata containing pointer to T object
}
5. 当脚本中指定函数被调用的时候,比如b:deposit(50.30)的时候,b是userdata,它的metatable的__index和tbClass绑定(见4),所以会调用到tbClass的相应成员,就是之前关联的thunk:这个时候L的堆栈里面有这个函数的两个参数,一个是b本身,一个是50.30。b是userdata,可以根据它取出对象的指针。见第4步。另外函数被调用的时候,它相关的upvalue也可以取得到,见步骤3。有了对象指针和相应的函数,调用也不为难了,记住参数50.30是保存在堆栈中传给类的成员函数来取得。
// 所有成员函数都会调用到这里,然后根据upvalue来执行具体的成员函数
static int thunk(lua_State *L)
{
// stack has userdata, followed by method args
T *obj = check(L, 1); // the object pointer from the table at index 0.
lua_remove(L, 1); // remove self so member function args start at index 1
// get member function from upvalue
RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
return (obj->*(l->mfunc))(L); // call member function
}
// 根据指定位置narg获得对象指针,这个userdata是在new_T的时候创建的
static T *check(lua_State *L, int narg)
{
void *pUserData = luaL_checkudata(L, narg, T::className);
userdataType *ud = static_cast<userdataType*>(pUserData); // 这个是函数的upvalue
if(!ud)
luaL_typerror(L, narg, T::className);
return ud->pT;
}