Lua调用C++类要点

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.注册一个keyT::classNamemetatable,并制定其中的一些成员,用于之后生成的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); newtbClass下的一个函数(另外指定的,不会掉到thunk,这一句会调用到C的一个函数,里面会生成一个C对象,然后创建一个userdata 用于关联到这个新生成的C对象。最后为这个userdata绑定上我们上面注册为T::classnamemetatable。因为定制了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)的时候,buserdata,它的metatable__indextbClass绑定(4),所以会调用到tbClass的相应成员,就是之前关联的thunk:这个时候L的堆栈里面有这个函数的两个参数,一个是b本身,一个是50.30buserdata,可以根据它取出对象的指针。见第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;

     }

你可能感兴趣的:(Lua调用C++类要点)