在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. Data Structure Trie: suffix problem

    http://www.geeksforgeeks.org/suffix-array-set-1-introduction/ http://www.geeksforgeeks.org/pattern-s ...

  2. AC自动机的一点理解

    \(fail\)指针:指向最长的在\(tire\)里出现的后缀 比\(tire\)多出来的子边:原来的\(tire\),我们失配后又得返回根结点再次匹配,而加入这些边后只需要花\(strlen(s)\ ...

  3. Android蓝牙串口通讯【转】

    本文转载自:http://blog.sina.com.cn/s/blog_631e3f2601012ixi.html Android蓝牙串口通讯 闲着无聊玩起了Android蓝牙模块与单片机蓝牙模块的 ...

  4. POJ 1068 Parencodings (类似括号的处理问题)

                                                                                                    Pare ...

  5. 算法(Algorithms)第4版 练习 1.5.5

    对于quick-find,对每个输入数据对,其最少的循环次数为N(sites) 故对于109 sites和106 input pairs,其总的指令次数为:sum = 10^9 * 10^6 * 10 ...

  6. Nexus4_换电池

    1.参考帖子:http://tieba.baidu.com/p/2444904362 ([图片]直播nexus4拆机换电池,勿插_nexus4吧_百度贴吧.html) 主要的内容是: (1).(5楼) ...

  7. 解析XML(2)

    在输入法非中文状态下使用ctrl+shift+f可以使文档换行.

  8. jenkins-小知识点

    如果想停止jenkins运行 控制面板-服务-查看本地服务-选中jenkins 1.启动类型改为手动 2.改为禁止 使用的时候,每次都改一下状态

  9. Idea_学习_01_Idea激活

    一.激活—激活码 下载地址:官网 1.注册码 http://idea.lanyus.com/ 二.激活—license server 1. (推荐) 弹窗中选择最后一个页面license server ...

  10. BEC listen and translation exercise 45

    So the Counselling Services we offer deal with any problems arising from your studies or in your lif ...