C++tolua++实现lua层调用c++技术分析

tolua++技术分析 cocos2dx+lua

前言

直接都应用 cocos2dx + lua 进行耍支付,用 Lua
开发可以小心于游戏逻辑的实现,另外一方面可以实现热更新;而且 lua
是一个轻量级的脚本语言,库小而功能齐全,所以在正式非常给欢迎。之前看了网上广大关于
c/c++ 如何跟 lua 互调的教授,也查看了 lua 官网的 lua api 和 c
api,感觉很出获取。最近随即一段时间研究了 tolua++ 里面 lua 层调用 c/c++
的技巧实现,准备记录转学习心得,这样可以叫好对 tolua++
工作机制理解的愈来愈通畅,也期望团结对公理及 api
的解析会对其他人产生协助!


tolua++需要用 c/c++ 中之项目,变量,函数,对象导出到lua

  1. 通过 tolua_reg_types(lua_State* tolua_S)
    将项目导出,作用是啊每一个消导出到 lua 中的 c++ 类型创建元表,比如
    CCNode 这种类型,就会以注册表中开创一个元表 CCNode_mt。( 之后会用
    _R 代表注册表 , _G 表示全局表 , type_mt 代表色也type的元表。 )

  2. 通过 tolua_cclass (lua_State* L, const char* lname, const char*
    name, const char* base, lua_CFunction col)
    把基类设为子类的元表;同时在 _R.tolua_super
    中为子类的元表为键,创建同摆放表作为价值,而立张表会以基类,基类的基类(递归下去)为键,true/false
    为值 ; 还会受基类和子类共同拥有同摆tolua_ubox表(以c++指针为键 ,
    fulluserdata也价值)。 最后让 _G 持有 name_mt,即:_G.lname =
    name_mt。所以对于一个 c++ 类型,tolua++
    为那创立的元表最终会于全局表及注册表共同享有。

  3. 通过 tolua_function (lua_State* L, const char* name,
    lua_CFunction func) 将成员方法导出到 步骤1
    创办的元表中;即:_G.type_mt.name = func

  4. 通过 tolua_variable(lua_State* L, const char* name,
    lua_CFunction get, lua_CFunction set)
    在c++类型对象的元表中准备简单摆表_G.type_mt.get = {} ,
    _G.type_mt.set = {} ; 两摆放表以变量名也键,get/set方法呢价值。

地方c++数据的导出步骤就是是 tolua_Cocos2d_open(lua_State* tolua_S) 的实现,在CCLuaStack 初始化的时段做到。

tolua_Cocos2d_open (lua_State* tolua_S)内容分析

1: tolua_open(tolua_S) 创建同雨后春笋全局的table

  • _R.tolua_opened = true

  • _R.tolua_value_root = { }

  • _R.tolua_ubox = { }

  • setmetatable(_R.tolua_ubox,{__mode = v })

  • _R.tolua_super = { }

  • _R.tolua_gc = { }

  • _R.tolua_gc_event = class_gc_event(_R.tolua_gc ,
    _R.tolua_super)

  • _R.tolua_commonclass = { }

  • _G.tolua = { type = tolua_bnd_type , takeownership =
    tolua_bnd_takeownership , releaseownership =
    tolua_bnd_releaseownership , cast = tolua_bnd_cast ,isnull =
    tolua_bnd_isnulluserdata , inherit = tolua_bnd_inherit }

  • 如果lua版本是5.1: _G.tolua.setpeer = tolua_bnd_setpeer ;
    _G.tolua.getpeer = tolua_bnd_getpeer ;

2: tolua_reg_types (lua_State* tolua_S) 为用导出到lua中之c++类型注册元表

  • 针对各级一个比方导出的c++类型调用tolua_usertype就元表的挂号。跟进tolua_usertype可以发现她做了少数码业务。

    Step1: 调用 tolua_newmetatable
    创建元表,并且吃元表设置同一多元之老大计。
    Step2: 调用 mapsuper( L , derived_type , base_type )
    在tolua_super表中以
    derived_mt(子类型的元表)作为一个字段建立平等摆映射表 t ,
    这个t以父类,父类的父类(递归下去)的元表为键,布尔变量为价值。
    用伪代码可以表示也 tolua_super.derived_mt = { base_type = true
    , base_type_B = true , base_type_C = true}
    ,这样可就得判断两单近乎的接轨关系。

3 tolua_cclass( L , lname , name , base ,col ) 实现类之间的涉,让子类能够连续父类

  • mapinheritance(L,derived,base)
    将base设为derived的元表,这样即便好认为derived派生于base.

    1: 内部会调用 set_ubox(L)
    实现基类与选派生类共享同一客tolua_ubox表
    2: 然后拿基类设置为子类的metatable,如果基类为nil , 就以
    _R.tolua_commonclass 设置为子类的元表。

  • mapsuper(L,derived,base)这个办法在地方提到了,就是以
    _R.tolua_super
    表中开创一个索引表可以据此来判定两独八九不离十中是否发生连续关系。

    tolua_super.derived_mt = { base_type = true , base_type_B =
    true , base_type_C = true}

  • push_collector(L,type,col)
    这个地方的col是单lua_CFunction类型;type_mt.collector = col
    将col函数设为type的元表collector字段所对应之首家计。c++
    对象释放的时刻会如碰这个函数。

  • _G.lname = name_mt
    最后是手续就是是管之前c++类型在注册表中创造的元表让全局表也富有一份。

