Hello
Hello
倚楼听风雨,淡看江湖路——未来的不可知,是我们前进的原动力! 花开,然后花落,星光闪耀,不知何时熄灭。这个地球,太阳,银河系,甚至整个宇宙也总会有消失的时候,人的生命和那些相比只不过是一瞬间吧,在那一瞬间中,人诞生,微笑,哭泣,战斗,伤害,喜悦,悲伤,憎恨谁,喜欢谁,所有的一切都是刹那间的邂逅,谁都不能逃脱死亡的长眠……
这里被天朝墙得太厉害,无奈呀,本博客永久性搬家到http://www.missdeer.com!
at
11:16
0
comments
Labels:
Water
今天终于把基于socket的远程调试架构搭建起来了!
昨天定位到controller给engine发送命令后,engine并没有收到,于是今天首先是开起两台机器,这样就可以用Wireshark抓包了。经过抓包发现,controller确实已经把命令发送过来了,只是engine没有正确接收。一番折腾后,参考了一下lldebug的代码,把engine端的代码从io_service.run()改成多次io_service.poll_one(),居然正常运行了。这怪我没有认真理解asio的用法,io_service居然有4个用于处理事件的方法,除前面那两个外,还有run_one()和poll(),从文档中我实在分不清到底它们有什么区别,以及分别适用于哪些使用场景。不过既然现在可以正常运行了,那我暂时也不去管它了。
socket的问题解决后,我就埋头于定位,修正controller与engine的交互过程。在我看来,这应该是状态机,controller端一个,engine端一个。不如我没有仔细考虑,只是因为现在的问题很简单,步骤不多,随便跑个例子试一下,有问题就解决,到现在基本上也是ok了。
编码过程中,发现boost::shared_ptr是个不错的东西,尤其适合于asio。比如要发送一段数据,把这段数据放在一个boost::shared_ptr中,无论它怎么传递,不用管它的生命周期。
自从远程调试架构完成以来,我就发现一个问题,从controller下发一个命令,engine要过一会儿才会作出响应,而engine反馈了信息回来,controller也要过一会儿才会显示出来。开始的时候我以为是网络传输延时的问题,后来把双机调试改成在同一台机器上调试,loopback应该够快了吧,结果仍然没什么变化。最后我想到,估计是我其中加入的延时操作引起的,如果在写之前发现队列为空,那么就一直等待,每隔1秒钟看一下队列中是否有可写的内容。所以要把这个设计改一下,不能用定时轮循,而要用事件通知,又得好好复习一遍《Windows核心编程》了。
接下来,就是实现各个调试命令了!
at
18:39
1 comments
Labels:
Lua,Script
估计这个Lua调试器是最近几个月来遇到的最具有技术挑战性的特性了,大概要分三步走。
首先,确定使用远程调试架构。用C++实现与用户交互的操作程序和与Lua解释器交互的执行部分。现在仍然在这个阶段。昨天差不多把socket通信的部分搭起来了,这也花了我不少精力,是用boost.asio做的,实然觉得自己对boost.asio的了解增进了很多,也突然觉得其实boost.asio似乎挺好用的。但是仍然有点小问题,比如我本来希望执行部分作为C/S结构中的客户端,它能在连接上服务端后立马写点数据给服务端,可是似乎总是没写出去,要先收到些东西再写才会成功。不过现在暂时不管这个问题,今天在写与Lua解释器交互的部分,发现代码真的是越来越多喽,一个又一个新的类被创建出来,真是面向对象惹的祸呀!没写完,中间脑袋稀里糊涂的,明天继续。
接着,把与用户交互的操作程序改写成Lua,并与宿主程序集成,可以在IDE的编辑器上就让用户进行操作和反映调试器执行结果。这里要注意的是,到底到时候要放多少代码是在宿主程序中实现的,多少代码又是让Lua脚本实现的,又有多少代码是用C++写成作为Lua的扩展库的,几部分又是如何交互的。
最后,把调试执行部分改成dll,以便注入到被调试进程中去,这样就可以调试任何嵌入了Lua解释器的程序了,只要Lua解释器是个dll即可。注入后,要hook掉几个Lua的C API,包括获取lua_State,装入文件。这里应该要注意的是,使用Lua解释器的C API不能直接调用和链接了,要动态获取API地址了,这样才能使用注入进程的那个Lua解释器的dll了,不过其实可能不需要这么复杂,只要额外说明如果要用本软件调试的话,要求用我指定的Lua解释器的dll和lib即可。我觉得这点是可以做到并且在某些情况下必须做到的,因为曾经还听人说过,很多内嵌Lua的游戏,都是静态链接Lua的,所以既使我能注入,也hook不到那些C API的。
at
19:52
0
comments
Labels:
Lua,Script
好运的部份:
2010年终於开始有苦尽甘来的感觉了,
木星将从1月中进入双鱼座的命宫。
你会觉得自己是属於你个人的,可以摆脱身不由己的感觉,
而且生命是全新的。
有许多的新资源会在今年奇迹式的展现,
帮助你实现你的梦想,让你的才能得到发挥,
感情也会遇到你生命中非常重要的那个人,请好好把握,
因为今年是你「对」的一年,
不要在今年闹情绪或任性,把这些幸运都丢掉了,
要记住,2010年只要是你愿意丢掉不幸的心情,迎接幸福,都可以心想事成,
除非是你自己愿意不幸。
厄运的部份:
经过土星行经下降位置的锻链,
双鱼座的人历经亲密关系的离开,
因此,当土星进入第8宫,双鱼座的人对於自己的欲望,
会变得有不敢奢望任何好事的情况存在,
有可能因此面对喜欢的人,自身的行动都是悲观和消极的。
以及由於土星与海王星都在双鱼座的健康宫位(8宫与12宫),
双鱼座会有3年身体上相当虚弱的问题存在,体力会显得比较差,
会常有一些疾病让你苦恼,
皮肤、骨头、牙齿会常常有一些疼痛,
睡眠依然会有常常做梦、无法熟睡的情况发生。
以及千万注意不要帮人作保、不要欠债与贷款,
这3年都容易背负庞大的债务,
以及有可能家中会有需要你长期照顾的长者。
这都会让双鱼座的健康运势下滑,
可以的话,双鱼座应该开始锻链你的身体了,
储存多一些的体力,预防健康上会发生的问题。
这些问题你现在(2009年11月)应该已经有所感觉了。
这种情况会持续3年,
请一定要好好注意养身,可以从事一些修养身心灵的运动。
工作运:
双鱼座的人今年会开始有机会实现自己的梦想,
只要是你想做的工作,想发展的志愿,都可以奇迹式的获得机会发展,
甚至是无心插柳的一个想法,都会获得很大的回响。
上半年的工作十分忙碌,可能还蛮多麻烦事的,
不过双鱼一定要把握今年的好运势,
把你过去想做却被束缚的想法全部付诸实现,
你将会在6、7月的时候发现,
你对工作上的努力会化成大量的金钱回馈给你。
是相当幸运的一年。
感情运:
2009年鱼的感情运历经了相当严苛的考验,
如果还能继续延续的感情,真的是得来不易的一段感情,请好好珍惜。
2010年双鱼座的桃花旺盛的狂开,
尤其是2、3月过生日的时候,还有金星加持,
并且没有不良的相位影响,感情可以说是心想事成,
比较伤脑筋的部份是,
今年双鱼座的人心情上会比较悲观,反而会让很多好机会溜走了,
4、5、6月的时候,土星会逆行回处女座,
是让双鱼决定一段感情的关键期。
到了下半年,一切都会变得稳定与明朗了,
如果是有喜欢的对象,
双鱼座的人要大方一些,不要搞暧昧,
今年会遇到对你生命中非常重要的那个人,千万不要错过了。
健康运:
双鱼座唯一比较糟糕的部份是健康运势不太好,
如果可以的话,
请现在赶紧找出治疗你身心灵的活动,
因为你即将有3年体能不佳的情况存在。
大小病痛都会不断,
就算是身体底子好的双鱼,
也会有睡眠障碍、精神不济的情况存在,
请好好注意身体健康、稳定心灵,
不要情绪化,保持心灵平静,对你的健康有益。
整体运:(5颗星)
今年是双鱼座非常重要的一年,
梦想与感情都会心想事成,
幸福决定权全在你自己手上,
你必须要清楚明白自己要什麽,不要什麽,
那些幸福才可以掌握,
如果你迷迷糊糊,对於自身的感情与梦想无法确定的话,
就会浪费2010年这麽好的能量了,
可以的话,现在就开始计画吧!
迎接属於你的2010年。
at
19:50
0
comments
Labels:
Water
2009过得很快,真的好快,感觉没做什么事,却又真的发生了些什么事。
不过我的记忆只到2009年的春节。在春节前,我仍然做着那看不到尽头的一体化平台项目,哦,后来有个正式的名字叫流程集成管理平台。春节后,经过各方的努力,我被调回自己项目组内做Ruby IDE,本以为这回是自己感兴趣又熟悉而富有想法的事了,可以大展拳脚了,可是现实老是跟理想偏差很远。一切都显得那样自以为是的混乱而不求上进。这样的状况一直持续到9月底辞职。
是的,辞职,我终于辞职了。记得清明放假回老家的时候,遇到一个看相的女人,说我今年工作会变动,当然她说的语气要难听一点,说是我工作丢掉了。当时我还是有点嗤之以鼻的,我是个比较向往真有神之类的存在的,但同时我却又是个比较坚定的无神论者,所以我觉得这种神棍的话,好听的可以听听当作鼓励,难听的就要过滤掉了。现在回想起来,我也不清楚到底当时是怎么下定决心辞职的,其实我很是怀念在深圳在华为的生活的。正是因为对在华为的生活方式的不舍,去年腾迅给我的offer我都拒绝了,当然还有其他各方面的原因。又想想,如果去年真的去了腾迅,我是不是会一直在那里干下去了呢。
辞职的时候,需要跟四级部门主管开始往下的各级领导沟通。所以能遇得到的最高级别的领导就是四级部门主管了,当然我果真见识到高级别的人眼光和视野确实是更锐利和明亮一些。给我印象比较深刻的是他当时问我“难道你就真的对自己以后在华为的发展这么没有信心么”,一句话说中了我辞职的最大的原因。是的,没信心,一点都没有,所以我只能离开,寻找自己的理想,追求自己的野望!
说实话,我一直没有很明确的思路,自己以后要怎么做的思路,我只有一个大致的目标和方向,甚至于这个目标和方向都时不时地产生模糊的变化。不过辞职后,我有了大把的自由支配的时间,我越来越觉得如果不辞职,根本不可能支撑起我想做这些事的欲望。但是,尽管每天的时间全都给了我自己,时间仍然是不充足的,我仍然要花大量的时间去堕落和颓废。辞职前,我曾经想着好好看一些书,好好学一些东西,结果到现在仍然没有进展。
2009已经过去,充满机会的2010已经开始。曾经数次列入新年规划的计划,每年都没有得到有效的执行,所以仍然要列入2010规划之中。我希望,2010是我事业上能够明显看到成功的一年,我需要组建一个小公司,招收一些人马来实现我的理想!
at
09:49
0
comments
Labels:
Be Old
今天在群里有个人问,他初始化Lua解释器后,用C代码执行一下lua_replace然后崩溃了,报没有环境。我觉得比较奇怪,看代码我觉得应该没有问题,但人家确确实实崩溃了。后来他解决了这个问题后,在群里公布解决方法。他看到一个老外的帖子,然后照他们说的,把调用lua_replace的那个C函数放到Lua中进行调用,一切都OK了!我仍然没明白其中的所以然,后来另一个人贴了个网址,是他自己写的一篇blog,其中解释了为什么在调用lua_replace时会崩溃。经过他的解释,是因为Lua的调用栈要求不为空,否则lua_replace就会崩溃,至于为什么要这样他也不明白。实际代码的解决办法就是在lua_replace前call一下Lua函数,比如前面说的,放在Lua中调用,那么理所当然调用栈中至少有本函数嘛。
我又看了几遍那篇blog,其中说到要嵌入的Lua解释器中要初始化标准库时,要用luaL_openlibs,如果用luaopen_base之类的直接调用就不行。这时我才渐渐地有点头绪。当年在嵌入Lua时,最早的时候用luaopen_base之类的是没问题的,后来确实不行了,在lua的maillist上有人告诉我要用luaL_openlibs,我还说应该不是这个原因,但他们说这是5.1.x之后才这么做的。现在回想起来,大概就是因为5.1.x修改了这部分的实现,而且看了一下luaL_openlibs的代码,确实就是把luaopen_base这些函数让给Lua来调用了,其它的没有区别了。
通过这件事,让我又一次深刻地认识到阅读分析Lua源代码的重要性,Lua本身的资料仍然太少,从源代码中可以得到很多undocumented的知识!
at
20:33
0
comments
Labels:
Lua,Script
本来以为自己对如何实现一个Lua调试器已经有了足够的知识储备,于是今天就提枪上马,结果无奈地发现,说是一回事,自己做是另一回事。我最终的目标是要达到Decoda那样的效果,但一开始只能慢慢一步一步来,先实现类似lldebug的debuggee部分。但是就算是这样小步开始,也仍然困难重重,很多问题其实我都没有吃透。
首先是发现在sethook时,只有按line回调是正常的,而call和return都没有回调,这让我感到很迷惑,无论是用C代码的hook还是Lua代码的hook,都是存在这个问题,而且用lldebug是正常的。所以我最早怀疑跟Lua代码有关的猜测也不成立了,只好试图从lldebug的源代码中寻找答案,当然寻找到的lldebug源代码跟我的代码没有本质的区别。最后过了很久才隐约记起来,我用的是LuaJIT,好像它对debug库的支持是有些跟官方Lua不同的地方。于是立马换回官方Lua,发现果然无论是C代码还是Lua代码都照预期的执行了。
之后是发现,hook的回调是能正常执行了,但程序还是自顾自地跑下去了,那怎么处理单步和断点呢。然后是回头看lldebug代码,发现是在hook回调函数中,处理完对应的事件后,应该要等待命令。一开始我还想不通,要怎么等待命令啊,后来突然明白过来了,比如我这个是控制台操作的,那么在这里接收一个控制台的输入就行了,不就变成运行一下,停一下了嘛!
我写了两个测试脚本,用于观察Lua的debug库三种hook回调的行为。发现Lua在执行一个脚本文件时,首先是将整个脚本文件看作一个整体,用Lua的话说,应该是一个chunk,而它执行这个chunk是个call操作,所以执行一个脚本文件会先有一个call回调,在整个文件执行完后,会有一个return回调。如果一个脚本文件中又执行了另一个脚本文件,那么就可以到嵌入call/return回调。当执行的代码是一个函数的定义时,会先在函数的end行有一个line回调,然后在函数的function行有一个line回调。当执行的代码是一次函数调用,那么会先在函数体(参数列表后面)的第一行有一次call回调,然后再在该行有一次line回调,在结束函数时,先在函数end行有一次line回调,再在该行有一次return回调。
再之后是开始考虑要支持哪些调试命令,以及怎么实现。简单地看了一下别的调试器的实现,我就暂定要支持step into,step over,run to cursor,breakpointp这几个操作了,step into大概是最容易实现的,就是debug库中按line回调即可。step over可能稍微麻烦一点,应该先假设是按line回调来处理,如果从该操作开始第一次回调的是call,那么应该记录下call的次数,并计数return回调的次数,直到return回调的次数跟call回调次数相同,再下一次line回调就达到step over的效果了。run to cursor也比较简单,按line回调直到当前光标所在行即可。breakpoint跟run to cursor的处理方法一样。
除此之外要注意的是coroutine的创建,会返回一个lua_State,所以需要拦截这个创建函数,对这个新创建出来的lua_State也sethook。因此在调试器中还得为不同的lua_State保存各自的调试上下文。
以上都是不考虑执行效率的做法,在云风的blog上有一篇文章,讲到他设计的一个提高调试器执行脚本效率的方案,似乎是勉强能提高一点性能,但要求用户修改Lua脚本来定期轮循调试器是否有新命令下发。其实在这个基础上,我们可以更进一步,修改Lua脚本的工作由调试器完成,对用户透明。而且定期轮循可以修改成只要下了断点的行的最前面自动插入一个调试器定义的函数调用,也就是将执行控制权再次转移到调试器中。这就比较像C/C++调试器中插入int3的做法了,如果断点是固定的话,那么调试器执行脚本的效率将几乎是无损耗的。除了效率上的提升外,各个调试命令的实现也将变得更加容易。但这个方案有一个问题是,这插入断点指令的操作必然是在脚本文件在被执行前完成的,如果在调试运行过程中有新增断点,那么仍然得退出原来的那种靠debug库line回调的方式了,除非重新启动调试。
at
20:01
0
comments
Labels:
Lua,Script
准备自己实现一个Lua的调试器,以前没做过这方面的工作,所以只好拿现成的开源代码来研究了。
比较容易找到的调试器代码有好几个,比如lldebug,RemDebug以及wxLua中的那个调试器,各自的实现方式有所不同,但万变不离其宗的是都使用了Lua自己的debug库sethook功能。还有个共同点就是远程调试的架构,而且都是用socket实现的。
RemDebug完全用Lua写成,一个只有两个文件,加在一起代码才500多行。其中一个文件中实现的是调试引擎,负现各种调试功能的实现,另一个文件是用于实现人机交互的接口。
lldebug是个日本人写的,简单看了一下,也是跟RemDebug类似分成两部分实现。它的人机交互部分就比较高级,用wxWidgets实现了一个GUI,可以直接在上面打断点,单步,查看回调栈,查看变量等等,可算是debugger部分,另外实现一个单独的程序用于执行Lua脚本,这里可称为debuggee,其实也就是在执行Lua脚本前设置一下调试选项,以及拦截一些Lua的C API。
wxLua带了一个调试功能,它跟lldebug比较像的一点,有一个独立的程序执行Lua脚本作为debuggee。另一部分则是用C/C++实现的Lua接口,可以由用户自己用Lua脚本来实现debugger,这是它相较于前两者比较独特的一点。
通过sethook实现的调试器,要特别关注coroutine的处理。
我一直比较羡慕的是Decoda的那种功能,简单说来就是它可以直接调试其他嵌入了Lua解释器的应用程序执行的Lua脚本。当时我一直想不清楚这是怎么实现的,只是看到它向被调试的应用程序进程注入了一个dll,于是猜测它拦截了应用程序对Lua的C API的调用,而拦截了C API后要做些什么我就不知道了。现在想起来,似乎也就是只要在应用程序执行Lua脚本前,对lua_State设置hook,于是就跟前面提到的那些传统调试器一样了。但另外要注意的问题是,抢在什么时候将这个dll注入,应该拦截哪些C API。如果能在应用程序创建Lua解释器前注入,那么拦截lua_open就可以了,通过修改PE文件的导入表,基本可以达到这个目的。如果不能保证注入的时机,那么比较合适的被拦截C API应该是lua_load,但要注意的是拦截后设置hook时应该注意一个lua_State只要设置一次hook就行了。
at
17:00
0
comments
Labels:
Lua,Script
前几天,把所有的插件以插件为单位打包成zip了。不过不知道wxWidgets中的zip虚拟文件系统如何支持密码,至少是没找到相关的选项。现在的情况是验证了方案可行性,可以确认功能上的不缺失以及性能上的损耗在可接受的范围内,但真正实用是一定要有密码的,也就是为了保护插件中的源代码。以前还以为带了密码就不方便第三方开发插件了,现在想通了,其实只要我提供一个插件开发环境,其中自带插件打包功能,这样就可以用同一个密码了。既然wxWidgets没有简单方便的接口来支持密码zip,那就只好自己写一个这样的功能了,好在zip实在是一个很大众化的格式,源代码很容易找到。
at
12:53
0
comments
Labels:
Plugin Framework
昨天脑袋发热,去买了个新凯越,心里很难过,死要面子活受罪。
今天早上醒来,心情更加的压抑和沉重,深深的孤独感和失落感以及挫败感。晚上做了个梦,梦见那个小姑娘,叫我帮她的同学(?)远程协助考试,考试的范围是一篇叫《西梅北》的文章。我无比清晰地记着这个题目,想起那个小姑娘,那个让我很后悔的小姑娘。
看到《间客》中的一句话:“如果将来这个联邦要收拾你……我很想在联邦之外给你留条后路。”不禁热泪盈眶。
at
10:19
0
comments
Labels:
Water
今天在宿主程序中做了修改,并添加了插件,可以在适当的时候触发刷新语法树视图。
刷新语法树的时机其实并不好选择,总的说来,应该在这两个时刻进行刷新:一,文档刚刚打开;二,做出了影响语法树内容的修改。第一点,比较容易做到,但第二点就比较困难了,它包括当前活动文档的切换,以及当前活动文档被修改。切换也是比较容易响应的,但怎样判断一个修改是否影响了语法树内容,就不简单了。我现在的简单做法是,每当有新的行时,就进行刷新,笨了点,但勉强能用。
刷新语法树的方式,也是需要仔细实现的,我现在的做法完全是为了简单起见,先停止刷新窗口,然后清空整个树,再把整个树的内容都重新添加一遍,再刷新窗口。这样的缺点是在刷新时,能明显地看到整个树视图的闪烁。如果要规避这个问题,应该比较当前树视图中的内容和待刷新的数据,在树视图中逐个判断有新增的,或需要删除的节点,这样就比较流畅了。但这个算法就相对来说复杂,而且可能更耗资源。
目前我只是让Lua中的函数定义作为语法树视图中的唯一的元素,目前看来效果还可以。其实单就Lua来讲,还可以添加变量的定义以及表的构造。但是眼前可以预见的问题是,变量可能有很多,全列出来可能干扰视线。而表的构造表达方式可以很复杂,而且表是可以在程序中动态增删元素的,这要如何处理。
at
20:10
0
comments
Labels:
Shareware
看了两天小说,呃,又堕落了。由于已经看完了,今天就比较认真地折腾起flex和bison。其实之前已经把lex和yacc脚本写完大部分了,至少可以从控制台打印结果出来了。今天就修改一下yacc脚本,把原来打印到控制台的内容保存在内存中,到时候转储成xml格式。因为只是要显示在界面的树视图中,我想了想,也就只有函数定义值得这么显示一下,所以也就只处理了这部分。
总的说来,感觉yacc有点土,它只能接受一种输入接口,而我用flex时发现可以生成C++代码,所以要给bison用的话,仍然需要把这个C++类的接口再适配成bison可用的C接口。
这种任务果然是实践性非常强的工作,本来看过一些资料,当然,能找到的资料也基本内容一样,翻来覆去那么几句话几个例子,等到自己要做时,不时地有些迷惑,只得慢慢尝试,倒也捣鼓出来了。
在yacc中可以为每个token或type指定一个union中的某个成员,其实这个成员的指定只在规则描述段中的action中有用,就我看来各种资料、教程中说的那一堆实在是扰乱视线。对于一个C/C++程序员来说,这种用法只是万千技巧中的一种,实在没必要说得那么严肃仔细,好像不那么用就不行了似的。
再说个我觉得yacc土的地方,由于这种格式上的限制,在action中只能访问一些全局的变量、对象等,至少在思维逻辑上很不连贯,其实lex也有这个问题。要我说,比较让现代化的做法是它应该生成一个类,每组action触发时,应该调用该类中的某个回调函数或虚函数,这里形式有好几种,都可以考虑,不知道boost.spirit是不是这种形式的,也许ANTLR等其他类似的工具就是这么做的。
最后抱怨一下,Lua Reference Manual中附录的complete syntax不能直接用的,至少不能直接用于yacc,有好些地方似乎没写全。
at
20:15
0
comments
Labels:
CPPOOPGPXP,
Lua,Script
昨天偶尔发现,在笔记本上编译的工程在台式机上启动即崩溃。一开始的时候我认为是因为lua装入的一些第三方库dll没有设置好依赖的dll路径和正确的manifest配置,后来发现问题不是那么简单。
首先是我用了LuaJIT的dll,而之后发现LuaJIT要求CPU支持SSE2指令集的,我那台式机是2002年的配置,当然没有。换成官方Lua后,问题仍然存在。
接着发现,安装程序没有把配置文件复制到正确的文件夹下。看了一下安装程序的脚本,果然没写对。但是当把配置文件的路径修改正确后,问题仍然存在。
因为台式机上装了Win2000ProSP4,WinXPProSP2和Win2003,所以当XP上仍然不能运行时,我切换到Win2000和Win2003上进行测试,发现问题同样存在。我考虑了一下两台机器软硬件的区别,猜想是不是仍然是软件配置的问题。于是我在笔记本上用VirtualBox建了个干净的XP系统,第一次运行时,居然报了个错,说ltxml加载不成功。幸好我前天已经自己写了个Xerces C++的绑定,而且用于的地方现在还不多,立马全都把使用ltxml的地方改成用xerces的。之后虚拟机上可以正常运行了。
到了这一点,说明可以排除安装部署的问题。再想想两台机器的区别,台式机上的3个系统都是原生的中文版系统,而笔记本上和虚拟机上的都是英文版的,其中笔记本上的打了个可以处理中文的多国语言包。我有点怀疑会不会是因为这个缘故,但又觉得可能性不大,如果真是这个原因的话就不好处理了。我试着把安装后的那些mo文件都删掉,结果仍然没用。
最后我没有办法了,只好修改源代码,从程序开始处依次加入日志,看到底在哪一步引起崩溃的。经过逐步排查,最后定位到使用Crypto++的初始化RSA公钥的对象构造时崩溃了,连异常都没有抛出。这时我想到了LuaJIT对CPU的要求,猜想会不会Crypto++编译时也受CPU的影响。用CPU-Z看了一下虚拟机的CPU,是跟宿主的CPU几乎相同的。不再继续猜想,直接在台式机上编译了一遍Crypto++,拿到笔记本上可以链接上,再把exe复制到台式机上,程序也可以正常运行了。
至此,问题解决!这时我不禁有些担心,我这样一直用笔记本写的程序会不会都有这种问题,一遇到老机器,就会罢工呢!
at
09:53
1 comments
Labels:
Shareware
本来是有ltxml这个库的,这个库使用TinyXML和TinyXPath,本来用着,也勉强,可以读取xml文件,但也不时地出点小问题,不过都让我规避掉了。这回是想写xml文件了,结果发现ltxml的接口我很不习惯,嗯,不喜欢,于是想自己重新写一个新的绑定。
我首先看了一下libxml2,发现它的文档和例子都不是很清晰,不想继续投入精力去研究了。剩下两个选择,MSXML和Xerces C++。这两个库我都用C++的类做了一层简单的封装,可以适配应用STL中的算法。后来想想,MSXML的那个封装因为当初只考虑配合MFC/WTL使用,所有的字符串都用CString了,而且另外一点是MSXML是只在Windows上可用,Xerces C++是跨平台的,特别是可以用gcc编译,于是最后就选择了Xerces C++。
本来我的那个封装是只针对DOM接口的,同时有4个类,分别对应DOM文档、DOM节点、DOM节点列表,以及DOM节点列表的迭代器。绑定的过程参考了ltxml的实现,但只给DOM文档和DOM节点定义了userdata,因为Lua有table,所以可以用来表示DOM节点列表。
绑定完成后,简单试用了一下,读取,写入都基本满足当前的需求,除了不知道怎么让它写入的时候进行format pretty print,老是多加个换行符,中间有空行可一点都不pretty。
at
20:17
0
comments
Labels:
Lua,Script
宿主程序提供了界面国际化,那么插件不能提供国际化就说不过去了,至少得有这样的机制以供支撑该种需求嘛。
得益于wxWidgets对国际化的良好支持,要让嵌入的Lua解释执行的Lua脚本也能根据宿主程序的本地化信息进行正确的处理非常简单。wxWidgets中对要进行国际化的字符串用_()进行包裹,其实这是一个宏,用于调用真正的翻译功能,比如wxGetTranslation。所以在嵌入的Lua中将wxGetTranslation函数注册到Lua中即可,然后在Lua脚本的最开始处将该函数换个更简单的名字,比如_,这是最好的名字了,哈哈。这样就可以在Lua脚本中对需要进行国际化的字符串也用_()进行包裹,它会调用wxGetTranslation函数。
GNU的国际化方案套餐中,提供一个叫xgettext的工具,可以从众多编程语言的源代码文件中提取出字符串,生成po文件以供翻译生成mo文件。比较不幸的是,xgettext不能支持Lua语言,同时由于我这个项目中使用的插件描述信息中有一部分界面信息是在xml文件中的,这也是一种自定义的格式,所在很不幸地xgettext更是不能处理啊!所以我在想,我是否要先写个可以支持Lua和我这种xml格式的类似xgettext的工具呢?Poedit太扯蛋了,居然只能认它自己生成的那种po格式的文件,稍微改一点就报错了!
at
20:17
0
comments
Labels:
Lua,Script,
Plugin Framework
上午花了几个小时修改了Auto Completion功能后,用起来的感觉已经超过LuaForWindows中带的SciTE了。于是又开始寻找起可以分析Lua代码并生成AST的东西,google了大半天,发现了几个Lua项目,一个ANTLR的文本,最后都可耻地放弃了!因为那些Lua项目全都由于这样那样原因而不可用,而ANTLR,呃,我试了下,它是用LL分析的,语法元素的分析顺序不能直接为我所用,而且我在之前一点都不了解ANTLR这个东西,我甚至翻遍网络,找不到怎么让它在需要的时候记录下行号!后来又翻了一下Lua的源代码,呃,说实话真没习惯它几乎每次函数调用都有一个回调函数的用法,嗯,我真的只会一点C++了,放弃!
真是个头痛的问题啊!我觉得我还是退回去,老老实实地再温习一下编译原理,用flex和bison自己写一个分析模块吧,毕竟Lua的语言核心真的很小很小!
at
20:04
0
comments
Labels:
Lua,Script
昨天晚上,突然发现又有内存泄漏了,我真的要疯掉了。一段代码一段代码地注释掉,来查看到底是哪段代码引起泄漏的,中间又偶然发现有些情况下又没有泄漏,真是太容易迷惑人了。最后发现还是原来那段扫描文件目录并分析xml文件那个函数引起的泄漏,费了老大的劲儿。这是一种比较奇怪的现象,我到现在也没有想明白怎么会有这种情况。本来以字符串作为key插入到std::map中时,如果有重复的key,我会用clock()生成一个数字添加到key的末尾,作为一个新key,以为这样就可以保证都插入进去了。现在发现似乎还是有漏网之鱼,真是太奇怪了。不过我也不想深究这个问题了,索性遇到这种重复的情况,直接在原来的key后面添加一串比较随机的字符,并再添加一个递增的编号,这样再要有重复,我就去买彩票了。
昨天在实现Auto Completion功能,呃,只是Lua编辑时可用。后来发现LuaForWindows中带的SciTE的一个做法可以学一下,就是可以分析出当前编辑器内的所有单词,以这些单词作为Auto Completion的列表源。于是今天想了想,这部分提取出单词的功能应该由C/C++来实现,Boost中有个Tokenizer库,刚好用来作这个事情。看了一下文档,用它自带的一个示例修改了一下测试一番,提取一个828KB的cpp文件中的所有单词,然后用STL的sort算法排序,再unique和erase一把,把重复项去除,最后再用STL中的copy算法复制到另一个vector中,总共这些操作,在VC2008中进行测试。最后发现,在Debug模式中大约在10700ms左右,Release模式则在500ms左右,近20倍的性能差异!我不知道这个比例是线性的,还是指数级的,反正证明一件事,那就是Release的确实比Debug的快很多!
at
12:38
0
comments
Labels:
CPPOOPGPXP
到昨天为止,基本完成Code Snippet的框架,剩下的都是些体力活。该特性要求在点击菜单项时,根据当前光标所在位置的字符串,替换成对应的代码片段。由于菜单项是通过插件添加实现的,而且Code Snippet又根据当前编辑的源代码对应的编程语言不同,也会有不同的处理,所以也是通过不同的插件实现的,这就要求插件可以再次调用插件。好在当初设计插件扩展框架时,已经考虑到这一点,所以虽然有实现过程中有需要慢慢调试的地方,但没有特别大的障碍。
完成Code Snippet后,应该开始Auto Completion特性的开发。该特性是本项目中可算是难度最高的特性之一,同时又有比较高的准确性和运行效率等要求。还有点比较头痛的是,针对不同的编程语言,可复用的东西不多。
此外,还有个功能应该尽早加入,就是处理文件的不同编码。比如通常,尤其是早期的代码,都是直接使用ANSI编码保存。而现在已经比较常用的是保存成UTF-8等编码方式,特别是像LaTeX的一些处理器直接要求输入文件是UTF-8编码。所以应该能在文件的装入和保存时,可以自动处理文件的编码问题,这可以通过iconv或ICU实现,不过问题就在于有了选择,才是苦恼啊!目前我倾向于使用iconv,因为相比之下更轻量,而且够用。
这些天发现,Lua的字符串连接符..效率还真低,怪不得Lua要提供*all和table.concat等设施。
本来Lua中操作XML有几种不同的选择,这跟在C++中情况差不多,我选择的是比较轻量的ltxml。而昨天在Code Snippet特性的开发过程中发现,我把所有的信息都保存在xml中,而每次完成snippet时都从xml中读取,开始几次还是正常的,但只要过一会儿,在调用xml.open时就会报什么试图index一个function值中一个number值,还真是诡异,但调试发现这时无论xml.open还是传入的参数都是正确的,很是纳闷啊。于是只好规避一下,只在开始时读一次,全部都装入到内存中,以后就直接读内存了。
一直以来,都是通过print来进行Lua脚本调试,真是应了那句“一夜回到解放前”。前两天才在界面上加了一个专门的输出窗口用于从Lua脚本打印字符串过来。好比是当年写Windows GUI程序时,用MessageBox调试进化到用OutputDebugStrng进行调试。昨天记起来有LuaLogging这么个第三方库,于是仔细看了看,很简单的功能,只有几个lua文件,可以记录日志到文件、控制台、socket、email或数据库中。于是我参照这些appender的实现,加了一个新的appender,用于将日志打印到宿主的插件输出窗口中,感觉不错。
接着是使用luabind的问题。在网上看到有人说luabind的各个版本都存在指针的double deleting问题,这让我有点惶恐。好在今天看邮件列表时,看到luabind的作者说,这种问题只出现在使用智能指针或类继承时切片的情况,而且要求是Lua的state先于这些对象被销毁,现在没有好的办法来修正这个问题。我想了想,这几种情况我现在都不会遇到,我只有最最简单的嵌入和扩展交互。之后,又发现有人在邮件列表中写了一个luabind和SWIG的性能比较,SWIG的封装比luabind的快一倍。看到这个结果,我觉得是意料之中,SWIG的封装方式比较底层,调用快也是正常的。不过luabind的作者说,他写了些benchmark的测试,在未发布的luabind 0.9中已经有不小的改进,虽然仍然不比SWIG快,但相比0.8.1版本,差距缩小了约一半,期待0.9的发布。
昨天无意中看到云风blog上一篇老文章提到有Lua Ring这么个库,可以在Lua代码中再创建个新的Lua state,让某些代码在这个新的state中运行,从而保护比较重要的核心state。我潜意识中认为,像我现在这个项目使用嵌入Lua来作为插件扩展的运行环境,确实要用一个比较安全的环境,即所谓的沙盒,但这个Lua Ring怎么应用上去,以及能有多少效果,仍然有待考察。
前些天,从SVN上更新的了wxPropertyGrid的代码后,发现用GCC编译不过了,直到昨天仍然不行,实在忍无可忍,真要骂娘了。上它的sf项目见面看了一下,自11月25日更新代码后,估计作者就压根没发现这个问题,于是在上面提了个单。今天发现作者已经回复那个单,并在svn trunk中已经修正了该问题,总算松了口气。
今天仔细学了一下如何让wxWidgets支持国际化,发现非常简单。只要在程序初始化时,自己创建一个wxLocale对象,把mo文件的搜索路径加进去,设置好当前要使用的语言。其他想要被翻译的字符串用_()宏替换wxT()和_T(),如果是个wxString对象,就用wxGetTranslation(),这样在这些字符串会自动从mo文件中读出相应的翻译后的文本,感觉比ini等配置文件,或是国际化资源dll的方案方便很多。不过为了让插件支持国际化,也可以使用类似的方案,但是我有poEdit时发现它只能从源代码中提取出需要翻译的字符串,不能全新的创建一个,这太土了,以后一定要自己写个好用的。
最后的一个问题是,现在的插件扩展机制,容易出现重复代码,比如相同的功能会在菜单中写一遍,在工具栏中也写一遍,这该怎么修改一下呢,呃,得仔细考虑考虑
at
13:56
0
comments
Labels:
CPPOOPGPXP,
Lua,Script,
Plugin Framework
![]()
![]()