在cocos2dx/tools/tolua++下面,有大量pkg文件,这些是按tolua++要求格式写好的、需要导出到lua中的c++类描述文件。

每当在c++类里增加了新函数需要导出时,应同步修改相应的pkg文件,然后运行此目录下的build.sh,就会重新生成cocos2dx/script/lua/cocos2dx_support/LuaCocos2d.cpp,里面就包含了对新增函数的封装代码。

build.sh的内容如下:

${TOLUA} -L basic.lua -o ../../scripting/lua/cocos2dx_support/LuaCocos2d.cpp Cocos2d.pkg

在运行标准的tolua++之前,还加载了额外的脚本basic.lua,这是由于cocos2dx在lua绑定方面并未完全遵照tolua++的默认做法,因此需要对其进行定制,basic.lua主要做的事情是:

1、将所有导出的CCXXXX类的push函数(也就是将cpp obj传进lua时调用的函数)修改为自己的toluafix_pushusertype_ccobject

2、将function和table两种类型的to和is函数(分别是指将lua obj传进cpp时调的函数、判断一个变量是否为本类型时调的函数)修改为toluafix_is/to_funtion/table

以上两条是tolua++本身提供的用户类型定制方法,也就是通过定义tolua++认别的3个特殊变量_to/push/is_function[classname] = XXX来实现。

至于为什么要做这些定制到后面分析tolua++的绑定实现时再详细说明。

下面还有一些通过强制替换输出的cpp胶水代码来实现的针对某一个类型的特殊定制:

3、将

ccColor3B color = *((ccColor3B*) tolua_tousertype(tolua_S,4,(void*)&(const ccColor3B)ccBLACK));

改为

const ccColor3B clr = ccBLACK;
ccColor3B color = *((ccColor3B*) tolua_tousertype(tolua_S,4,(void*)&clr));

这里的原因是某些函数声明里,给ccColor3B类的参数带了默认值ccBLACK,因此tolua++会转出前者代码,而ccBLACK实际是个const定义,无法取地址,因此强制替换成后者。这个问题其实很典型,在我以前自己做lua绑定库时也遇到过,即c++的默认参数只是个语法糖,在实际生成汇编指令时,所有参数都是要齐备的,而调用方未提供的实参,自然是由编译器帮助补上了(编译期),因此在导出到lua里被调用时,早已无默认参数的概念(运行期),要么lua代码必须提供所有参数,要么就是胶水层代码提供。我当时的做法是胶水层没有管这个事情,也就是写lua代码时根本不要想默认参数这回事。而cocos2dx这里的做法则是在胶水层搞定,给lua代码提供了便利。

4、将

tolua_usertype(tolua_S,"LUA_FUNCTION");

删除,将作为参数的

