Lua相关学习参考链接:点击打开链接

 

Lua调用C++类要点:

1.       为此类建立一个全局表,表名为类名tbClass;

lua_newtable(L);

intmethods = 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的个数,并指定回调函数统一为thunklua_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;

     }