4 接下去就是是c++类中之主意以及分子变量的导出,就因为导出 ccColor3B类吃之成员方法与变量 为例子

  • tolua_cclass(tolua_S,”ccColor3B”,”ccColor3B”,””,tolua_collect_ccColor3B)
    先将类导出至_G中,并且安装好累关系.

  • tolua_beginmodule(tolua_S,”ccColor3B”); 将 _G.ccColor3B
    压入栈中(此时lua栈负1位置是ccColor3B_mt,负2位置是_G).

  • tolua_function(tolua_S,”new”,tolua_Cocos2d_ccColor3B_new00);
    将方法导出到c++类型所对应之元表中不怕:
    _G.ccColor3B.new = tolua_Cocos2d_ccColor3B_new00

  • tolua_function(tolua_S,”new_local”,tolua_Cocos2d_ccColor3B_new00_local);
    _G.ccColor3B.new_local = tolua_Cocos2d_ccColor3B_new00_local
    同理下面的同样多元tolua_function都见面拿c++方法注册到相应项目的元表中。

  • tolua_variable(tolua_S,”r”,tolua_get_ccColor3B_unsigned_r,tolua_set_ccColor3B_unsigned_r)
    tolua_variable(tolua_S,”g”,tolua_get_ccColor3B_unsigned_g,tolua_set_ccColor3B_unsigned_g);
    tolua_variable(tolua_S,”b”,tolua_get_ccColor3B_unsigned_b,tolua_set_ccColor3B_unsigned_b);

tolua_variable
的逻辑是也各级一个门类创建同布置get表,一张set表,然后以变量对应的 get / set
方法放到 get表 /
set表中;这样于lua层访问成员变量最终就会招来引至对应之存取方法。
即:_G.ccColor3B.get = { “r” = tolua_get_ccColor3B_unsigned_r , “g”
= tolua_get_ccColor3B_unsigned_g , “b” =
tolua_get_ccColor3B_unsigned_b }

_G.ccColor3B.set = { “r” = tolua_set_ccColor3B_unsigned_r , “g” =
tolua_set_ccColor3B_unsigned_g , “b” =
tolua_set_ccColor3B_unsigned_b }

tolua++ 胶水函数分析 , 还是因为ccColor3B为例

1. tolua_Cocos2d_ccColor3B_new00(lua_State* tolua_S) 将C++对象的指针以full userdata 的款式传播到 lua 层

  • ccColor3B* tolua_ret = (ccColor3B*)
    Mtolua_new((ccColor3B)())先创造一个c++对象,获取到指针。
  • tolua_pushusertype(tolua_S,(void*)tolua_ret,”ccColor3B”) 函数在
    c++ 层创建对象,然后创建full userdata压入栈中,函数最后见面管 userdata
    地址返回到lua层,lua 要惦记操作c++对象就得操作 fulluserdata,
    又因为fulluserdata 的元表是 c++对象在lua中之元表,所以最后 lua
    就是通过操作c++类型对应之元表来决定c++对象。这也是眼前一多元步骤的意义。

    压入c++对象时,以 light userdata 为键,full userdata
    为价值将这无异针对key-value存入tolua_ubox中。 即:
    ccColor3B_mt.tolua_ubox.tolua_ret = userdata
    setmetatable(userdata,ccColor3B_mt)

2. tolua_Cocos2d_ccColor3B_new00_local 和前者相比多矣一个tolua_register_gc方法,其他的还一致

  • tolua_register_gc :
    以c++指针为键,c++类型对应的元表为价值,将即刻对准key-value放于_R.tolua_gc中。

3. tolua_get_ccColor3B_unsigned_r(lua_State* tolua_S) 获取ccColor3B类中的r值

  • 第一通过 tolua_tousertype 从栈顶拿到userdata中的指针ptr, 把 ptr
    转型也(ccColor3B*)
  • 然后将 (lua_Number)ptr->r 压入栈中,最后回到给lua层。

总结

  1. tolua++ 为要导出到lua中之 c++类型 创建元表,这个元表由 注册表 和
    全局表共同持有,同时以元表中登记了相同文山会海初计。

  2. tolua++
    将父类型设为子类型的元表;父子类共享有同一卖tolua_ubox;同时在tolua_super中吗c++类型准备了一致摆放路映射表,可以由此该表来查询自己有怎么样父类。
    这样就是足以以lua层实现类似的连续。

  3. 经调用 tolua_register_gc
    方法,以c++类型的指针为键,c++类型对应的元表为值
    作为key-value插入到_R.tolua_gc中 来保管创建c++对象的内存。

  4. tolua++ 对 c++ 对象内存的管制,以及
    c++对象在lua层的扩展准备放下一篇文章更写!