*((LUA_FUNCTION*)

也删除,也就是普通的lua function不做为usertype使用(注册、取参)。照理说lua自己的基本类型本就不该做为usertype,为什么tolua++会生成这样的本不该有的代码呢?出现这种情况的原因是,cocos2dx里有一些接受某个lua回调函数作为参数的函数,如

void registerScriptObserver(CCObject *target,int handler,const char* name);

这种函数在c++代码里,使用int作为回调函数参数的类型是很自然的,因为cocos2dx除了lua还要支持js等其它脚本语言,不可能直接使用某一个语言特有的函数类型来表示此参数,因此将其抽象为一个int型的handler。(虽然实际上lua本身也恰好是用int来表示函数引用的)

但是在pkg文件里,以上声明被改写为:

void registerScriptObserver(CCObject *target,LUA_FUNCTION funcID,const char* name);

这里int换成了LUA_FUNCTION。因为如果不换,那么tolua++生成的胶水代码就不知道这里要提取一个function,而是直接生成提取一个int变量的错误代码了。为了让tolua++生成正确的代码,需要hook它生成此处代码的逻辑,所以这里实际上包含两步,一是首先通过将int修改为LUA_FUNCTION使tolua++意识到这里有一个特殊类型(即不是基础类型)的参数,二是通过上述第2条所做的事让tolua++使用cocos2dx针对此类型提供的专有存取函数。如果不做第一步,直接给int提供push/to/is函数,当然也可以达到hook插入自有代码的目的,但是所有使用int做参数的地方(而非仅是用int表示脚本回调函数处)就全受影响了,因此LUA_FUNCTION在这里就是起到一个标识回调函数——hook只限于此——用途的作用。

可以在basic.lua里将此处注释掉(包括第2条)来检查生成代码的差异:

@@ -13018,7 +13018,7 @@ static int tolua_Cocos2d_CCNotificationCenter_registerScript
{
CCNotificationCenter* self = (CCNotificationCenter*) tolua_tousertype(tolua_S,1,
CCObject* target = ((CCObject*) tolua_tousertype(tolua_S,2,0));
- LUA_FUNCTION funcID = ( toluafix_ref_function(tolua_S,3,0));
+ LUA_FUNCTION funcID = *((LUA_FUNCTION*) tolua_tousertype(tolua_S,3,0));
const char* name = ((const char*) tolua_tostring(tolua_S,4,0));

-对应的是正确的代码,调用专门提供的toluafix_ref_function来取得一个lua function的ref,其返回值类型定义正是pkg中形参类型,也恰好是lua本身的一个typedef,语法完全正确。

+对应的是注释之后生成的错误的代码,对于lua function型参数,用普通的tolua_tousertype去提取,完全对不上型号。

5、将

toluafix_pushusertype_ccobject(tolua_S,(void*)tolua_ret

替换为

int nID = (tolua_ret) ? (int)tolua_ret->m_uID : -1;
int* pLuaID = (tolua_ret) ? &tolua_ret->m_nLuaID : NULL;
toluafix_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret

这也是无奈之举,第1条虽然用_push_function修改了usertype的push函数名,但传参列表却没有改,为了结合cocos2dx自己的CCObject::nID/LuaID机制,这里只好强行替换函数调用语句,加上提取出这两个参数。具体这两个ID的用处也待后面说明。

除了LuaCocos2d.cpp,同目录下还有几个胶水文件:

LuaCocoStudio.cp: 与上述一样,用大量pkg和一个定制lua脚本通过tolua++自动生成

下面这些看起来似乎都是手写的了,各有各的特殊逻辑要处理(都不是tolua++常规途径能解决的),就先不一一细说了。

lua_cocos2dx_manual.cpp:

lua_cocos2dx_cocostudio_manual.cpp:

lua_web_socket.cpp:

lua_extensions_CCB.cpp:

CCBProxy.cpp:

关于tolua++自身的编译:有个特别一点的地方,即它先编出一个tolua++_boostrap,用途是将src/bin/lua下的一堆lua文件转成二进制字节数组的c文件(toluabind.c),然后再加进这个c文件生成最后的tolua++,好处是发布最终程序时,就是一个裸的可执行程序,不需要再携带一堆lua脚本了(通常容易带来各种定位麻烦)。至于那一堆lua文件,除了package.lua是用来做lua文件转字节数组外,大部份是用来做pkg文件解析的,也就相当于一个微型类c++头文件解析器了,这一点使我觉得tolua++很蛋疼,因为c++语法解析(即使只是弱化版的头文件)本来就很复杂,非要自己做,还写那么大一堆冗长晦涩的lua代码,不是原作者根本没法看懂,想对pkg格式做点修改扩展什么的几乎下不了手,只能把它当一个将就能用的东西用了,所以也才出现了上面cocos2dx对它各种修改替换的结果。

cocos2dx之tolua++全面分析(一):tolua++工具本身的更多相关文章

  1. cocos2dx之tolua++全面分析(二):类注册

    tolua被作为库使用时,首先会做大量内部初始化工作: 一.tolua_open是入口点,它创建很多用于管理的内部变量,以下用_G指代全局表,_R指定registry table: 1._R.TOLU ...

  2. mysql性能瓶颈分析、性能指标、指标搜集方法与性能分析调优工具

    本文主要讲解mysql的性能瓶颈分析.性能指标.性能指标信息的搜集工具与方法.分析调优工具的使用. 文章尚未完成. 性能瓶颈: 慢.写速度比读速度慢很多  主要的性能指标: 访问频度, 并发连接量, ...

  3. linux下源代码分析和阅读工具比较

    Windows下的源码阅读工具Souce Insight凭借着其易用性和多种编程语言的支持,无疑是这个领域的“带头大哥”.Linux/UNIX环境下呢?似乎仍然是处于百花齐放,各有千秋的春秋战国时代, ...

  4. linux命令 host-常用的分析域名查询工具

    博主推荐:更多网络测试相关命令关注 网络测试  收藏linux命令大全 host命令是常用的分析域名查询工具,可以用来测试域名系统工作是否正常. 语法 host(选项)(参数) 选项 -a:显示详细的 ...

  5. Qt qml调试,qml性能分析和优化工具

    QML语言为qt推出的用于界面编程的语言. 1)如何在qt creator中进行调试qml: 以Qt Creator 4.6.2为例: 在qt creator的debug模式下,可以直接在qml中打断 ...

  6. tolua++实现分析

    项目正在使用cocos2dx的lua绑定,绑定的方式是tolua++.对大规模使用lua代码信心不是很足,花了一些时间阅读tolua++的代码,希望对绑定实现的了解,有助于项目对lua代码的把控.从阅 ...

  7. cocos2d-x 纹理源码分析

    转自:http://blog.csdn.net/honghaier/article/details/8068895 当一张图片被加载到内存后,它是以纹理的形式存在的.纹理是什么东西呢?纹理就是一块内存 ...

  8. Android APP性能分析方法及工具

    近期读到<Speed up your app>一文.这是一篇关于Android APP性能分析.优化的文章.在这篇文章中,作者介绍他的APP分析优化规则.使用的工具和方法.我觉得值得大家借 ...

  9. 【Android端 APP 内存分析】使用工具进行APP的内存分析

    Android端可以通过adb 命令直接获取内存信息,当然Android studio也提供了对内存的监控分析工具,并且后续可以结合MAT做分析 今天介绍的是通过Android studio和MAT工 ...

随机推荐

  1. python用特殊方法定制类(不全)

    定义在class中不需要直接调用,python的某些函数或操作符会自动的调用对应的特殊方法. 1.python中 __str__和__repr__ __str__()用于显示给用户,而__repr__ ...

  2. 【Flask】模板继承

    # 模版继承笔记: ### 为什么需要模版继承:模版继承可以把一些公用的代码单独抽取出来放到一个父模板中.以后子模板直接继承就可以使用了.这样可以重复性的代码,并且以后修改起来也比较方便. ### 模 ...

  3. LINQ 学习路程 -- 查询操作 Aggregate

    聚合操作执行数学的运算,如平均数.合计.总数.最大值.最小值 Method Description Aggregate 在集合上执行自定义聚集操作 Average 求平均数 Count 求集合的总数 ...

  4. bzoj 3038: 上帝造题的七分钟2 线段树||hdu 4027

    3038: 上帝造题的七分钟2 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1066  Solved: 476[Submit][Status][Dis ...

  5. 在eclipse中引入jquery.js文件报错的解决方案

    从官方下载的jquery.js在myeclipse始终用个大大的红叉,看着很不爽,如何解决呢:jquery.js在myeclipse中报错:jquery.js -> 鼠标右键 -> MyE ...

  6. 机器学习(十五)— Apriori算法、FP Growth算法

    1.Apriori算法 Apriori算法是常用的用于挖掘出数据关联规则的算法,它用来找出数据值中频繁出现的数据集合,找出这些集合的模式有助于我们做一些决策. Apriori算法采用了迭代的方法,先搜 ...

  7. Javascript-- jQuery事件篇(1)

    jQuery鼠标事件之click与dbclick事件 用交互操作中,最简单直接的操作就是点击操作.jQuery提供了两个方法一个是click方法用于监听用户单击操作,另一个方法是dbclick方法用于 ...

  8. os.path

  9. 08 - Django应用第五步

    1 自动测试 自动测试与测试的不同在于, 自动测试的测试工作是交给系统完成的 测试也有分类和级别, 有的用于一些细微的细节, 有的是针对整个软件整体 测试会保证一些看起来正常运行的功能在实际的多种情况 ...

  10. torch7 安装中Missing dependencies for nn:moses >= 1错误解决办法

    Torch7.0安装步骤(默认安装路径是在home下): git clone https://github.com/torch/distro.git ~/torch --recursive cd ~/ ...