Cocos2d-X与游戏开发cocos2d-xcocos2d-Lua

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

2017-11-09  本文已影响71人  芒果有点甜

标签(空格分隔): 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

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

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 ) 实现类之间的关联,让子类能够继承父类

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

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

4 接下来就是c++类中的方法和成员变量的导出,就以导出 ccColor3B类中的成员方法和变量 为例子

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 层

压入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方法,其他的都一样

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

总结

  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层的扩展准备放下一篇文章再写!

上一篇下一篇

猜你喜欢

热点阅读