Google
 

2009-12-30

Lua源代码阅读迫在眉睫

  今天在群里有个人问,他初始化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的知识!

2009-12-28

Lua调试器研究续

  本来以为自己对如何实现一个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回调的方式了,除非重新启动调试。

2009-12-27

Lua调试器研究

  准备自己实现一个Lua的调试器,以前没做过这方面的工作,所以只好拿现成的开源代码来研究了。
  比较容易找到的调试器代码有好几个,比如lldebugRemDebug以及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就行了。

2009-12-26

插件打包

  前几天,把所有的插件以插件为单位打包成zip了。不过不知道wxWidgets中的zip虚拟文件系统如何支持密码,至少是没找到相关的选项。现在的情况是验证了方案可行性,可以确认功能上的不缺失以及性能上的损耗在可接受的范围内,但真正实用是一定要有密码的,也就是为了保护插件中的源代码。以前还以为带了密码就不方便第三方开发插件了,现在想通了,其实只要我提供一个插件开发环境,其中自带插件打包功能,这样就可以用同一个密码了。既然wxWidgets没有简单方便的接口来支持密码zip,那就只好自己写一个这样的功能了,好在zip实在是一个很大众化的格式,源代码很容易找到。

2009-12-22

《西梅北》

  昨天脑袋发热,去买了个新凯越,心里很难过,死要面子活受罪。
  今天早上醒来,心情更加的压抑和沉重,深深的孤独感和失落感以及挫败感。晚上做了个梦,梦见那个小姑娘,叫我帮她的同学(?)远程协助考试,考试的范围是一篇叫《西梅北》的文章。我无比清晰地记着这个题目,想起那个小姑娘,那个让我很后悔的小姑娘。
  看到《间客》中的一句话:“如果将来这个联邦要收拾你……我很想在联邦之外给你留条后路。”不禁热泪盈眶。

2009-12-19

基本搞定语法树视图

  今天在宿主程序中做了修改,并添加了插件,可以在适当的时候触发刷新语法树视图。
  刷新语法树的时机其实并不好选择,总的说来,应该在这两个时刻进行刷新:一,文档刚刚打开;二,做出了影响语法树内容的修改。第一点,比较容易做到,但第二点就比较困难了,它包括当前活动文档的切换,以及当前活动文档被修改。切换也是比较容易响应的,但怎样判断一个修改是否影响了语法树内容,就不简单了。我现在的简单做法是,每当有新的行时,就进行刷新,笨了点,但勉强能用。
  刷新语法树的方式,也是需要仔细实现的,我现在的做法完全是为了简单起见,先停止刷新窗口,然后清空整个树,再把整个树的内容都重新添加一遍,再刷新窗口。这样的缺点是在刷新时,能明显地看到整个树视图的闪烁。如果要规避这个问题,应该比较当前树视图中的内容和待刷新的数据,在树视图中逐个判断有新增的,或需要删除的节点,这样就比较流畅了。但这个算法就相对来说复杂,而且可能更耗资源。
  目前我只是让Lua中的函数定义作为语法树视图中的唯一的元素,目前看来效果还可以。其实单就Lua来讲,还可以添加变量的定义以及表的构造。但是眼前可以预见的问题是,变量可能有很多,全列出来可能干扰视线。而表的构造表达方式可以很复杂,而且表是可以在程序中动态增删元素的,这要如何处理。

2009-12-18

初步搞定语法树

  看了两天小说,呃,又堕落了。由于已经看完了,今天就比较认真地折腾起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,有好些地方似乎没写全。

2009-12-10

解决程序不支持老机器的问题

  昨天偶尔发现,在笔记本上编译的工程在台式机上启动即崩溃。一开始的时候我认为是因为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复制到台式机上,程序也可以正常运行了。
  至此,问题解决!这时我不禁有些担心,我这样一直用笔记本写的程序会不会都有这种问题,一遇到老机器,就会罢工呢!

2009-12-08

给Lua绑定个Xerces-C++

  本来是有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。

2009-12-04

插件支持国际化

  宿主程序提供了界面国际化,那么插件不能提供国际化就说不过去了,至少得有这样的机制以供支撑该种需求嘛。
  得益于wxWidgets对国际化的良好支持,要让嵌入的Lua解释执行的Lua脚本也能根据宿主程序的本地化信息进行正确的处理非常简单。wxWidgets中对要进行国际化的字符串用_()进行包裹,其实这是一个宏,用于调用真正的翻译功能,比如wxGetTranslation。所以在嵌入的Lua中将wxGetTranslation函数注册到Lua中即可,然后在Lua脚本的最开始处将该函数换个更简单的名字,比如_,这是最好的名字了,哈哈。这样就可以在Lua脚本中对需要进行国际化的字符串也用_()进行包裹,它会调用wxGetTranslation函数。
  GNU的国际化方案套餐中,提供一个叫xgettext的工具,可以从众多编程语言的源代码文件中提取出字符串,生成po文件以供翻译生成mo文件。比较不幸的是,xgettext不能支持Lua语言,同时由于我这个项目中使用的插件描述信息中有一部分界面信息是在xml文件中的,这也是一种自定义的格式,所在很不幸地xgettext更是不能处理啊!所以我在想,我是否要先写个可以支持Lua和我这种xml格式的类似xgettext的工具呢?Poedit太扯蛋了,居然只能认它自己生成的那种po格式的文件,稍微改一点就报错了!

2009-12-03

AST, AST, AST...

  上午花了几个小时修改了Auto Completion功能后,用起来的感觉已经超过LuaForWindows中带的SciTE了。于是又开始寻找起可以分析Lua代码并生成AST的东西,google了大半天,发现了几个Lua项目,一个ANTLR的文本,最后都可耻地放弃了!因为那些Lua项目全都由于这样那样原因而不可用,而ANTLR,呃,我试了下,它是用LL分析的,语法元素的分析顺序不能直接为我所用,而且我在之前一点都不了解ANTLR这个东西,我甚至翻遍网络,找不到怎么让它在需要的时候记录下行号!后来又翻了一下Lua的源代码,呃,说实话真没习惯它几乎每次函数调用都有一个回调函数的用法,嗯,我真的只会一点C++了,放弃!
  真是个头痛的问题啊!我觉得我还是退回去,老老实实地再温习一下编译原理,用flex和bison自己写一个分析模块吧,毕竟Lua的语言核心真的很小很小!

2009-12-02

又有内存泄漏

  昨天晚上,突然发现又有内存泄漏了,我真的要疯掉了。一段代码一段代码地注释掉,来查看到底是哪段代码引起泄漏的,中间又偶然发现有些情况下又没有泄漏,真是太容易迷惑人了。最后发现还是原来那段扫描文件目录并分析xml文件那个函数引起的泄漏,费了老大的劲儿。这是一种比较奇怪的现象,我到现在也没有想明白怎么会有这种情况。本来以字符串作为key插入到std::map中时,如果有重复的key,我会用clock()生成一个数字添加到key的末尾,作为一个新key,以为这样就可以保证都插入进去了。现在发现似乎还是有漏网之鱼,真是太奇怪了。不过我也不想深究这个问题了,索性遇到这种重复的情况,直接在原来的key后面添加一串比较随机的字符,并再添加一个递增的编号,这样再要有重复,我就去买彩票了。
  昨天在实现Auto Completion功能,呃,只是Lua编辑时可用。后来发现LuaForWindows中带的SciTE的一个做法可以学一下,就是可以分析出当前编辑器内的所有单词,以这些单词作为Auto Completion的列表源。于是今天想了想,这部分提取出单词的功能应该由C/C++来实现,Boost中有个Tokenizer库,刚好用来作这个事情。看了一下文档,用它自带的一个示例修改了一下测试一番,提取一个828KB的cpp文件中的所有单词,然后用STLsort算法排序,再uniqueerase一把,把重复项去除,最后再用STL中的copy算法复制到另一个vector中,总共这些操作,在VC2008中进行测试。最后发现,在Debug模式中大约在10700ms左右,Release模式则在500ms左右,近20倍的性能差异!我不知道这个比例是线性的,还是指数级的,反正证明一件事,那就是Release的确实比Debug的快很多!

2009-11-29

当前项目进展及小结

  到昨天为止,基本完成Code Snippet的框架,剩下的都是些体力活。该特性要求在点击菜单项时,根据当前光标所在位置的字符串,替换成对应的代码片段。由于菜单项是通过插件添加实现的,而且Code Snippet又根据当前编辑的源代码对应的编程语言不同,也会有不同的处理,所以也是通过不同的插件实现的,这就要求插件可以再次调用插件。好在当初设计插件扩展框架时,已经考虑到这一点,所以虽然有实现过程中有需要慢慢调试的地方,但没有特别大的障碍。
  完成Code Snippet后,应该开始Auto Completion特性的开发。该特性是本项目中可算是难度最高的特性之一,同时又有比较高的准确性和运行效率等要求。还有点比较头痛的是,针对不同的编程语言,可复用的东西不多。
  此外,还有个功能应该尽早加入,就是处理文件的不同编码。比如通常,尤其是早期的代码,都是直接使用ANSI编码保存。而现在已经比较常用的是保存成UTF-8等编码方式,特别是像LaTeX的一些处理器直接要求输入文件是UTF-8编码。所以应该能在文件的装入和保存时,可以自动处理文件的编码问题,这可以通过iconvICU实现,不过问题就在于有了选择,才是苦恼啊!目前我倾向于使用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时发现它只能从源代码中提取出需要翻译的字符串,不能全新的创建一个,这太土了,以后一定要自己写个好用的。
  最后的一个问题是,现在的插件扩展机制,容易出现重复代码,比如相同的功能会在菜单中写一遍,在工具栏中也写一遍,这该怎么修改一下呢,呃,得仔细考虑考虑

2009-11-24

找到关键跳,再强的加密算法也没用

  突然想研究一下软件注册机制中使用比较强的加密算法,能带来什么效果。说到比较强的加密算法,用非对称的算法应该是公认的比较好的选择。流行的大概有RSAElGamalECC了。翻出几本买了好久,都没仔细看过的书,最后发现我的数学基础实在很差,这方面的思维分析能力也很差,最终大致能看懂原理的只有RSA算法。RSA算法的强度建立在大素数因子分解的基础上,只要选择足够大的两个大素数,当然还要受制到当前主流硬件运算能力,就能保证一定意义上的安全。
  目前有很多开源的加密算法库和大数运算库,可以提供RSA算法的实现,比如MIRACLCrypto++LibTomCrypt等等。结果发现MIRACL只能免费用于非商业用途,而LibTomCrypt的接口让我比较迷惑而不会用,剩下的Crypto++倒是可以正常使用,接口设计得也非常方便易用,缺点是体积大了点,而且很早以前在网上看到过有人说它有些算法可能涉及到一些版权、专利的问题。
  最后是使用RSA实现软件注册算法的问题,从书上看到,注册机使用RSA私钥对用户名进行解密,解密的结果则作为注册码,用户拿到该注册码,输入到软件注册界面后,软件使用RSA公钥对注册码加密,能得到用户名?不过我发现,在用Crypto++进行计算时发现,随便取的一个字符串作为密文用私钥根本解密不了啊,会抛异常说无效的密文,其次,使用公钥进行加密时,也有限制,对明文的长度有限制,根本不能对很长的,可能是密文的字符串进行加密呀!倒是用RSA进行签名是可以的,这种签名方式就与MD5、SHA之类的单向散列算法效果类似了,只能确认明文是否被修改过。
  不过我到PEDIY的论坛上问了问,其实采用多强的加密算法没多少区别,只要破解者能找到最后判断的语句,找到关键的那条跳转语句,就可以爆破了,前面的那些复杂的加密解密运算全做无用功了。汗!

2009-11-18

修正3处内存泄漏

  不知从什么时候起,程序退出时,VC的输出窗口中就会打印一大片内存泄漏信息。开始还没怎么在意,认为只要程序功能正常,有点儿内存泄漏实在不是什么大不了的事情。最近随着代码的增长,似乎打印出来的内存泄漏数量也随着增长了,我仍然有些想逃避,安慰自己说不定是wxWidgets的问题,其实用膝盖想都知道,这种可能性太小啦!
  晚上实在忍不住,或许真是只是想证明,确实是wxWidgets的问题吧,决定看一下到底是哪里泄漏的。因为程序使用嵌入Lua,很多功能都通过脚本扩展实现了,C++的代码量不多,基本上只剩下一些主框架界面的创建和消息响应转发的工作,所以没花多少时间,通过分段屏蔽代码来检测内存泄漏的源头。
  最终发现有3处,而且确实都是我自己的代码有问题。
  首先是有个singleton在程序退出前没有销毁掉,好像在GoF中还是《Modern C++ Design》中说的,这种情况的singleton销不销毁影响不大,不过用Loki中的Singleton是不会有这种问题的,所以还是为了美观起见,主动销毁吧。
  然后,稍稍花了点时间,发现有一处new了一个对象后把指针插入到std::map中,却发现new出的对象个数最后比map中的元素个数多几个,那么最后通过迭代map销毁这些对象时,就有几个漏掉没销毁掉了。我一时间还没想明白怎么会出现这种问题的,后来想起来,有几个对象插入时估计是使用了相同的key,于是只能在map中留下一个。程序运行表现倒是没错,这是一种奇怪的现象,但在这里是合乎逻辑的,但仍然要改掉。
  最后,发现是new了wxFlatNotebook后,就有泄漏。照理说,这种有父窗口的子窗口对象创建后,wxWidgets是会负责销毁的。所以估计是wxFlatNotebook有什么特殊的要求,从sourceforge上找到它的页面,有SVN下载选项,最近一次更新是2008年的事了,估计是不会更新了。它的作者居然就是CodeLite的作者,但我发现似乎CodeLite本身就没用这个组件,作者又自己实现了一套标签系统,不过都放在CodeLite里没独立出来。对比了一下我使用的wxFlatNotebook的版本,应该是2.1版,跟CodeLite代码里放的那份是一样的,SVN trunk中的至少是2.2以后版本了。于是下载下来,还有sample,这才是最重要的,看了一下sample在主窗体的析构函数中调用了一下wxFNBRendererST::Free()。把这条语句加到我的程序中,真的没有泄漏了!
  现在舒服了,不报内存泄漏了,能这么顺利地解决3处内存泄漏问题,跟程序架构有很大关系啊!

库使用注意

  上午整了一两个小时,在wxWidgets程序中使用第三方库wxPropGrid,结果发现在VC2008中链接时有几个warning,虽然看起来刺眼,但似乎是可以正常运行的,也没有很在意。然后用MinGW编译链接,最后链接不通过,报未定义的符号,而这些符号是之前用VC2008时报warning的那几个,这就说明不是库编译得有问题,就是本身程序编译得有问题。
  我先把焦点放在库上,wxPropGrid是编译成静态库的,这不但编译链接选项不同,连有个宏定义(静态库是WXMAKINGLIB_PROPGRID,动态库是WXMAKINGDLL_PROPGRID)都不同。我仔细观察了该宏定义对源代码的影响,并参考了wxScintilla的做法,发现区别很小,基本可以忽略。于是我琢磨着如果实在不行了,把wxPropGrid编译成动态库试试。正在这么打算的时候,突然想起来,这个宏定义在主程序中没定义啊!一定是这个原因!于是修改了主程序的配置,加上了这个宏定义,重新编译,发现果然有效,VC中也不报warning了,MinGW中也可以链接通过了!
  其实这是个老问题了,只是平时很少遇到这种情形,一时没想起来。

2009-11-14

渐渐习惯用Lua写代码了

  今天本来打算把那几个菜单项的功能完成的,不过后来又转去做其他事情了,计划执行力不强啊,唉!
  要在Lua中操作xml,在现在的软件开发中,这是个很常见的需求,xml已经变得无处不在了。在神作PIL中提到的是用expat库,呃,我只会用DOM,所以只好找其他的库了。前些天在luaforge上看到一个叫ltxml的,才0.2版本之后就没更新过了,用的是TinyXPath和TinyXML,我决定好好考察一下。
  它没有文档,只有一个readme后面四五行C代码示例,不过这足够了,看一下那个cpp文件中注册的方法,基本可以猜出用法。总的说来,先require,再用xml.open方法打开xml文件,然后就可以用TiXMLDocument的select等方法得到TiXMLNode,就跟我熟悉的用C++操作DOM的做法一样了。
  说起来TinyXML的表现不差,基本能满足我当前的需要,我有点儿后悔当年花了那么大力气将Xerces C用VC2008编译了一遍,又绞尽脑汁用MinGW编译了一遍,还自己封装了一把,以适应STL中的算法。对于我来说,它太庞大了,让我畏惧,那么大一个却仍然要让Xalan来处理XSLT和XPath。同样,libxml和libxslt给我的印象也差不多,它们甚至没能让我顺利编译!
  昨天说到的,现在程序崩溃总是无声无息地自动退出了。今天想了想,其实之前好像也想到过,会不会是因为LuaJIT的缘故,于是换了官方的Lua的dll来用,果然在原本会引起退出的地方,Lua只是而压了条错误信息到栈中,LuaJIT的行为没跟Lua一致啊!在Lua list上发了个邮件问问,结果一个老表说他没能重现,问我有没有证据,我汗,用Wink录了个8MB的操作录像,然后发现这maillist限制最大附件是40KB,严重超标,还要等人审查。总结一下崩溃的条件,在Lua代码中调用第三方C/C++代码注册的类和方法,如果方法、成员不存在,就会退出。而Lua是能处理成将其识别为一个nil,然后报不能在nil上进行函数调用之类的话。
  总之,一切在向好的方向发展,甚至自己已经能渐渐地习惯于写Lua代码了,不像之前那样非C/C++不爽!

2009-11-12

修正问题,展望未来

  经过定位,方法很简单,在有怀疑的地方加入跟踪语句,打印栈大小,发现确实还是在那几个地方,栈中项的数量稳步增加,这才想到,会不会是从表中获取某个元素后,那个表还在栈中呢?看了下文档,也没有相关的说明,只好先作这个假设。在调用表中的函数后,应该弹出两个值,这样修改后,果然过了一个多小时也没退出,而且看栈中的项也确实没泄漏的。
  昨天修正了对TeX代码高亮的问题。原本发现有TEX和LATEX两种lexer,如果直接设置好lexer的话,LATEX只能着色,没有代码折叠,而TEX有代码折叠,不能着色。看了代码似乎也都不用设置什么关键字字符串的,没办法只好到scintilla的maillist上问一下,Neil回复说SciTE中用的是TEX。于是我又看了一下TEX的properties文件,发现还有4个专用的property要设置,加上后果然好了。开源就是开源,只有通用的scintilla接口文档,却没有针对每个lexer的文档,唉!
  昨天还写了个小程序,用于将原本给另外一个程序用的Scintilla的所有lexer的语言配置文件从xml格式转换成现在正在进行的这个程序可用的 lua脚本。这是个很省事的活,不过这小程序也是花了不少时间修改,因为要生成的lua脚本也在不停地修改。从这看出,xml真是一种存储数据的好格式 啊,可以方便地转换为其他格式。要是SciTE的配置文件也是xml的就好咯,想当初为了把众多lexer的配置从properties格式转换成 xml,可是花了我好几个晚上的业余时间的。
  接下来应该要实现其他一些基本的功能,以及好好考虑下如何实现针对不同lexer的各种功能。

狂烈地崩溃

  昨天偶然发现一个超级严重的问题,程序运行一小会儿就会自动退出,什么提示都没有。至于没提示,这已经有一段时间了,照理说,内部状态、逻辑不正常么,可以给个Windows的崩溃报告嘛,可是它偏偏没有,弄得我要跟着崩溃了。
  后来在代码中加入一些跟踪语句,发现出错的原因跟我的猜测一致,内嵌的Lua解释器栈溢出了。这是个很头痛的问题,以前听人说过,如果没有sandbox,插件运行环境是不可靠的,呃,最出名的是chrome的架构,经典的sandbox。但是我这个程序跟它的情况有点不同,在主窗口和子窗口上都有大量的用户交互操作,以及主窗口和子窗口之间大量的交互,子进程间的通信会很复杂。而且现在引起崩溃的,都是主窗口中的逻辑,所以还是会导致整个程序的不可用。
  昨天晚上调试了好久,发现只要更新工具栏按钮或主菜单项的界面状态的响应函数打开后,过一会儿就会退出。所以最后可以把范围缩小在C++调用Lua函数的那一块代码上。我不怀疑Lua的代码有问题,凭我现在对Lua的了解,即使真有问题,估计我也是束手无策的。既然是栈溢出,而且时间不长就可以重现。我仔细地看了那块代码,又看了Lua manual和PIL,以及Luabind Documentation,发现我一直忽略的一个问题,在调用Lua的C API出错后,Lua经常会把出错信息压入栈中,而Luabind可能会直接将其封装为luabind::error类型的异常抛出,然后我就只是看一下那个字符串内容,却没其他处理了。这是一处错误,应该在提取字符串后,将其弹出。另一处错误是,我这里调用Lua中的函数,都是存放在一个表中的,所以中间无论哪个步骤出错,都应该把先前压入栈中的东西弹出。还有一处错误是,最后我从Lua栈中获取到函数后,用luabind::object封装了一把,然后luabind::call_function来调用,这时我又直接返回了,却没把这个放在栈中的函数弹出。
  昨晚解决了这三个问题后,还以为所有问题都已经修正了。今天又测试了一遍,发现过了约半个小时后,程序还是自动退出了,而且连那exe文件都没了!我要疯了!
  唉,这什么都是从零开始的,风险实在太大了。使用wxWidgets是第一次,复杂的内嵌Lua扩展框架是第一次,使用Luabind是第一次,使用wxLua是第一次,把所有东西混在一起用更是第一次!而且很不爽的是,已经用惯了MS的解决方案的我,没有像MSDN这样的大而全的文档极不适应,那些说使用开源的东西成本低的人,不知是真的短视,还是别有用心呢。

2009-11-10

使用SWIG和Luabind的问题续

  这几天似乎渐渐进入了正轨,发现并解决了一堆的问题。
  从github上下载的0.9版Luabind可能不稳定,毕竟是没有正式发布,之前没有经过仔细的试用,后来实际用的时候总是这也不行那也不行,万般无奈之下退回到正式发布的0.8.1版。两个版本确实有不少区别,至少源文件个数都不一样。在实际使用的过程中发现,0.8.1中如果注册一个类时,无论有没有注册它的构造函数和析构函数,都要求它们是public可见的,而0.9似乎没这个限制。
  比较郁闷的是,没找到一个可靠的办法,把一个类中的STL的容器类型的成员变量传给Lua。Luabind中有个return_stl_iterator策略,似乎是可以把它传给Lua,也能在Lua进行迭代,但是在应用程序退出时,就会报未处理异常。我想以后如果真的一定有传容器的需求,可以试一下SWIG。
  在Lua和C++之间传递字符串是个很头痛的问题。C++中的字符串类型太多了,几乎每种框架或大规模类库,都会有至少一种自己的字符串实现类。在wxWidgets中用的是wxString,但跟Lua交互的最好就是C的字符数组。如果是只读的访问,那还是比较容易处理的,Luabind默认处理了std::string类型的转换,SWIG中只要包含了std_string.i,也是差不多透明地处理了。但是如果要把字符串作为输出参数,那就头痛了,Luabind中是有out_value策略和pure_out_value策略,但实际上我发现在Lua中用的时候程序就崩溃了。暂时也不想再深究这个问题了,顶多在必要的时候考虑修改一下接口了。
  又是才发现,SWIG和Luabind中注册的类型是不能互相混用的。比如SWIG中注册的某个函数,返回一个类的实例,而Luabind中又注册了这个类的话,是不能作为那个返回值的类型的。所以有时候可能需要在两边都注册一遍某个类型。比较安慰的是,基本数据类型还是能混用的,呵呵。
  对于有缺省值参数的函数,SWIG会自动封装成多个函数,每个函数使用相同的名字,只是参数列表不同。而Luabind一次只能注册一个函数,那个完整参数列表的函数,如果想要达到SWIG那样的效果,大概只能自己老老实实一个一个注册上。
  再有是Luabind中,不能随便注册char*类型参数的函数,要注册得指定out_value策略,不过说到这里我就有点疑惑,Luabind好像不支持多个策略的啊,如果有多个参数要指定策略怎么办?而且虽然我没经过测试,但我想除了char *外,其他的自定义类型作为参数的,都有这个问题吧!

2009-11-08

混用SWIG和Luabind的问题

  花了两三天时间,总算是从头到尾看了一遍Luabind的使用手册,呃,为了确保没有漏过的内容,甚至还把整个文档都翻译了一遍。在看文档的过程中,还是比较感叹该库做得功能强大的,不过在实际用的时候就比较郁闷了。
  我用Luabind绑定了一些类和函数等内容到一个模块中,用了SWIG把几个头文件处理了一遍的,把所有内容都放到同一个模块中。一开始发现在Luabind中绑定的类在Lua中死活不能用,调用成员函数时,总是说那个类是个nil,不能被index。
  后来尝试了下,把Luabind中绑定的内容放在全局域中,发现居然可以了!虽然这样似乎可以工作,但我想让所有由内部C++代码提供的服务放在同一个模块中。后来想了想,把SWIG和Luabind的注册顺序换一下,改成先注册SWIG的内容,再注册Luabind的内容,嘿嘿,真的也可以了!我猜想可能Luabind的注册内容是采用追加的方式,而SWIG却是覆盖方式。这个猜想没有经过证实,只是如果真是因为这个原因的话,也是可以理解的。

2009-11-04

从C++传递窗口给wxLua

  描述一下问题,程序主框架是用C++实现的,GUI框架用的wxWidgets,有一部分功能通过嵌入Lua解释器调用Lua脚本完成,如果要在Lua脚本中用wxLua实现个对话框的这种情况下,需要一个父窗口,而最好的父窗口是由C++实现的,现在就需要能把C++中实现的窗口传递给Lua,并让wxLua作为父窗口使用。
  问题根源是出于偷懒的考虑,我把C++中需要能被Lua调用的类、方法等用SWIG嚼了一遍,生成了脱水代码。这样用SWIG返回的wxWindow*跟wxLua中的wx.wxWindow就不是一种东西。
  解决方案比较quick and dirty,过程略有点曲折。昨天偶然在wxWiki上看到一段文字,描述了如何将MFC的窗口关联到wxWidgets中,我就想如果wxLua是严格移植了wxWidgets的话,应该也很容易实现的。不过很沮丧的是SetHWND、AdoptAttributesFromHWND、Reparent这三个方法wxLua一个都没有实现!于是又去wxWidgets的Google group上找了找,发现一个叫AssociateHandle的方法,可以关联一个原始的Windows窗口句柄。到了这一步,我已经没有其他出路,只好修改wxLua的源代码,在wxLua\modules\wxbind\src\wxcore_windows.cpp这个文件中给wx.wxWindow添加一个新的方法void SetWindowHandle(long hwnd),代码实现可以抄SetWindowStyle的,函数签名相同,里面的步骤也类似。最后要在wxWindow_methods的初始化列表中添加这个新增的方法,就可以重新编译wxLua了。
  有了这个修改过的wxLua,再在自己的应用程序中暴露一个方法以便Lua获取主窗口的句柄,接口就可以在Lua中这样使用了:
  local win = wx.wxWindow()
  local hwnd = frame:GetMainFrameHandle()
  win:SetWindowHandle(hwnd)
  这个win就可以作为wxLua中创建的子窗口、对话框的父窗口了!

2009-11-03

划分脚本插件架构的职责面临的问题

  今天整理了一下项目中使用的第三方框架、库的列表,很多,有C++的,有Lua的,突然面临一个迫切需要解决的问题:怎么决定某个功能应该由C++实现还是由Lua实现?
  最早决定让项目成为一个由C++构建主体框架,由Lua脚本扩展实现其他的业务逻辑时,只是单纯得想让C++完成一部分最核心的功能。但现在的问题是,怎么判定一个功能是否够核心,以及即使够核心了,还得考虑其他一些因素,包括实现难度,安全保护,代码架构合理性,代码和逻辑共享等。
  众所周知,用不同的语言实现相同的功能,难度和工作量可能差别很大。以前听同事和领导不止一次说起过,一行脚本顶得上一百行C++代码。这也许有点夸张的成分,但正好说明差异巨大这个事实。引起这种差异的主要原因就我自身角度出发来看,在于对语言的掌握程度,不同的语言风格就对不同开发任务的适应度,以及可利用的现成的库和代码的丰富度和成熟度。
  说起安全保护,是今天看到Lua的maillist上讨论LuaJIT时Mike Pall说起源代码保护时才提醒了我。一般说来,用C++编写的代码,经过编译生成二进制代码,比起用脚本语言写的代码生成的字节码(中间码)反编译要困难得多。而我本来考虑让众多功能都通过脚本实现,这就面临一个问题,如何保护自己觉得重要的代码。最早的时候想用Lua的官方编译器编译一把就行了,现在看来这个保护弱得可以。而最近又面临另外一个短期内不可能解决的问题是,我打算嵌入LuaJIT 2.0的解释器,而刚刚才知道LuaJIT 2.0不兼容Lua官方的字节码,只提供源代码级的兼容,似乎LuaJIT也没提供一个自己的编译器,同时即使有这样的编译器,万一某种情况下需要用Lua官方编译的文件,那么处理就不一致了!Mike Pall的意见是,用个zip之类的东西打包加密就行了,呃,怎么说,确实是个方案,但使得本方案只能自己使用的了,不能让第三方的开发者参与了!所以除非有其他比较完善的解决方案,不然那样的代码只能用C++实现了。
  再说架构合理性,总的说来,到目前为止的进度,自我感觉这样的架构还是比较满意的,当然现在只是一个空壳,只能支持让主菜单、工具栏按钮和右键弹出式菜单的构建和触发都是由脚本插件扩展而成,接着就而对的问题是,像配置选项功能要由谁来做,这种功能有一定的复杂性,又有GUI界面,又有后台处理逻辑,是全部让C++做,还是全部让Lua做,或者是各做一点,那又是各做哪些和各做多少呢?几乎所有同时涉及界面和后台逻辑的功能点,都有这样的问题,究其原因在于,用C++实现了主界面,而在Lua中目前并不能很方便地操作这些界面。原本天真地以为,C++用了wxWidgets做界面,那么Lua中用wxLua就可以实现无阻碍互通了。现实是残酷的,现在光是想让wxLua中使用C++中创建的主窗口作为父窗口就搞不定!
  最后是代码和逻辑的共享。这个问题不是很严重,如果是纯粹的计算逻辑最容易共享,一些常用的底层功能,两种语言都差不多拥有第三方库来解决。除了GUI,其他的代码(逻辑)要共享,通过luabind和SWIG可以比较方便地粘合起来。如果粘合起来还是犹豫不决,那就是架构合理性的问题了。
  想不到这次决定构建一个基于C++的脚本扩展框架的应用程序,会引出这么多问题,大大出乎我的意料啊,我是一直以来对风险的估计不足啊!

直接用LuaJIT 2.0

  昨天发现LuaJIT2.0跟某些第三方Lua库不正常使用,于是在Lua的maillist上发了个邮件问问,结果今天看到Mike Pall的回复说是IUP、IM它们的代码里插入了硬编码的已经被编译成字节码的Lua脚本,而这些脚本在IUP中处理时,没有正确处理出错的情况,于是说这个不是LuaJIT的问题,应该向IUP提交这个Bug。
  这让我比较纳闷,因为我不知道到底问题出在哪里,即使要向IUP提交bug,只说一句Mike Pall说的你们的代码有问题,LuaJIT2.0里不能require,而明明官方Lua和LuaJIT1.0是可以正常使用的,人家会睬我吗?
  今天我又发现,在require另外一个叫iupluaim的库时,LuaJIT 2.0会崩溃!于是我想我先看看是LuaJIT中的哪行代码引起的崩溃,用VC2008创建了个解决方案,添加了代码进去,编译出Debug版本的LuaJIT,重现问题,最后发现居然是lj_vm.obj里崩溃的,而这个lj_vm.obj文件是通过一个叫buildvm.exe的程序生成的,所以VC的调试器跟踪不到它的代码里面去,唉,只好放弃了,反正也不能说一定是LuaJIT的问题,或一定是IUP的问题,只有Mike Pall才知道。
  最后,我把直到昨天下午编译出来的众多第三方Lua库都用LuaJIT来require,发现2.0版本中一共有9个dll不能require,而1.0版本只有一个会崩溃。现在想想,这些2.0版本中不能require的dll都是IUP、IM和CD中的,这些库其实我暂时也用不上,IUP可以用wxLua代码,而IM和CD是跟业务基本无关的,就直接用LuaJIT 2.0就行了!

2009-11-01

LuaJIT初体验

  偶然看到云风blog上讲到LuaJIT2.0 Beta发布了,于是很好奇地到它的官方网站上看了看。以前也是听说过有这个东西的,不过以前根本不用Lua这东西,看过也就忘了。
  这个东东据说是从API到ABI都是兼容官方Lua的最新版本的,所以一般说来,用官方Lua做的事情,用LuaJIT也可以做。但是它的强项在于,它执行Lua脚本比官方Lua要快,最慢的是快一点点,大概一点几倍,好的情况下能达到几十倍。
  这次说的2.0 Beta版本据说是VM部分跟1.x版本来说完全重写了,效率又是提升了n倍。这个效率有提升,其他代价也几乎没有,这等好事不能错过,于是下载了它的源代码来体验一把。
  它的编译方法跟官方Lua的很像,反正很容易。在Windows平台最后会生成一个dll文件一个exe文件,这点跟官方Lua也是很类似。只要在编译的时候保证dll的文件名,这样就可以把这个dll拿到其他嵌入Lua的项目中去用了,那些项目的源代码是不用修改的,因为API兼容嘛,而且好像也不需要重新编译,因为ABI兼容嘛。
  经过短暂的体验后,发现Beta果然是Beta啊,直接require那iuplua只报什么call nil value,另外就是我自己的那个嵌入Lua的程序会崩溃,具体就没有定位了。而这些问题在1.1.5版的LuaJIT中是不存在的,所以2.0要能正式Release应该还需要一段时间。

无奈啊,还是离不开VC

  之前说过,wxWidgets程序是用MinGW编译的,所以用到的wxLua就只好用其他编译器了,试了BCC 5.5和OpenWatcom 1.8,都因为不能顺利编译wxWidgets而放弃了,只好再掉头用回VC 2008。
  既然用了VC编译wxLua,而前些天用MinGW编译的IUP等用起来又有问题,于是就索性让VC把IUP、IM、CD等其他的库也都编译了好了。因为只有最最核心的功能是用C++写的,其他的功能能用Lua的都用Lua写,所以就需要有比较完善的常用功能的库。除了wxLua、IUP这等GUI库外,剩下还需要数据库访问的,至少是能访问sqlite3的,网络通信的,XML操作的,正则表达式,MD5等散列值计算的这几方面的库。现在暂时还没编译,等到时候真正需要的时候再搞吧。
  在编译和使用wxLua和IUP的过程中,遇到不少问题。
  Lua脚本在require一个模块时,会去几个固定的路径下搜索名字匹配的文件,于是我照LuaForWindows的做法,把dll都放在exe程序所在目录的clibs子目录中。发现一个一直以来的错误的认识,以为一个dll在载入另一个dll时,会像exe一样首先搜索自己所在目录。错了,一般情况下是不会搜索dll所在目录的,而是搜索载入该dll的exe文件所在的目录。所以一开始总是不能正确地让wx.dll载入wxWidgets的dll,后来将wxWidgets的dll放在exe所在目录后,又报不能载入msvcp90.dll,这是VC的重发布文件,本来exe是通过manifest文件来指定这个文件的搜索路径的。所以经过试验,发现wx.dll和wxWidgets的dll是没有包含manifest资源的,只好用命令行mt.exe -manifest xxx.dll.manifest -outputresource:xxx.dll;2这样把manifest再注入进dll。命令行参数都容易理解,最后个2,我猜测是注入后的资源编号,manifest类型资源在exe中是1,在dll中是2。经过这样处理的dll,都能正确载入msvcp90.dll等重发布文件。
  接着再解决dll存在位置的问题。本来这些dll只是为clibs下的dll依赖的,我当然不乐意让它们放在clibs外面,所以在网上找了一下,发现确实可以为单独的exe文件设置dll搜索路径。只要在注册表中 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths 下添加一个新的键,项的名字就是exe文件的名字,比如CodingStudio.exe,然后在这个新键中写入字段,默认字段的值为exe文件的完整路径,再添加个新的字符串,名称为“path”,值为dll所在的目录完整路径。这样不但可以在“开始”-“运行”对话框中直接输入exe文件名来启动该程序,还可以让该exe在载入dll时搜索那些目录。
  编译IM时,有个im_capture库,在Windows下是依赖于DirectX的,需要先安装DXSDK,现在MS网站上只能找到2008年发布的,版本至少是9.0以后的了,这里有个问题是,im_capture用到了qedit.h文件,该文件中又包含了dxtrans.h文件,而这个文件是没有的。在网上找的方案基本上有两种,一种是在其他地方找个dxtrans.h文件,然后就行了,不过我没找到,另一个方案是修改qedit.h文件,不要包含这个文件,然后在它的开头定义4个宏:__IDxtCompositor_INTERFACE_DEFINED__、__IDxtAlphaSetter_INTERFACE_DEFINED__、__IDxtJpeg_INTERFACE_DEFINED__、__IDxtKey_INTERFACE_DEFINED__。这样就可以了,im_capture只需要DXSDK的头文件,不需要DX的什么库文件。
  还有个im_wmv库,依赖于MS的Media Format SDK,可以在MS的网站上找到。其实只是需要它里面的一个叫wmvcore.lib文件,其他的头文件是不需要的,如果把头文件路径添加到搜索路径,反而编译会出错。
  最后是发现,在方能iupimglib时,VC的编译器就占满CPU然后就挂死了,郁闷!只好继续用前些用MinGW编译出来的iupimglib.dll和iupluaimglib51.dll了。

2009-10-31

继续改进插件扩展框架再续

  今天完成了右键弹出菜单的插件扩展框架支持,基本上没有遇到什么障碍,跟原来想的一样简单。
  除了这个,还把菜单、工具栏的插件扩展支持功能的代码重构了一遍,把这部分功能提取成一个独立的类,在类中完成插件扩展的相关功能,只有最终的事件消息响应函数仍然放在界面类中,这是因为才发现不是随便一个类的成员函数都可以绑定成事件处理器的。
  顺便说个可能是wxWidgets的bug,动态创建的菜单,动态添加的菜单项,第一个图标总是显示不出来!
  再一个是原本用SWIG生成的文件,我把它直接作为头文件,包含在另一个源文件中,而该源文件因为某些原因,经常会被重新编译,而恰恰这SWIG生成的文件体积巨大(超过25000行),所以编译要花不少时间。于是又仔细看了一下生成的这个文件,发现其实最终只是需要一个luaopen_libname的函数,这样SWIG生成的文件就可以作为源文件了,不用跟着其他文件编译了。

2009-10-30

继续改进插件扩展框架后续

  今天修改了插件扩展的描述方式,把菜单项、工具栏按钮的标题、路径和帮助文本,工具栏按钮的图片等信息,全都写到xml描述文件中,这样一弄,lua脚本确实精简了很多。到现在为止,已经可以正常地通过插件扩展实现主菜单和工具栏的点击响应了,如果要说更新界面状态,也不是很麻烦,也就是多添加一个消息连接而已。
  再说右键弹出菜单,我粗略地想了想,应该不是很麻烦,也就是添加到各自的扩展点下即可。其实直到现在,我才想起来,我这种实现方式,其实应该跟Windows传统的GUI资源编程基本思路是一样的。主菜单用一个编号标识,然后是菜单项信息,需要足够多的信息可以标识出菜单项的位置(路径),然后是给菜单项添加消息响应。工具栏的实现也是类似,所以如果要支持右键弹出菜单,也沿用那套思路就行了。
  昨天说到,如果在插件扩展中使用wxLua,那么wxLua不能使用宿主程序使用的wxWidgets二进制文件,于是我今天想用IUP来试试。我从CVS里取出IUP的代码,然后用MinGW编译出所有的dll文件,可是用的时候发现总是报IupClipboard符号在指定的dll中找不到,而我用depends看是有的,郁闷!但是用LuaForWindows里的dll是可以的,可是它是用VS2005编译的,要带一个VC2005的redist,不爽啊!
  另外一个问题是,本来以为脚本扩展时用了wxLua,而宿主程序也用了wxWidgets,两个之间可以无阻碍地互相使用各种控件,今天才发现,当时太想当然了。我用SWIG封装了宿主程序中的一些代码,比如wxFrame,在wxLua中是不认识这种封装的,两种不是相同的类型。所以现在只能精心挑选一组必需的,常用的代码来用SWIG封装,现在让它生成的包括scintilla和scilexer以及wxcintilla的声明后,生成的文件有近30000行,编译要花不少时间。说起来,我应该再仔细研究一下SWIG的用法先。
  这两天用wxWidgets,有时候感觉它比MFC、VCL要灵活,比WTL要易懂。这也许很偏面。不过我最不满的是它的资料太少,以及运行效率不高。
  

2009-10-29

继续改进插件扩展框架

  这两天又用wxWidgets,不得不感叹一下,资料实在太少了,只有一个现成的manual,其他时候就只有看看CodeLite、Code::Blocks的源代码了!
  到今天为止,修改了脚本扩展的功能,可以在一个描述文件中定义多个扩展的信息。对于主菜单来说,倒是勉强够用了,不过当时因为想让描述文件中对扩展的描述尽量通用,将其他的信息都写到脚本里去了,现在看来如果要对工具栏也使用脚本扩展,那么这种方式实在太不方便了点,还是应该把这种静态配置信息的都放在xml格式的描述文件中,脚本中应该只有动态的逻辑。所以还需要修改。
  今天又忘了,wxWidgets的程序如果使用Lua扩展,而扩展又装载wxLua的话,wxLua的二进制文件不能用和wxWidgets程序相同的wxWidgets二进制动态链接库,不然会出现各种奇怪的问题。这是让我目前比较头痛的问题。我现在是用MinGW来编译wxWidgets和相关工程,那么一来wxLua就只能用VC或其他编译器编译了,但我今天试了OpenWatcom和Borland C++ 5.5,连wxWidgets都编译不过,郁闷!

2009-10-26

又一年重阳

  今天是重阳节,据说要登高,这个习俗我是去年才知道的。突然想起去年的情景。
  去年十一的时候嫌无聊,开始参加网上组织的户外活动组织,一个月要出去2-4次,而像重阳登高这种是纯娱乐休闲级别的,那次是下了班转了几次车最后还是打车去的集合地点的。
  时间过得真快啊,就这么一年过去了!

2009-10-24

提交了100多个网站

  这是这三天来忙碌的成果,其实实打实只有近一天的时间是花在这个上面了。前天差不多提交了50个,昨天休息了,今天则是差不多提交了90个左右。本来说是一个150的列表,其中有重复的,有需要back link的,有类型不合适的,还有其他一些原因的,最终没有提交成功的,所以估计最后的总数在140左右。
  今天开始就新的内容了,嗯!

2009-10-22

提交也不很麻烦

  原本我提交的时候,都是老老实实一项一项地填写软件信息的,虽然也知道有PAD,还装过个自动生成软件,但一直没用上。今天突然觉得应该多提交些网站,于是从cnsw论坛里找了批下载站的地址,开始提交,猛然发现大多数站点是只接受PAD方式提交的,于是没办法咯,只好老老实实生成各个PAD,再提交。
  不过这样下来,发现用PAD提交,既然没有软件自动提交,纯粹人肉也不是很费时间啊!

2009-10-20

投身AV事业

  昨天跟一个以前的同事在QQ上聊天,说到他有朋友是卖电脑的,问有没有什么软件可以OEM的。想到要OEM,必然要大众,要傻瓜,要新潮。于是我随便想了想,倒也不是真想去OEM,只是最后想到AV类的软件,实在是极其巨大的市场!
  于是我又发散思维地想了想,最后觉得这类型的产品是有利可图,而且网上代码和成品软件很多,开始涉足的技术难度应该不是非常大。
  唉,为了快速圈地,只好先投身AV事业了!

2009-10-19

ISTool区别大了

  今天在安装程序中添加了2个网页快捷方式,因为用的是ISTool编辑的,所以就直接用它来编译了,结果试了几次,都发现没正确添加上快捷方式,连原来应该有的其他的快捷方式都没有了,甚至发现我本来让它在所有文件复制完后,应该有个选项可以让用户选择是否运行程序的,连选项都没有了。
  真是奇怪的问题,于是在CruiseControl的历史记录中翻出最近的历史,发现还是正确的。想到这个安装脚本的区别基本没有,主要在于使用的是不同的编译工具。于是用命令行ISCC.exe编译了一下,居然是正确的!
  这ISTool原来还是自己做了一套编译功能的说?

2009-10-18

MinGW编译IUP、CD和IM

  知道IUP应该是去年的事,有个叫LuaForWindows的集合,里面就带了一个,上网了解了一下,知道还有一起的IMCDLuaForWindows已经提供了使用MSVC2005编译好的二进制文件,不过我不喜欢。我打算用MinGW编译一把。
  开始的时候我以为可以直接用自带的makefile编译。结果是我花了半天时间,在msys里编译,还试图根据编译时报的出错信息,来修改makefile。在msys编译失败后,我尝试看那个自带的makefile,以为用TecMake可以编译通过,事实又是让我那么的失望!
  最后,经过我对makefile的简单解读和分析,我决定自己写下个bjam的编译脚本。总的说来,我深信bjam是很适合用来编译库的,毕竟boost就是一堆的库。经过近一天的努力,除了那些需要依赖OpenGL、Windows Media SDK、DirectX等额外库支持的库,其他的基本都被我编译出来了。
  本来bjam是可以支持一次编译多个工程(库),或只编译一个工程(库)的,不过那种bjam脚本我还不会写,我只能分别为IUP、CD和IM各写了一个bjam编译脚本。看来还是得再深入学习一下啊!

2009-10-14

封得越来越死了

  前些天才知道,tor也是会被封的,要用tor bridge才行,至于bridge的ip从哪来,网上到处有说,也就是给某邮箱发个邮件索取来的。
  这两天发现,这些bridges也是时好时坏的,于是笨办法是不停地索取新的bridges来用,今天发现得到的ip有的是之前得到过的,也就是说可能是不好用的,只能试试之前没用过了。
  唉,封得越来越死了,无语!

2009-10-12

被LaTeX折腾了

  昨天一时好奇,从网上下载了CTeX 2.8 Beta1来,安装好后,尝试着编译原来的一个纯英文的文档和一个Beamer做的幻灯片,结果发现全都有问题。首先是编译速度非常非常慢,CPU占满。然后是原来用grphicx的地方,直接插入png的语句编译不过。最后是发现Beamer包的应用和bibtex联用的地方,最后一次pdflatex编译时,编译不过。
  速度慢倒暂时也忍了。至于插入png的语句编译不过,通过网上找到的资料,下载了个ImageMagick,里面有个convert.exe,可以将png转换成eps,而实际上这eps也只有在普通文档中是可用的,在Beamer中仍然要用png,这是让人很郁闷的地方,几方处理方式不一致,是让写程序的人很头疼的事。
  好吧,这些都是可以放一边的。最最关键的是,那个幻灯片编译不过呀!如果不编译最后一次,那参考文献就不行了。于是继续上网找办法,发现一会儿工夫有了CTeX 2.8 Beta2,嗯,实际上我是很有心理准备的,这么一个小Beta号的变更,是不会有大变动的,但仍然不死心地尝试了一下,果然不出所料还是不行的。于是在CTeX的网站上看到正式发布的2.7.1.42版本CTeX,仍然不行。期间还试过有个CTeXLive2008的集成包,呃,也是不行的!
  我相当的郁闷!于是上CTeX的论坛上发帖求助,直到今天有个管理员给了个2.4的CTeX下载路径,才算最终搞定!对于这种不了解的东西,不能随便升级了,要升级前也得对能正常工作的旧版本做好备份工作,以便及时回滚啊!
  昨天差不多搞了整整一天,幸亏现在网速快了,不然还得花更多的时间呢。被LaTeX折腾了,教训啊!

2009-10-10

读书计划

  辞职前,还计划在到年底前的3个月里好好读一下买来的那么多书,可是事实是回来这么些天了,还是没什么动静。
  那时的计划是要读一本市场营销原理的书,一本经济学原理的书,一本心理学原理的书。之后再是读一遍已经买的“图灵”系列丛书,主要是想把实现模式、设计模式、面向对象程序设计以及重构这4方面的内容结合在一起,以提高自己的水平。
  近两周来,我没基本看过一本书,我觉得应该调整一下,慢慢地看也好,每天看一点点,但要做笔记,或者写心得。这是最近让我觉得比较迷惑的地方,前段时间要写重构相关的幻灯片,可是虽然我读了《重构》,但在写幻灯片的时候却只能抄书上的内容,这是让我觉得很不爽的一件事情。我觉得心得,主要应该是自己结合原书内容得加以扩展、延伸的内容,而我现在却写不出什么东西来。在之后的读书过程中要努力改进啊!

2009-10-08

做界面真麻烦

  我到现在还是没有掌握到Windows图形界面的真正规则,只能使用各种现成的类库、组(控)件、框架来做界面。很多效果如果默认的控件不能提供,需要自绘什么的,我就傻眼了。
  昨天又花了不少时间在做界面上。本来是想在一个Pane上放在一个自绘的ListBox的,结果问题不断,困难重重。其实到最后发现所有的问题都是只要使用适当的窗口风格就可以解决,我大汗!

2009-10-06

有关GUI框架的一点事

  今天偶然发现在svn trunk中的wxWidgets已经支持Ribbon了。在更新了代码后,我还特地用VC2008编译了一遍体验了一下。总的说来,可以算是Ribbon了,但感觉没有像XTP、BCG或MFC v9.0中的那么舒服。我看的是它自带的那个sample,还带着Windows标准窗口的标题栏,没有左上角的按钮和下拉菜单,也没有右上角的按钮。不知道是sample中没实现,还是wxWidgets本身就不支持。不过我想么,用wxWidgets本身就说明是重点关注它的跨平台可移植性,就不要关注这种平台特定的东西了,包括那GDI+也是。它既然号称look and feel native,那么代价就是界面不容易做得很有个性,就只适合做些严肃题材的软件了,比如IDE。
  今天又看了下MSDN中对VC 2008 Feature Pack中对MFC界面增强的介绍,让我有点儿小小的郁闷,之前是过分谨慎,或者说固步自封了,没早点尝试这个增强库,而一直盯着XTP不放。今天看来,绝大部分需要的特性,在这个Features Pack中是提供了的。只不过现在还没仔细看过它的各个sample的代码,不知道它到底可以实现怎样的效果。

2009-10-04

太惨了

  昨天拉肚子,拉了一天,上了5次,拉得四肢乏力,全身脱水,口干舌燥,真是太惨了。好在晚上去二伯家拿了一些药,效果还是挺明显的,早上本来的时候,肚子没怎么乱叫了,经过一晚上的休息,总算也恢复了一点体力!

2009-10-02

八年

  今天去市区买电脑桌,我需要的是是圆角的那种,上面还连着书柜的,去找了几个地方,很贵,嗯,至少是超出了我的预计,不过最后还是买了一个那里看起来最便宜的一款。
  既然去了市里,就趁此机会去见一下老同学,自从高中毕业以后,至今已经八年多没见,missdeer这个id的由来的那个家伙。昨天在msn上跟她联系过,今天我先打电话到她家,是她妈妈接的电话,我却以为是她在逗我玩,娘俩的嗓音是比较接近,让我差点儿摆了乌龙,还没有多说话而说错话。
  倒是想过不少再次见面时的场景,不过现实是让我有点儿意外,她居然叫我去她爷爷家,我比较汗!路上买了点儿水果,呃,确实没这种上人家家里的经验,也没人告诉过我,不过我想有长辈么,带点儿水果应该不算失礼,也不算唐突吧。
  实际上,我比我自己想象的要不拘谨得多,这让我自我感觉良好,哈哈。不过毕竟分开得太久了,而且两人的经历差异也太大,让我觉得实在很难找到话题。
  这丫头现在瘦得不成样子,164的个子才92斤,真是皮包骨了,还说现在上海就是流行这种骨感的。看那小腿,大概真只有我上臂的粗细了,还是喜欢她以前稍稍有点肉嘟嘟的可爱模样啊!

2009-10-01

祝贺伟大祖国60华诞

  嗯,今天是国庆节,是的,我得祝贺,说实话我的心底还是热爱这个国家的,热爱这个国家中的人民的,当然这种热爱并不表示我会同样热爱这个国家的执政党。
  上午还是看了国庆阅兵的一部分直播内容,呃,我不是个军事迷,对那些部队装备和武器是一窍不通,不过这不影响我对阅兵式的观赏心情。总的说来,作为一个仅仅60年历史的政府来说,能做到目前这样的程度,真的很不错了,人渣和败类是很难免的,这需要经过长时间的修正才能慢慢做得更好!
  祝贺伟大祖国60华诞!

2009-09-23

幸亏我不是做网页的

  看到一篇义愤填膺的博文,正气浩然。又看了一下文中的链接,一篇广告帖引起的口水仗。不过倒真是看不惯那作者的作法,死不认账。又看到其他人的跟帖,我不禁庆幸,幸亏我不是做网页的!
  不过说到底,我的专业领域C++方面,应该也是有这种问题的,不过我自己没怎么接触到而已。在我准备大举进入本行业之时,有候捷在那里做了不少出色的教育和科普工作,从此弯路就极少走了,想看的书,基本都是挑公认的最顶尖的专家写的最优秀的书籍。而过了那么一小段时间后,自己也基本上具备了识别并挑选出优秀书籍的能力。
  不过今天看到那些讲什么ASP、AJAX、JS的书,我还是很庆幸,那个行业似乎比C++的混乱多了啊!

2009-09-22

图形程序设计续之自动调整连接点

  本来计划是明天才开始做这个功能的,不过因为着急着要尽快走流程办手续,只好把这最主要的任务尽早实现了。
  总的说来,基本算法是完成了,不过还是有些小bug。大体的算法是这样的:三类连接线,包括折线,样本曲线和贝塞尔曲线,其中两种曲线要都Flatten一下,最后变成一组前后相连的线段,然后将这组线段分别与矩形的4条边计算交点,把离交点最近的跟踪点作为新的连接线的一个端点。
  这里要注意的是计算交点的顺序问题。也就是说,如果是从连在矩形上的端点开始遍历那组线段来计算交点,那么应该以最后一次的交点作为最终结果。而如果是从另一个端点开始遍历计算交点,那么应该以第一次的交点作为最终结果。
  其次是中间点的调整。像折线,一个比较简单的做法,把交点与端点间的所有连接点都删掉就可以了。而样本曲线则应该调整中间的切点位置,贝塞尔曲线则是调整两个控制点的位置,以免新生成的曲线因为中间那些点的牵引而仍然横穿矩形。

代码的坏味道

  最近一直在学习Martin Fowler的《重构》,并且对照我参与的一个已经投入至少15人年,历时3年,约20万行,目前仍然在继续开发维护的项目,让我觉得触目惊心,其中的代码,到处充斥着Martin Fowler所谓的坏味道,而又困惑重重,不知道别的项目代码质量是如何的。
  下面就都只是随便举一下项目中的实际情况为例,项目是用MFC开发,使用了Codejock的Xtreme Toolkit Pro界面扩展库。
  重复代码。有3处计算MD5的实现,分别由3个开发人员完成,大概实在是这种实现的代码在网上太容易找到了。另外有一个特性,可以与另一个服务进行文件的上传、下载、更新、同步,而文件因为类型不同,做这些操作时某些细节有细小的差别,但实现中却是为每一类文件具体而完整都实现了一遍这些操作。
  过长的函数。有的开发人员就是习惯性地写出长函数。整个项目中,圈复杂度超过100的有4个函数,超过20的不知道是几十还是上百个。
  过大的类。有一个类的cpp文件,是18000行,另外有一个类的cpp文件是10000行。还有CMainFrame类的cpp文件,用Source Insight打开后,在列出函数列表的窗口中显示“Too complex to parse”。
  过长的函数列表。有一个cpp文件中共9个函数实现,每个函数的参数都超过7个,而且含义晦涩,自从原创人员两年前离职后,没人敢去动那块代码。
  发散式变化。前面提到的一个18000行的cpp文件,是一个视图的实现。如果要给该视图的右键菜单中新增加一个菜单项,并进行响应,需要修改不知多少个函数,记得曾经有个开发人员,花了一周时间都在为了一个新增的菜单项。添加代码没花多少时间,时间花在添加后,因此引发的问题上。
  霰弹式修改。有两个模块都需要一个高亮显示语法关键字的编辑功能。有一个基本的控件封装类,但要修改一些代码时,总是要很小心地去从头检查一遍另一模块的实现是否受影响。我的理解是,这个控件封装类的抽象不够通用,或者两个模块的相似度并不高。
  基本型别偏执。这样的代码在项目中不好找,不过有类似的。项目中使用MSXML操作xml数据,在各个模块的实现中,都直接聚合了一堆MSXML的接口指针,操作xml的方法,和业务逻辑、界面响应完全混合在一起。
  Switch结构。很多处又大又长的switch结构。
  冗余累赘类。有两个(派生)类过于考虑以后的扩展性,而那种扩展性的需求至少在未来2、3年内是遇不上的。
  夸夸其谈未来性。有一个快捷键处理模块,从项目刚开始就已经实现完成,但后来一直没被用过。项目没有开始实际编码前,超过5个人,花了2个月制订了各个模块需要暴露的COM接口,结果到现在3年了,真正实现的接口也才10个左右。
  中间转手人。CMainFrame类已经成了各个模块用来转发消息的场所。一个重要的原因是界面与业务逻辑耦合,很多业务处理需要MainFrame转发到相应的界面实现类中进行处理。
  狎昵关系。无论是各个Pane还是MDIClient,都与CMainFrame存在着这种双向依赖关系。
  异曲同工的类。两个有交互的模块,居然各自定义了一组数据结构,用来描述现实世界中的同一种事物,中间又由CMainFrame来完成这两组数组结构之间的转换。
  纯数据的类。很多时候,为了向线程函数传递一些数据(超过一个DWORD的量),就专门定义一个纯数据的类。
  被拒绝的遗赠。两个平行的模块,一个类是从另一个类继承过来的,而明明有很多那被继承的类的功能,在派生类中是不需要的。呃,被继承的就是那个18000行的类。另外还有那两个需要编辑功能的模块,曾经居然也是一个类从另一个类直接继承,导致在派生类中变成不需要什么功能,就加些代码,把那部分功能屏蔽掉。
  过多的注释,有一个开发人员,喜欢在自己编写的函数开头部分写上几十行注释,呃,全是算法描述和伪代码。
  在公司4年,我参与过的略有规模的项目,除了这个外,另外有一个,基本是独自一人完成,代码量最高峰是7万行,后来路过不断的重构,在仍然有新特性增加的前提下,代码量缩减到4万多,现在回头看来,这个项目中代码的坏味道似乎少一些,但质量却也不行,崩溃经常发生,其他业务逻辑有问题的也不少。
  所以,我就很是困惑啊,别人的项目是怎么样的情况?

2009-09-21

图形程序设计续之添加子对象

  今天大概花了两个小时吧,加了右键菜单添加子对象的功能。这个功能的实现,有几个部分需要关注。
  首先是图形绘制部分。这是需要修改最少的部分,因为之前的工作已经实现了子图,子对象就是在子图中添加的对象,只需要在相应的响应函数中实现添加子图,在对应的图中添加对象即可。
  其次是右键菜单动态生成。这部分也不是很麻烦,只要识别出当前点击位置是什么图形,该图形在业务意义上可以拥有哪些子对象,就可以拼装出一个完整的右键菜单。
  最后是花了不少精神的配置信息读取部分。本来配置信息都是从配置文件中读取的,因为并没有对配置文件与配置项的对应关系做严格的限制,所以原先的设计中,把配置项的信息基本上都是做成实时读取的,今天又要拿来用一次,就显得原来的设计似乎不是很方便。不过我还是没做更多的改进,仍然沿用原来的做法,实时读取某个对象可以拥有的子对象的类型信息和其他描述信息,以便于添加到右键菜单和后面的创建子对象 。
  今天的任务基本完成。数了一下代码量,这个项目总共才4.7Kloc,挺少的,很出乎我自己的意料之外。这么算来,假设我在这个项目中已经投入20天,那么有效产量大约是230行每天,比自己订的目的500行每天少了一半多啊!

2009-09-19

图形程序设计续之子图

  这次的需求中提到,能为一个图形关联一个子图,用该子图来描述该图形的内部结构信息。展现给用户的语意是,当双击一个图形时,则展示该图形关联的子图。
  今天投入去实现该特性,基本没有遇到什么阻碍,因为原来的架构设计可以满足这个需要。当初把用户界面视图绘画层,图形管理器,图形对象三部分独立开来实现,所有用户操作都从视图绘画层接受,将操作信息传递给图形管理器,再由图形管理器转发给各个图形对象进行响应。在那之后,我曾经考虑过,这么做是不是有点儿过度设计,这个图形管理器的操作响应转发功能是不是多余的。现在却发现,这样的实现才刚刚好,一个子图,对应一个图形管理器,视图绘画层要始终跟踪当前起作用的图形管理器对象,任何时刻都只跟一个图形管理器进行交互。这样,没花费多少力气,只是增加了创建图形管理器,维护图形对象与图形管理器的对应关系以及销毁图形对象和图形管理器的功能,就可以实现子图功能了。
  在实现这个功能时,有一个小心得,及早加入dump文件捕获功能很有用的,因为即使是开发阶段,也不是每次都在调试器中运行,所以当在自测试时偶然出现的崩溃现象,可以及早通过dump文件进行定位,而且因为是Debug版本,所以通过dump文件分析出的结果往往比较精准和明确。
  除此之外,还发现一个以前没想到的现象,原来MFC中,CDocument类的OnCloseDocument方法,居然会销毁自己,及其派生类对象,如果要做些扫尾功能,放在派生类的OnCloseDocument方法中时,一定要把调用CDocument的OnCloseDocument方法放在最后。

2009-09-18

std::copy_if?

  几天没有正儿八经写代码了,今天又咬牙写了一会儿。在完成一个新功能后,开始重构原来的代码,把其中用于BOOST_FOREACH的地方都检查了一遍,看能不能重构掉。在刚有BOOST_FOREACH的时候,我还是觉得它很简单很方便很易用的,也许是当时觉得写for循环来迭代遍历容器步骤繁琐了一点儿。自从用上了boost::bind和boost::lambda以后,渐渐习惯了作用STL中的算法来操作容器,到现在,看BOOST_FOREACH都觉得很不舒服。
  今天看到的几个BOOST_FOREACH,最后被我改得只剩下一处。其中有一处,需要用copy_if算法,而STL中明显是没有这个东东的,看《C++标准程序库》一书说的,如果要这样的功能,得用remove_copy_if,它是copy和remove_if的结合体。结果我开始用的时候没有仔细看它的使用说明,拿来便用,运行结果总是和我预期的刚好相反。我一开始总以为remove_copy_if,是先像remove_if那样,把满足op为true的元素都移动到容器的最后,然后把这些元素都copy到另一个容器中。实际上是我想差了,应该是它会把源区间内的所有元素都尝试copy到另一个容器中,在copy过程中会把满足op为true的元素剔除掉。我大汗,这个算法的设计实在不好,我就觉得很纳闷,当年那帮大佬们怎么硬是不提供copy_if呢?
  回到家看了看boost sandbox中的algorithm库,里面已经实现了copy_if,以及其他几个很实用的算法,看注释似乎是作为TR2存在的,可能会加到C++0x中,但那实在是遥遥无期啊!

2009-09-17

几根白头发

  中午去吃午饭,楼梯上遇到王同学,依然是一脸茫然困顿的样子,问我去不去吃KFC,我大汗,这个时候选择去吃KFC还真是意外,问她为什么,说是要去洗头,只好去那旁边的KFC店里解决午饭了。而我原本是去食堂的,根本没带钱出来,两手空空,王同学只带了30元,外加几张KFC的优惠券,两个人就这么冲过去了。
  花光那30元钱,外加一些她车上的几元零钱,基本心满意足地吃完KFC,又让王同学带我去洗头。洗完头,吹干,看起来舒服多了,这几天台风来,气温降,早上都懒得洗头洗澡,确实脏了点。猛然发现自己头顶好几根白头发,恐慌,恐惧,恐怖!

2009-09-16

热替换不成功的bug

  升级程序中有一个热替换功能,呃,这个热替换,其实是我自己发明的词,意思是如果EXE、DLL之类的文件正在执行,文件是不能删除的,那么升级时也要能被替换成新版本的文件,而不需要相应的进程退出,这就是所谓的热替换。
  之前也一直陆陆续续有bug报上来,说热替换不成功,但有时候又是成功的,于是也一直没放在心上,把它归结于Windows这个API可能有问题,当然更可能是自己用得不对。直到昨天自己再一次调试时,发现一个诡异的现象。升级程序的可执行文件叫updater.exe,其中加载了dbghelp.dll,当我升级这两个文件时,必然需要热替换了,结果发现,在文件下载完后,替换确实成功了,dbghelp.dll确实已经是新版本的了,可是当升级程序在弹出提示框,提示用户升级完成后,用户点击确定,提示框消失,那个dbghelp.dll文件又诡异地变回旧版本了!一连试了几次,都是如此,简直就是灵异事件了。
  当然,不可能真是灵异,这种单线程的逻辑调试起来还是比较简单的,单步跟踪了一会儿,就发现,原来在在提示框消失后,会调用一个结束处理过程,这个过程中首先会判断当前是否正在升级,如果是,则中断升级过程,并把已经替换掉的文件还原回去。而刚好这段代码写得有问题,把是否正在升级的标志置位,放在调用这个结束处理过程后了,于是总是会发生回滚还原的操作。问题的解决很容易,只是调整一下两个过程的调用顺序就可以了。

2009-09-14

修改升级程序

  今天花了点时间在修改升级程序。这个程序完成后,一直没有进行过有效的测试,而是直接给别人用了,呃,这里的别人指的是部门内的其他有项目升级需求的同事。实际上,至少有3个其他项目中使用了这个升级程序,但我自己却没有真实地体验过。总的说来,这个程序在功能上,基本能满足当前的绝大部分需求,从当初的质量部的地个项目中独立出来后,一次大规模重构,代码结构作了不小的调整,当然这调整的原因是原来的架构已经不能适应后面增加新特性的需求了。
  曾经倒也是规划过一个比较长远的版本计划,呃,也许不能称之为版本计划,称为需求实现计划更合适一些。不过后来还是剩下几条没有实在,因为没有工作量分配过来了。今天的投入则是现在发现,在某些情况下,它会崩溃,这是最不能容忍的致使问题。不过很无奈的是,原来没有使用google breakpad来生成dump文件,现在加入了这功能后,暂时也没能把它弄崩溃,于是也压根不知道问题出在哪里。只能听天由命了!

2009-09-13

呃,csdn blog expert?

  今天,也是无所事事地点开在csdnblog来看,并不特意是为了看什么,只是一种下意识的行为。惊讶地发现,居然成了博客专家,我狂晕!就凭我那一年有数的几次心血来潮,偶尔想到了,才会用Windows Live Writer顺便把文章发到那上面去,到目前为止,也总共才8w多的访问量,其中不少还是自己点的,排名居然也有1500。自从确定在blogger上放置blog以来,对那的关注度实在很少很少。
  由此可见,csdn真是没落了啊!想当年,大一上学期,几乎天天跑去网吧,混迹于汇编和C/C++板,下学期买了个电脑后,开始学习C++Builder,就在C++Builder板混,当时的技术氛围多浓厚啊!可是现在,那些已经消失N久的id,也早已从记忆中淡出!
  呃,csdn blog expert,还真是让我觉得有点尴尬的称号呢!

2009-09-12

想去北京

  不知怎的,昨天我突然想起北京这个一直只在书报、电视、网络上才能得到些许印象的城市来。
  想到是不是该挑个什么时候去北京玩一下。想想在北京,认识的人还真不少,有中学同学,有大学同学,有以前一起灌水的网友,有以前的同事,甚至还有关系比较奇怪的,当年帮人家做毕业设计的,呃,勉强可以算是朋友吧。
  想到这些人,就更想去那里旅游一下,顺便想去爬下长城,逛逛故宫。想做的事情还真多啊!

2009-09-09

证明自己在这个世界存在过

  昨天上午跟大领导谈了后,基本确定下来辞职的事实。这2周多来,几次同领导们沟通,都被问及到底是什么原因要离职,我都一直以身体抱病需要休息,家人意见要我返回老家以及失恋之后精神不济,工作状态极差等等这些事情做为理由。嗯,其实我说的都是事实,当然也许还有一些没说出口的原因,毕竟作出一个决定,尤其是对自己来说是个关系比较重大,影响比较重大的事情,肯定是有很多原因掺杂在一起,只不过有的比重大些,有的小些而已。
  昨天中午跟王同学在食堂吃饭,我兴高采烈地跟她讲我怎么和领导沟通,我要怎么处理剩下的事情,以后我有什么理想等等,荣荣就说我好兴奋啊,还真是脱离苦海了。兴奋只是暂时的,之后便是接踵而来的麻烦事情需要处理。
  星期天在江江家打牌时,看到一个电视剧,里面一个坐在轮椅上的青年男人歇斯底里地大喊“我受够了,我什么都没有,我没有钱,没有健康,没有爱情……”,沉重地拨动了我的心弦。那不正是现在的我的真实写照吗?记得前些日子跟王同学在QQ上聊天,她还说“可怜的胖胖,出来几年,带着一身病回去了”,我当时可真是郁闷坏了,我可不希望这样被人同情,被人怜悯!但那却是事实。当在电视上听到那几句嚎叫时,我的心情很压抑,不过好在我马上想清楚了,我跟电视上的人是有区别的,最大的区别就是我没有绝望。我仍然对自己的未来充满了希望,尽量到目前为止,情况并不乐观,但我的选择是挣扎,而不是放弃。我不过分乐观,保持着一点应有的悲观和警惕,却不失强大的自信。我有的是远大的而自认为还算高尚的理想,并且坚信自己一定能通过努力在这条路上走下去,直到收获成果。
  这些日子的所见所闻,加上和人讨论得到的启发和自己的思考。我略有点惊讶地发现,原来自己还是有点儿事业心的,只不过跟旁边的大多数人都没取得过一致意见而已。就说那天去江江家打牌,事后回想起来,真觉得有点后怕,人多了还可以一些打个牌,聊个天,要是两个人这么静悄悄的下午呆在家里,可以做些什么?还有上次,小思宇来深圳,把我叫去一起去小妞家里,那第二天下午,4个人打牌打累了,就一个一个倒在客厅的沙发上睡过去了,我也觉得后怕。那不是我想要的生活,至少不是现在的我想要的生活,那太缺少激情和活力,懒洋洋而无所事事是我很害怕面对的状态。我希望在自己有精力有能力的时候,做些事。难得活一次,做一回人,总得做点事情,留下点痕迹,证明自己在这个世界存在过……

2009-09-07

今天面了几个人

  也算是体验了一把面试官的感觉,好好玩哦,看着有些人紧张的样子,想想当年自己在学校时那紧张的情形,只能感叹时间的匆匆流逝!

2009-09-05

用Beamer作幻灯片后想

  昨天在公司里说到,以后要多作培训,多作交流,这种事情我倒是有点儿兴趣的。培训、交流,那么幻灯片是必不可少的,于是回来后就开始捣鼓Beamer。
  因为就我了解的用LaTeX做幻灯片就只有PDFScreen和Beamer两种方式,而看过实际效果后,更喜欢Beamer生成的那种。以前也用Beamer做过一个非常简单的,这次我想好好研究一下,可以做出一些比较好看的效果来。
  经过昨晚和今天上午的折腾,基本掌握了一些常用的用法。总的说来,还是比较满意的,虽然速度上比较起PowerPoint这种工具慢了不少,但心中还是比较满足的,主要还是熟练程度上的不足,以及缺少良好的工具支持(呃,就是直接拿UltraEdit编辑的)。所以更坚定了我要做一个TeX代码编辑器的想法。
  因为这半年来使用MediaWiki的粗浅经验,让我对TeX的用法有了更多的理解。这所谓的“所想即所得”实在只是为了就对Word之流“所见即所得”才勉强杜撰出来的词汇。在我看来,这个TeX的设计思想,就是典型的西方人的思维方式:懒散、直接、天马行空。对于几千年来接受着严谨、务实、勤勉思想熏陶的中国人来说,一时间还真有点转不过弯来。在TeX中用这样的描述方式:我把这些文字作为标题,把那些文字作为章节名,还有些其他的就是正文,而至于标题、章节名和正文应该用什么字体,什么大小,什么颜色,什么对齐方式则不是当前我应该关心的事;而我们往往已经习惯了这样的做法:我把这些文字设置成黑体四号,居中对齐当作标题,那些字是楷体小四,左侧对齐,当作章节名,还有剩下的就宋体五号了,首行缩进两个字符,就是正文该有的格式。
  就这么对比着来看,所见即所得容易催长用户做出花哨的排版,用户放了较多的精力在如何定制排版格式上;而TeX的所想即所得则比较适合于循规蹈矩的排版任务,在排版过程中可以少费脑筋。这也就是我说西方人的懒散、直接、天马行空的原因。由此也更好理解,为什么Nokia的手机里没有树型结构的文件系统,全靠文件名来索引;为什么wiki也是这样只靠文章标题进行检索;为什么国外品牌的PC机硬盘只有一个分区了……

2009-09-02

铁打的营盘流水的兵

  在食堂跟江江、bobo一起吃过晚饭,晃晃悠悠走向F3,没想到在楼下遇到疯丫头,最近见到她有点多啊,前不久教授来深圳,一起吃饭时就见过一次,再之前是马姐姐召集大家吃饭,也见过。她在这边刚开完会,于是就和她聊起来,我告诉她我要辞职回家了,她倒是表现出一点惊讶的样子,比较有趣的是她也跟我说一个“绝密”的事,她老大同意把她调到北京去了。
  这让我越来越觉得用“铁打的营盘流水的兵”这句话来形容我司实在太合适了,真是半军事化的管理,连里面的人这方面的行为都很半军事化啊!想想我进公司这4年来,陆陆续续离开(不一定是离职)的人中,有多少是跟我比较熟的。雨烟离职去了北京,跟老公一起走的;教授是最近才离职的,不过中间也是几经波折,现在是去tsinghua读博了;骨狗是个比较有趣的家伙,离职前的告别邮件还是中英文双语版;小思宇是去巴基斯坦陪彭彭的;孙同学没离职,却想方设法调到武汉去嫁人了,瀑布汗;还有一些不是很熟的同事、同学离职的;剩下几个准备行动的,这中就包括疯丫头,猫猫也许算一个吧,一直嚷嚷着要去南京;小丫头说是年底会去上海,也算是一个吧,也是一直吵着要离职的家伙……总之,大的趋势看,都是往着离自己老家近的地方挪,嗯!

2009-08-29

Byebye,C++Builder!

  昨天熬到快后半夜,从Embarcadero官网上把Delphi/C++Builder 2010的ISO安装镜像下载了下来,并从网上找了个一年试用的license,装好了来体验一把。
  在4年的大学本科生活中,可算得上用了3年半的C++Builder,在毕业后从事测试工作的那近2年的时间里,也断断续续地用着,所以总以为自己对C++Builder的感情可能是不容易割舍的。但是经过昨天晚上和今天晚上短暂的试用后,嗯,只是把一个C++Builder2007的工程转换为C++Builder2010,编译有不少通不过的。我放弃了,我想说:Byebye,C++Builder!编译不过的地方,我看了一下,都是因为Unicode这个编译选项引起的,我粗略地找了一遍工程设置,似乎没有可以修改这个选项的。说实话,我感觉最好的版本是C++Builder6,后面跟Delphi合并使用一个IDE后的版本,差不多每个大版本都装过,但都没有让我继续用下去的念头。
  出2009时,我曾自己安慰自己,彻底放弃它了,这回有了2010,还是忍不住弄来装了装,真的彻底放弃了,没有一点亮点!一款C++开发工具,我关注的是三方面:IDE、编译器、类库(框架)。曾经在BCB6.0和VC6.0的年代,这三方面BCB都是占据优势的,可是这之后的7、8年来,VC在大幅进步,而BCB却是裹足不前,VC现在都已经精进到几乎没事可做的地步了!
  就让它,活在回忆中好了!

2009-08-28

图形程序设计续之零碎问题

  图形编辑功能已经做了两周了,到现在为止,也只能画出矩形框,在矩形框之间用白线、样本曲线,或贝塞尔曲线进行连接。又一次发现,只有在编码实现的时候,才会遇到很多之前没有想到的各种问题,而且有些问题要解决,也是要花费不少功夫的。
  要考虑图层的问题,如何定义每个图形所在的图层,在绘制时,才得比较方便快速,而且需要可以随时修改图形所在的图层。图层的影响是多方面的,例如在绘制整张画布及图形对象时,要从底层开始绘制,而对于在某个点上点击选中一个图形对象时,却要从上到下进行遍历查找。
  还考虑随时可以修改画布的大小,因为在添加图形,或修改图形位置时,是随时都可能把图形放在一个走出当前画布大小的位置上的,这时程序应该可以自动检测出需要扩大多少。对于向右、向下方向的扩大处理很方便,直接将画布大小重新定义即可。而对于向左、向上方向的扩展,则除了重新定义画布大小外,还得把超出部分归整到(0,0)这个坐标上,也就是说,要把所有已存在的图形对象的位置都调整一遍。
  再要考虑子图的问题。也就是说,一张完整的图片,在某一时刻,只是显示了其中一部分图形对象,这部分当前被显示的可以作为一个子图来对象,而没有被显示,则是属于其他的子图。每个子图之间应该没有多少强相关的关系,最多是一个图形,与一个子图可以有包含(从属)关系,一个子图用来解释一个图形。常见的一种应用场景就是,双击了某个图形,就展示另外一张图片。这就同样要求设计一种灵活高效的数据结构来保存这些信息。
  从当前的实现情况看,每个图形对象中,都有不少成员变量,保存了一些中间数据,这些中间数据不属于图形对象的本质属性,却对于图形在特定时刻进行动作起着重要作用,从代码美观角度讲,感觉很冗余,所以要考虑如何优化,减少这种成员变量。例如对于一些状态变量,或许可以用位域的标识。

2009-08-26

把最幸福的事都回忆一遍

  昨天晚上,近12点,锁好门,准备熄灯上床,却听到手机的短信铃声响起。当时我就猜,在这么个时间,说不定是那小丫头发的,不,应该是我很肯定,一定是那小丫头发的。拿起一看,果然是小丫头,那个我曾经叫她宝宝,后来一直称呼为小乖的女人。短信的内容倒比较意外,说自己头好痛,眼睛也好痛,要爆了,却又睡不着。于是我便拨回去。
  两个人断断续续,说着从我毕业工作以来的两个人之间的几乎每一件事情。睡在同一个屋子里,一起逛街,一起去欢乐谷玩,给她买眼霜和书,帮她买数码相机和笔记本,她大队培训期间出去玩让我在家里着急,买了里芯已经烂了的苹果给她……她问我她要结婚了我祝不祝福她。我短暂地沉默后,回答当然祝福了。然后她就在电话那头哭起来,我问她为什么哭,是不是他对她不好。她说没什么,他对她很好很好。还说她最对不起的人就是我,我说我没有怪你,是我自己给你这个机会让你对不起我的。而且就算真的很喜欢很喜欢一个人,也不是一定要生活在一起的。以前听小思宇说起过,陈奕讯在一个访谈节目中说的,他都已经有小孩的人了,到现在心中还是一直放不下一个人,惦记着一个人。我也可以。
  我给她说起我妈妈,那是一个眼光很挑剔的中年妇女,我把众多女同事,女同学的照片拿回家给她看,基本上都是挑缺点的。小丫头就说,是不是她的脸那么圆,我妈妈也是不喜欢的。我解释说,你那种圆脸,我妈妈是很喜欢的,还说了脸圆圆的,屁股也圆圆的,这样的女孩子才好。小丫头说,那她最终也没能成为我妈妈的儿媳妇。我说,那是我妈的儿子没本事,没福气。说到很少见小丫头哭,其中的一次,是那年雪灾,到了机场没车回家,给我发短信,在机场一个人哭,我正在家里吃饭,我妈问我在跟谁发短信,我说了后,我妈还叫我回短信让小丫头不要哭了,不然不漂亮了,而我却说小丫头脸圆圆的,哭起来的样子很可爱的。
  还说到,我曾经在她的照片袋里看到她和一个男生的大头贴,我心好酸好痛,当时怎么那么老实,没有趁上街的机会拉她一起去拍大头贴。她说,那个男生是yaya以前的男朋友,居然让人误会了,哈哈,印象中好像也只有这么一个男生一起拍过大头贴。后来她又说到,当时我千方百计把她介绍进公司,本来是希望能离得近一点的,想不到一下就被派到成都去了,是不是当时后悔得要死。我说,后悔倒没有,就是很痛恨这个命运,想不到在命运的面前真的一点能力都没有。真的,我不但痛恨这个命运,还痛恨着这个公司,就是它把小丫头弄到离我那么远的地方去了,便宜了以后可能成为她老公的那个小子。我问小丫头,如果她还是一直呆在深圳的话,我应该还是有机会的吧。小丫头轻轻笑笑。
  小丫头说,很怀念以前在深圳的日子。呃,她只在深圳呆了不到半年。我说我也是,我很怀念她在深圳的日子。
  几乎把我觉得最幸福的事情都回忆了一遍,最后我说,以后不要哭了,你哭我也会难过的,你已经不和我一起生活了,那就要在其他方面有所补偿,你担负着让我们两个人都快乐的责任。

2009-08-25

图形程序设计续之困扰

  开始做那个图形编辑程序1周多了,现在遇到了瓶颈,因为原来的设计考虑得太不全面,很多很明显的问题都没有有顾及到,结果到实际编码实现到一定程度时,发现原来的程序结构有点不行了,不够用了。
  主要的矛盾在于,原本我的设计上是,每种图形,都各自响应用户操作,互相不知道其他同类的存在。而实际上这是不对的,或者说不合适的,比如连接线,在端点确定前就是要知道那个位置是否贴到某个图形上了。另一方面,当某个图形上的连接点移动时,同样得通知所有跟这连接点连着的连接线,或者是某个图形被删除时,得同时删除连接在它上面的所有连接线。
  本来所有图形对象都在一个管理器进行任务分派,现在看来,至少有得有一个类,可能还是这个图形对象管理器,做更多的事情,可以查询它所管理的图形对象一些特殊的状态,还要维护图形对象间的关系,可以是一个邻接矩阵,或一个邻接链表,反正就是一个图的表示法。这些不是我现在的主要困扰,我的困扰在于,图形对象怎么来调用这些查询状态的操作。本来图形对象被那个管理器对象包含了,另一方面那些查询状态的操作得知道所有这些对象,这样的循环依赖,让我感觉不爽!

2009-08-23

有点必要学习SQL

  话说,我并不是计算机科班出身,因此很多基础的知识都是不知道的。这不,发现SQL真是有必要要好好学习一下了。最近在很多地方用到了数据库来存储东西,于是不可避免地需要写些SQL,最最基本的SELECT、INSERT、UPDATE还是会点的,不过也仅限于在单表中进行操作。
  前两天发现一个可以显著提高在Sqlite3中SELECT性能的方法,那就是建索引。因为IDE需要AutoCompletion和Calltip功能,而用于这两个功能的数据都是存在一个sqlite3数据库中,该数据库目前已经有不少数据了,光是一张记录了方法的表,就有1万多条记录,从中SELECT是经常要做的操作,所以能提高点性能是很有意义的事。自从给该表建了索引后,至少感觉上非常明显,AutoCompletion几乎没有任何停顿就提示出来了,而以前的话,有个明显的停顿,虽然这个停顿的时间并不长,但已经能让人感觉出来了。这只是一点,让我觉得学习一下SQL有好处的地方,就让我尝到了莫大的甜头。还有一点就是,有不少地方的SELECT的条件,是从这次其他的SELECT中取得的数据,而我全部都是分成一条SELECT一次操作来进行的,我想,如果能把这些SELECT合并成一条语句,应该还是能再提高点运行速度吧。

2009-08-20

图形程序设计与实现

  这周开始投入环境组网绘图功能的Demo实现。说简单点,这就是一个图形编辑器,就像Visio之类的。以前也有过自己使用GDI画图形的经历,但那时更简单的一点,没有要求图形可以移动,所以实现时做的事更少些。虽然这次说是做Demo,但我为了以后能以这Demo为基础,继续实际项目的完全实现,在初期就做了不少事情,费了好些心思。
  首先是程序架构上,基本上是按照实际项目的设计来做的,以后也不用做大的修改和调整。界面上会有的元素,都留出位置了。花了近2天的时候,才把主界面的框架搭好。昨天又花了1天,把输入的数据源那部分功能也差不多完成了。今天才开始实现真正的绘图功能。
  这次Demo主要就是为了预研或显示绘图功能,所以这里需要投入的精力更多。项目使用MFC开发,不使用任何其他第三方的库,传统而显而易见的作法是在CView上进行绘制。本着尽量OO的原则,最先可以想到的是,每种图形元素,可以用一个独立的类来表示,而所有这些类,有一个公共的基类,在基类中声明接口。以前的设计中,我只是让每种图形类保存了各自的位置和类型信息,并实现一个绘图接口。这次我突然发现,如果把相应的响应用户操作也放到各个图形类中实现,才更合理。比如点住鼠标进行移动,这得让图形类自己决定自己该进行什么动作,像一般的矩形,就可以直接将整个矩形进行移动,像折线,则可能只是在中间的折点进行移动。于是一般说来,这些图形类应该能处理鼠标的按键按下,弹起,光标移动等事件。现在觉得这样的设计是理所当然的,可在以前,我是绝对想不到这点的,也确实曾经把这些操作都放到外面统一分类处理。而且自从知道了Loki::Factory这个模板类后,对于这种大批类的操作,我有种近乎执着的热情想让它们不被任何除了这工厂之外的其他模块知道,直至不知道它们的存在。有了这一批图形类后,就需要一个管理器来维护这些图形类实例化出来的对象。这个管理器完全将这些图形类实例的创建、销毁、任务分派等与界面(CView)隔离开来。对于界面来说,图形对象管理器可以完成所有功能。
  再说代码实现方面。这次专门看了一遍GDI+的SDK,准备试用一番,嗯,不算试用,是实际用上了。MFC中使用GDI+没有任何限制,在CView的OnDraw方法中把所有图形绘制一遍即可。当然也有双缓冲以免闪烁。网上的用GDI+实现双缓冲的文章和代码都很多,但一般说来只分两种:1、标准的GDI+做法,临时创建一个Gdiplus::Bitmap,将所有内容都画到这个Bitmap上,再将这个Bitmap画到设备上去;2、GDI风格的做法,先创建一个内存DC,GDI+都向这个内存DC画内容,最后将这内存DC都BitBlt到设备上。经过我的实验,发现第1种方法的资源消耗比较大,速度感觉上似乎也慢一点,但没具体测过,没实际数据来证明。于是我换用第2种做法,网上有一个很流行的CMemDC类,使用非常方便。但等我这样实现完后,发现移动某个图形时,需要刷新整个绘图区,还是会闪烁。上网随便搜索一下,发现一个很简单的解决办法:处理WM_ERASEBKGND消息,直接返回TRUE就可以了。

2009-08-19

初步了解google-breakpad

  第一次知道google-breakpad这个东西,是一篇讲chrome使用的开源库的文章,当时也只是一带而过,心想这功能也能做成多平台的?
  最近还是因为项目的需要,原本已经有一个这种实现的,是从FileZilla的2.x版本中抠出来的,不过有些时候会生成不了dump,让人觉得诧异,还有一些是生成了dump,但最后发现栈回调的信息太缺乏可参考的价值了。我估摸着,这可能跟如何使用dbghelp.dll里的函数的方式有关系,而这google-breakpad在我看来可能是当前功能实现得最好的一个了,就打算好好了解一下。
  从svn里取得到的代码,解决方案是用于VC2005的,大概google内部VC都是用的2005吧,看到好些它的开源项目都是。直接用VC2008打开,自动转换版本后,也可以直接编译。主要看src\client\windows\目录下的代码就可以了,一般而言可以用到的有3个lib文件,分别是crash_generation.lib、exception_handler.lib、crash_report_sender.lib。如果用户程序只是为了能生成minidump,直接链接exception_handler.lib就可以了,crash_generation.lib已经被它包含了,而crash_report_sender.lib顾名思义是可以将文件发送到某个地方的,从代码上看,是通过http的上传功能来实现的,不过还没研究。另外还有一个GUI的演示程序,crash_generation_app.exe,可以测试除0异常,CRT函数参数无效异常以及纯虚函数调用异常。
  通过阅读crash_generation_app.exe的代码可以大体上了解google-breakpad的使用方法。google-breakpad将这么一个小功能分成几部分来实现,首先,它分为服务器和客户端两部分,这两部分都可以生成minidump,但应用场景不同。用户程序出现未处理异常时,被ExceptionHandler捕获到,该模块会根据当前进程是否已连接到一个服务器,来决定由谁来生成minidump。在crash_generation_app.exe中默认是会去连接一个服务器的,所以如果服务器没有问题,则是由服务器来生成minidump的。而且服务器生成minidump不限于当前进程,它通过Event得到客户端的dump请求,通过管道进行其他数据的传递。使用C/S结构的好处是,可以尽量减少对被dump进程的影响,代价则是大大增加了代码实现的复杂性。如果没有可连接的服务器,客户端也可自己生成minidump,这部分的实现上网上的所有有关这个主题的代码,基本上都是相同的。唯一有点区别的是,网上其他的代码一般只接管了未处理异常,而google-breakpad则还可以接管CRT函数无效参数异常和纯虚函数调用异常。
  在crash_generation_app.exe的实现中,使用服务器dump的方式,最后返回的结果总是说没成功,而实际是dump文件是生成了的,这应该算是个bug吧。还有个问题是,如果一开始尝试连接到服务器后,后来服务器又被关掉了,那之后的dump会全都失败,这可能是因为演示的缘故,没有仔细编写这种异常流程的处理代码吧。另外,算是小瑕疵吧,用google-breakpad默认好像是不能自定义dump文件的名字,只能指定个保存路径,最后的文件名是随机生成的uuid。
  总的说来,对于有这方面需求的应用,使用google-breakpad是个不错的选择,它做了不少工作。

2009-08-17

MinGW中使用GDI+

  昨晚坚持到1点多,做了两件事:1、验证GDI+在MinGW中的使用;2、编译wxWidgets中access这个sample。
  GDI+在MinGW中的使用,在网上有不少方法,但绝大多数是不行的,至少都有点小小的问题需要自己修改一下。我本来是没想过有这方面的需求的,也是因为最近想用用GDI+,就顺便想试试MinGW是否也可以。很快就搞定了,先到这里下载一个包含GDI+的头文件和库文件的包,然后把所有头文件解压出来放到MinGw的include目录下面,再把库文件放到MinGW的lib目录下面,其实我没用这个包里的libgdiplus.a文件,而是用reimp.exe重新生成了一个。网上有篇文章说,reimp.exe后面的参数是GDIPlus.dll,其实是错的,看一下reimp.exe自带的命令行参数说明就知道,人家明明是接受一个IMPLIB嘛,所以要跟GDIPlus.lib,之后会生成libgdiplus.a和gdiplus.def文件。这些文件准备好后,可以试着写个小程序编译一下,我就没自己写,直接试着用MinGW编译wxWidgets,带GDIPlus编译,中间会报两个错,错误提示很明显,只要打开那两个文件,把它报错的地方,定义类成员函数的签名的地方,不用加类名作用域就可以了。
  编译access则花了我不少时间,因为其中不少时间是在等待wxWidgets库的编译。如果使用默认的编译选项,最后可能也可以得到一个可执行程序,但在运行时会弹出消息框说要定义一个什么wxUSE_ACCESSIBLITY之类的宏。这时可以修改一下access的makefile,在编译命令行中加入这个宏定义。再编译时,可能到最后一步是说某些符号链接找不到。我当时的第一反应是,编译wxWidgets时没有定义这个宏,于是修改src/msw/setup.h中的定义,将这个宏的值设为1,再编译,到最后也是说找不到一些符号链接,这才意识到是没有链接相应的库,还是修改makefile,加上-loleacc,给wxWidgets和access的makefile都要加,就可以正常编译过了,运行access也可以正常工作了。

2009-08-15

昨晚遭遇小偷入室

  早上醒来,习惯性地找床头的iPhone看时间,居然没找到,很奇怪地起床,找挎包,心想昨天记得把手机拿出来了的啊,结果连挎包也不见了,越发奇怪,等到发现卧室门打开着,才开始有点焦虑,走到客厅一看,挎包被丢在靠近门口鞋架的旁边,里面的东西已经被翻过了,钱包里面大约有1千的现金已经不翼而飞了。这时我才确认,昨天晚上有贼入室了,这才稍微有点后怕,晚上有人进来我居然什么都不知道,万幸的是,只是丢了点钱和一个iPhone,至少连T43都没丢,T200也在,PSP,NDSL都在,连一起放在挎包里的Nano都还在,除了这些,人身也是没损失。
  情绪理所当然地比较差,但也不知道到底是什么心情,没有恐惧,没有愤怒,没有后悔,没有惋惜,只想躺床上睡一觉。大约的损失总共是5000RMB,却没有一点心疼的感觉,对金钱的态度自己都觉得有点不可理喻。想要钱,却不在乎钱。
  这该死的国家,该死的社会,我诅咒这个世界,却对那个小偷什么想法都没有,连骂几句,憎恨一下的念头一丁点儿都没有。
  还是得靠自己,拥有了强大的力量,才能保护好自己和对自己来说重要的人或东西。

2009-08-11

可以自动提交dump分析记录了

  今天花了近一天的时间,重新写了一遍自动分析mini dump然后将分析结果提交到wiki上的程序。通过这次实践,也确认了我原先的想法是可行的,内嵌一个IE,然后通过它暴露的COM接口,进行一些基本的操作,主要有遍历已有元素,填充TextArea,提交。只是发现这些操作,全都不是阻塞的,所以具体应用的时候还是得留心一下。
  晚上加班,看了一下一个公司的人写的ppt,介绍Erlang的,看得让我觉得有点不舒服。按我的理解来看这ppt,似乎他的意思是公司目前使用C/C++来开发电信类软件,而且有那么多的问题,而Erlang从语言的机制上就提供了不少的保障机制,可以写出更快更稳的程序来。让我感觉他在鼓吹这就是银弹。就我以为,写这个ppt的目的就不明确,或者说就算明确了,也是动机不纯,我们应该立足于眼前,如何提高公司开发人员的设计和编码水平,而不是考虑着换一种语言就妄图可以解决一堆问题。
  不过总的说来,我看了《Erlang程序设计》中的前面一两章后,就觉得这是我真的需要的一种东西,也许它在保证并发上,或者FP编码风格的处理上并不完美和彻底,但就目前看来,它应该是比较适合我的需求的一个选择。

2009-08-10

原来一直都理解错了

  嗯,一直以来都以为UTF-8和Unicode是一类的,今天才明白过来,UTF-8确实明明是多字节一类的才对啊,我的理解能力实在是太差了,唉!
  不过今天倒是也知道了,如果只是要在ANSI、Unicode、UTF-8之间进行转换,使用两个Win32 API就可以全部搞定了,不需要用什么iconv和ICU了。总是觉得微软做的东西易用性真的很好,通用软件产品很适合普通用户使用,而那些开发工具、库、API对于开发者来说,也很好用啊!
  唉,我也就只用玩一下这些容易的东西了。

2009-08-09

桌面时钟换肤

  看到搜狗输入法网站上有那么多皮肤,想到哪里可以利用一下。最后想到,桌面时钟啊,以前为了弄些皮肤来,还找了不少其他的桌面日历桌面时钟之类的小程 序,把它们的皮肤里的图片都抠出来,然后转换成自己的程序可以用的格式,还真花了不少时间。输入法的皮肤与桌面时钟的布局有些微差别,于是只好只用来显示 一下数字了,不能显示那种模拟钟面的了又花了近8个小时吧,其中还有不少代码是直接复制的。好在搜狗皮肤文件是用ZIP打包的,这比较方便,原先写的那块 解压缩的功能是调用7z实现的,那接口太繁复了,不好用,还自己特地写了个小小的dll来简化,但还是怎么看怎么觉得不爽。这次就索性全部改成zip的, 从codeproject上找了个解压zip的代码。
  刚弄得可以显示出来的时候,发现显示的字体有点问题。首先是字体很小,其次是字体显示部 分是透明的,颜色不明显。字体小我倒不是很关心,但颜色不明显就比较严重了,属于不可控问题了。后来乱试了一下,偶然发现用GDI+来写字时,字体不要从 HDC那里获取,而要直接从字体名称和大小创建一个,这样就可以了,两个问题都一起解决了。
  演示如下:






2009-08-08

兴致缺缺

  又要开始做环境设计编辑器了,几乎是全部重新做过。这次的计划是自己画,可是现在的我却兴致缺缺,想当年,嗯,也就是一两年前吧,我是多么的希望可以自己动手专心地实现一个这种图形编辑器啊。
  我现在的想做的是把自动分析崩溃报告的事情做好。不过这种事情收益有一些,不过却不是主要业务,因为对用户来说,没有多少良好的体验可以从中体现出来。唯一的好处是,开发人员可以减少工作量,嗯,这点上我倒是真有点典型的程序员风格——懒惰!

2009-08-04

x和dt偶尔还是有些用处的

  事实证明,x和dt偶尔还是有些用处的,有几个core dump记录,经过分析后,嗯,是x或dt后,发现就是在倒数第二次或倒数第三次调用某个对象的方法时崩溃的,而崩溃的原因就是因为那个指向对象的指针其实是个0,只要调用的方法引用了什么成员变量,那么对不起,保证完成“死给你看”的任务。想来,在知道用x和dt前,那些问题我估计是分析不出什么结果来的。
  今天,抱着试试看,用VC9重新全部编译了一下Scintilla,发现真可以用了。在这之前的几天里,偶然发现只要向Scintilla发送什么消息,应用程序就会崩溃,还以为是CVS里的代码有问题。
  这软件的问题啊,真是个很缠人的事啊!

2009-08-03

除了!analyze -v外什么都不行

  那些个core dump,都是我抠了FileZilla早期版本中的源代码出来,利用DbgHelp.dll的功能整出来的mini dump,本来有点迷惑的为什么查看变量内容时,老说访问内存错误,又翻了一会儿书才偶然发现,原来是这mini dump本来就只保存了寄存器和栈回调的信息,这也可以理解了,不然加上完整的内存dump,怎么可能只有几十KB大小。于是只好很沮丧地得出一个结论:对于mini dump,除了!analyze -v外,还真的什么都不行。

2009-07-31

除了!analyze -v外

  今天发现,对于core dump文件的分析,除了!analyze -v外,还可以.frame一下,再x或者dt一下的。不过也就只能这么一下了,而且不知道为什么,那时x或dt出来的对于之前调用栈中的内存,显示都是读取错误,而且有时候看地址似乎也真的不对。
  由于发现了x和dt这样的命令,于是今天在分析core dump时也稍微卖力了点,不过最后我却又不得不怀疑我这样的理解是不是正确的,因为有两个core dump到最后分析出来都是因为类指针为空而对它进行提领,而看代码实现是很不像会出现这种指针为空的情况啊!我大汗!
  还是得继续学习啊!

2009-07-30

写程序是件很诡异的事

  上午花了两个多小时,嗯,确实是两个多小时,写了个小程序,每隔一段时间扫描一下指定的文件夹下所有core dump文件,和历史数据库中的记录进行比较,如果历史数据库中没有记录,则认为是新追加的文件,一边将其添加到历史数据库中,一边则使用cdb.exe通过命令行进行分析,最后将分析结果连同那个core dump文件一起通过SMTP协议发送到notes邮箱中。
  其中遇到不少奇怪的问题。本来是打算cdb.exe将分析结果重定向到文件后,自己读一下这个文件的内容,把这个内容作为邮件的正文发送的。结果首先是发现,使用system这个CRT函数,其中是调用了cmd.exe /c这个命令行,所以如果传给这个函数的参数如果同间有空格,是要用双引号包括起来的。尤其要注意的是,如果打算执行的是一个带参数的命令行,得从头到尾一起括起来。接着我就想用ifstream的方式把文件内容读出来,然而很诡异的是,直接将其输入到一个stringstream时,只有一小部分内容读出来了;通过getline一行一行读出来后,本来打算全部追加到另一个string中去的,但是就是追加不进去,最后想使用API吧,直接CreateFile,再ReadFile,内容但是全读出来了,最后通过SMTP发送这部分正文时,依旧只能发送前面一小部分。放弃了,本来就昆个Quick & Dirty的东西,于是还是直接把这个文本文件作为附件发送吧。也不知道是不是那个SMTP类本来就有问题,运行的过程中时不时弹出个出错信息来,什么delete指针时错了呀,堆被破坏了呀,等等等等,不一而足,实在无语得很!
  不过尽管还是会弹错误框,但手工点一下也不麻烦,先这么着吧,至少可以当成一个监视器用了。
  这两天我一直在想一个问题:是不是mini dump只有一个用途——!analyze -v?没有找到答案,但翻遍所有的能找到的参考资料,似乎这个猜想是真的!

2009-07-29

炮轰Boost build

  今天在Boost的开发maillist上看到有人在抱怨Boost Build系统的不好用,我难得耐下性子来看了看,其中提到好几个问题:Getting Start文档中没有明确的说明命令行;编译MPI库需要修改user-config.jam文件;与ICU一起编译Regex库会报静态链接和动态静态不兼容的错;解决了Regex编译时链接的问题后,用的ICU lib文件名却需要自己修改一下;编译Graph库时链接Expat时需要修改jamfile.v2文件来修改链接的lib文件名。除了MPI的问题外,其他的我也都遇到过,也都差不多自己解决了,我还真是个可以逆来顺受的人,虽然觉得很麻烦,但却想不起要求Boost来改进这些缺点。不过今天看到这个帖子后,感觉大快人心啊,哈哈!
  顺便提一下,今天我找到可用通过cdb.exe来直接使用命令行对dump文件进行分析的的方法了,可以直接将分析结果在控制台上输出,这样就简单地利用管道将分析结果获取到了。这样基本解决自动分析dump文件的技术上的障碍,最多是要再自己写个小程序,可以分成两部分,分别是前端和后端。前端负责获取新的dump文件,可以是定时扫描某个文件夹,或者定时扫描email,或者通过其他的socket传输等,反正就是获取到一个新的dump,然后调用cdb.exe这个命令行,而后端则是将cdb.exe输出的结果重定向到其他地方去,可以是通过email发送出去,或者是写到数据库中,或是写到excel中,或是直接填充到WIKI上去。总之,现在怎么整都可以了。

2009-07-27

发现一个问题

  看《Windows高级调试》猛然发现一个问题,书中的例子都是对exe进行调试的,而且几乎都是知道问题重现步骤的。这跟我现在的情况可差得很远了,我只有一个core dump文件,不知道问题出现时的操作。看书上都是一直操作,直到问题出现,然后在调试器中断下来,查看寄存器,堆栈,句柄等信息,再抽丝剥茧一点点推测出问题的根本原因。我不行啊,我在源代码中加入了一个自定义的UnhandledExceptionHandler,在问题出现后,就转到那里去执行了,连最后现场都被破坏了的!
  晕!

2009-07-26

音乐方块


  昨天小妞给介绍我玩了一个PSP上的方块游戏,规则很简单,只要把4块或更多的同色方块组成正方形或扩展的长方形,就可以消去这些方块。这个游戏的很美妙的背景音乐,还有绚烂的动画。在小妞屋里玩了差不多一个小时,虽然规则简单,但要玩好实在不容易。
  于是回到家,我自己去网上搜索了一下,原来叫《音乐方块2》。它由Q Entertainment公司门户的水口哲野出品,可玩性角度讲很适合mm玩,嗯,这也跟实际相符合,毕竟就是小妞介绍我玩的。
  在家我也玩了一阵子,感觉比我原来PSP中已经安装的那些游戏要吸引我得多,看来PSP也不是一无事处啊,哈哈,原本都只是在像候机之类的时候打发无聊游戏才玩一下的。最近拿出PSP也是上次小思宇来这儿过夜,她拿来玩了一会儿炸弹人,后来还说搞得她一晚上没睡着,哈哈。以后得多多发掘一些有趣好玩的游戏啊,包括NDS上的。

2009-07-25

让时间来化解

  今天疯丫头发邮件来,随便聊起各自的近况。这个性格柔弱却有点儿自己的小固执的姑娘,看着就是那种让人忍不住想欺负一下,却又忍不住要好好疼惜的人。想起以前一起玩耍的日子,还有她对我的各种大决定的意见,有时候让我觉得有点儿困惑。
  也许我在她眼中真的就是一个小弟弟,偶尔在她的面前会表现出一些小孩子脾气来,也许有时候我在她眼中又是一个男人,站在女人对面的男人,希望能做些理应不该由女人担当的事情。
  原本被我深深压抑在内心底层的那些亘古遥远的回忆和情绪,又都活络起来。也许唯有时间可以化解这一切。

2009-07-24

《Windows高级调试》

  书在桌上摊开好几天了,不过一直没有投入进去用心读。今天似乎也有点闲了,于是认真地看了一会儿。这本书在去年《软件调试》上市做宣传的时候就已经听说了,还花了点时间去网上找过英文的电子版本,尽管后来发现公司网上很早前就有人发过了。找来英文电子版也就纯粹是满足一下心里那种奇怪的怪癖吧,而从来没有开始看过。
  今天读来,发现《Windows高级调试》和《软件调试》基本上两本没有交集的著作,但在一定程度上却是互补的。《软件调试》着重的是原理和底层设施的介绍,而《Windows高级调试》则注重实践,应用各种现有的软件工具,调试各种软件问题。由此联想到另外一本书《Windows用户态程序高效排错》,该书则是案例形式,内容显得比较零碎,不如《Windows高级调试》那么系统化。
  最近几个月来,在负责项目组内的core dump文件分析工作,由此才对该领域开始重视。一直以来,都是依靠WinDbg的!analyze -v命令进行分析,如果它能打印出最后的调用栈,而且调用栈又能比较明显地指出出错的源代码行,我才能连猜带蒙地得出一个定位结果并以此指导问题修改。但这样的好事并不多,大概还有3层左右的core dump直接靠!analyze -v是得不到那么明显的信息的,于是对这方面的技能要求就高了。
  今天又从一个同事那里得到一个ppt,里面是公司一个传说中的core dump分析组的演示,原来还怀着比较崇敬的心理希望能从中学到那么一招半式的,结果从头到尾翻了一遍后,发现他们就是直接调试的debug building,让我比较失望,同时更是有些不屑,不过如此嘛!
  最终还是得靠自己呀,总之看了《Windows高级调试》一书中对于“栈”的调试那部分后,我隐约觉得,在这书的指导下,加上这么多的实践机会,应该能让我在分析core dump的能力得到很大的提高。

2009-07-19

MinGW升级

  前些天鬼使神差地换上了MinGW官方的4.4.0Release,当时也没怎么想什么后果,只是觉得这4.4.0无论是在语言上或者库上,都向C++0x靠近了不少,就应该拿来用用。昨天兴起,启动了CruiseControl,却发现其中一个用到了wxWidgets的工程构建不通过,依次修改,修改好几次,才终于搞定。
  换编译器是件大事啊,boost要重新编译,wxWidgets要重要编译,这些都是比较正规的使用了C++的库,最近一次出错,是发现我的工程在链接时找不到一些符号,而其中一大批符号的名称中都有sjlj字样,想来是异常处理机制的问题了。上官网看了一下release notes,原来它把默认的异常处理机制从sjlj换成dwarf-2了,而我原来用的TDM-GCC是默认用sjlj的,所以只要全部重新编译工程就可以了。传说中,这个dwarf-2在没有异常时,是0开销的,不过TDM-GCC的网站上说,用dwarf-2前,要先仔细确认一下,该工程会链接到的那些DLL中如果要抛出异常,是用什么机制的,除非能保证它们也都用dwarf-2的方式或者干脆不抛异常,不然还是老实点用sjlj方式的好。不过我想,我在这方面的顾虑很小吧。

2009-07-18

看到Chrome的可执行代码差分算法

  无意中在一个讨论组中看到Chrome的新differential算法Courgette(http://dev.chromium.org/developers/design-documents/software-updates-courgette)的讨论,其中提到一个Develper Channel从190.1到190.4的升级例子:完整升级包:10,385,920字节,一般bsdiff算法:704,512字节,而Courgette算法:78,848字节,近十倍的空间效率提升啊!真是太恐怖了,直到今天我才知道,原来对于二进制补丁,也是有这种优化算法的,汗啊!http://neugierig.org/software/chromium/notes/2009/05/courgette.html 这篇文章也对这个算法进行了介绍。大体上,Courgette算法目前是基于x86体系的,适用于exe、dll等可执行文件补丁,它会将这些新旧版本二进制码进行反汇编,在反汇编的基础上进行比较,据说是将二进制文件中所有的internal pointer/reference的地址重新符号化, 然后再计算differential,得到补丁。
  另外一个博客中也有一篇讲优化二进制补丁算法的,http://blog.csdn.net/housisong/archive/2006/04/11/658863.aspx。
  由此我想到我们现在的自动升级程序,都是完整文件下载替换的,土了啊!

2009-07-15

Loki::Factory挺好用

  这两天在做一个特性,其中一段很经典的根据不同的类型id来创建不同的对象的代码,想到了《Modern C++ Design》中的那个实现,马上找来Loki用。
  非常简洁的代码,定义一个基类,然后就可以根据类型id来创建对象了,如果创建对象的时候需要一些参数,也很容易,另外定义一个Loki::Seq就可以了。
  每个派生类可以在源文件中用一个匿名的namespace保护起来进行注册,这样就彻底达到了只要加入该源文件,就有该类,不加入该源文件,就没有该类。不需要修改其它任何代码。
  挺好用的!

2009-07-13

AST

  做IDE,很重要的特性是对代码进行分析,并根据分析后的结果做些其他的事情。
  出于好奇,在google上搜索一下python、ruby、lua的相关资料,只要以AST为关键字进行搜索,就能找到一些信息。其中,Lua有好几个第三方的项目,专门或顺带实现了AST操作的功能。Ruby的也是第三方的项目实现的,Impeller就是修改了某个第三方实现。Python则比较强悍了,由官方标准库中提供该功能。
  虽然这些脚本语言的核心解释器都是用C实现的,不过这些AST功能的实现一般却是直接用脚本完成了,这也是可以理解的,或多或少地可以利用核心解释器中的一点功能嘛!

2009-07-11

PCLint应用尝试

  本来我一直是觉得VS2008自带的Prefast在这方面的需求足够了,不过似乎老雷对于PCLint有着近乎宗教般的狂热信仰和崇拜,时不时地催促一下。
  不过在公司里的项目中,折腾了不少时间,仍然没能正常使用,总是要么只输出一些头文件中的检测信息,要么就索性什么都没有输出。后来同事发现,这似乎是跟我们的代码包含的头文件关系太复杂有关,他从其他项目里随便找个cpp文件来,用PCLint就是很正常地检测了cpp中的问题。
  昨天晚上回到家,从网上找到PCLint v8.00x,并从官方网站上找到最新的适用于v8.00的.lnt文件,然后开始PCLint的再次尝试。惊喜地发现,自己写的一个MFC小程序用PCLint确实可以正常输出些内容。但想想公司里的另外一个小项目,也是很简单的头文件包含关系啊,为什么就不能正常使用呢?最终还是没有找到确切的原因,太奇怪了,网上随便搜索了一下,关于PCLint的问题定位方面的资料几乎没有,全是些基本使用说明。
  比较了一下,PCLint的速度真是慢得可以,似乎比Prefast慢很多很多。
  在试用了PCLint和Prefast后,我发现它们对提高代码质量有客观上的能力和辅助作用,但我觉得人才是最主要的因素,我在短短的试用过程中,突然醒悟,很多时候是纯粹为了通过它们的检测而机械地修改代码,怎么能躲避它们的检查就怎么改,而完全丧失了作为一个有责任心有进取心的开发人员应该具有的致力于编写出优质无错的代码的积极心态,这实在是一件很恐怖的事情。再联想一下,对于衡量代码质量的其他静态指标,比如圈复杂度,重复代码行统计等,也有相同的问题。为了降低某个函数的圈复杂度,就无所不用其极地运用一些诡异手法,只是为了骗过工具的检查,反而降低了代码的可读性可维护性。
  程序员啊,真是一个让人头疼的群体。

2009-07-08

该死的_SECURE_SCL

  昨天开始,就有人发现最近的版本不正常,严重不正常,甚至都不能启动!这是个极端致命的问题啊,于是开始有同事着手定位。昨天没有进展,顺延到今天,今天上午也没有进展,下午的时候人都慌了,于是3个人投入进来定位问题。最初只是通过dump文件发现,在启动程序时,会自动加载文件,并通过正则表达式分析该文件,而这正则表达式使用的是boost::regex,可是在创建boost::regex对象时,它的构造函数就崩溃了,没有抛异常。那大量的时间就花在分析为什么构造函数会崩溃上。而且比较诡异的是,Debug版本是没有问题的,只有Release版本才不正常。于是全部人都切换成Release进行编译、定位。直到很后来,另一个同事发现,只要在该程序的任何处使用到boost的东西,就会崩溃!这没法活了!于是我们不约而同地想到,这应该是编译选项引起的问题。从回溯CruiseControl上的编译版本,发现最后一次表现正常的构建之后,有7次提交记录,才构建出下一个版本,范围可以缩小到这7次提交记录中。其中只有一次是我在工程设置中定义了一个预处理宏_SECURE_SCL=0,为了在VS2008的Release模式下编译过1.39.0版本boost::signals2的代码!把这个预处理宏去掉,并暂时注释掉因为没有这个宏而编译不过的boost::signals2应用代码,编译,发现一切又都恢复正常了!
  该死的_SECURE_SCL,看来只能等1.40的boost了!

2009-07-07

线程问题

  发现前段时间写的那些代码很不稳定,总是会崩溃!今天被人翻出来批判了一把,惭愧啊!
  问题出在多线程上。一个编辑器对象创建后,会创建一个新的线程来分析编辑器中的内容,然后把分析后的结果保存在成员变量的。这时如果在保存结果之前,编辑器对象已经被销毁了,则访问成员变量也是不可行的,所以我在编辑器对象被销毁的时刻(析构函数中)强行打断线程的执行。
  另一个问题,同样是编辑器对象创建后,要创建一个新的线程来分析编辑器的内容,然后根据分析后的结果,来对编辑器做些特定的操作。这里我使用了boost::signals2来进行通知,其实这不是必要的,用boost::function作为回调函数也是可以达到目的的,毕竟在我看来,使用boost::signals2的区别只在于它可以绑定多个接收槽。结果问题来了,boost::signals2的自动对象管理似乎只能依赖于boost::shared_ptr的对象生命期管理,而这boost::shared_ptr的使用在我这种情况下就比较棘手了,原来只是把该对象作为一个聚合对象实现,而似乎boost::shared_ptr的使用最好是从一开始就是在堆上new出一个来,并以boost::shared_ptr的形式保存起来,现在要改,也是有不小的工作量。说实话,在我看来,这样的接口,还不如boost::signals的trackable基类的实现好呢。而且另外还有个问题是,就算这信号-槽的机制真正正确起作用了,信号过来后,仍然是在一个工作线程中对编辑器对象进行一些操作,而这些操作进行到半途,如果编辑器对象突然被销毁了,那也是不行的,所在不得不在这个接收槽的处理过程中也加入线程中断的检测。
  这样,貌似理论上两个问题都可以解决了。于是用boost::thread实现吧,它有比较方便的创建新线程的接口,可以方便地把一些参数传递给线程函数,这真是一个巨大的进步。其次,它有interrupt接口,可以在让线程被中断,有join/timed_join接口,可以绅士地等待线程结束。似乎一切都正是为我解决上面这两个问题问题准备的!但是,问题仍然困扰着我,居然,偶尔的interrupt并不能让boost::this_thread::interrupt_point抛出异常来,而这个“偶尔”实在在是太频繁了!还有,居然,似乎,偶尔join并没有真的等到线程结束就返回了!现有,这个join在等待的时候,会有死锁的现象!
  我狂分特啊!到底是哪里出错了,而且这样的问题还都不是必然重现的,郁闷哇!

2009-07-06

《实现模式》

  这两天闲暇的时候,翻起Kent Beck的《实现模式》。Kent Beck也算是世界有名的编程方法论方面的牛人了,这本薄薄的《实现模式》买了几个月了,这回翻起,才明白过来“实现”二字的含义,这不是一个动词,而是一个名词。与设计模式相对应的,有设计,然后有实现,既然设计可以有模式,那么实现也可以有模式,原来是这个意思!
  不过我仍然没有耐着性子把书看完,只是快速浏览了一下目录以及前半本的内容。书的主题不是一个新鲜的话题,但它的切入点却是比较新鲜,居然也套上了“模式”,倒也是一种可尝试的方法。这书的目标是为了帮助程序员可以写出美观优雅,易读易懂的代码。作者把这个主题分成7部分来讲,每部分是一个大的分类,各自包含一些模式。从我自己对已经读过的部分来看,大体上作者的目的是能达到了,至少有不少条目我看了就觉得很有道理,心有共鸣,而且相比GoF的《设计模式》来说,这本《实现模式》无论是语言表达,还是技术内容,都要浅显易懂得多。
  觉得有点缺憾的是,该书中例子代码实在太少了,而且缺少对各条目的应用场景,作用,使用限制等方面的描述,不过如果加上这些内容,也就不是这么薄薄170多页可以解决了的。

2009-07-04

bjam编译不了资源

  从网上找到一份Windows下的Intel C++ v11,装了来试了试,发现居然用bjam的话,不能正确为编译资源文件生成命令行,可是试了一下它集成在VC中的功能,似乎又是可以编译过去的,看来是bjam的问题了。在网上随便搜索了一下,也没找到什么有用的信息。打开Boost.Build.v2目录中的那些.jam文件,看不懂哇!
  另外,boost也是需要单独使用icc编译了,才能为其所用,而且icc是和msvc一样可以支持auto link的,这点倒是不错。但是最不爽的是,wxWidgets似乎没有提供icc用的工程文件或者makefile啊,郁闷!

2009-07-01

遭遇VC不被Boost承认

  今天郁闷地发现,有一处代码,在CruiseControl上编译居然不过。很奇怪的是,在本地机器上编译是没问题的,而其中最大的区别就是本地以Debug模式编译,CruiseControl上则是Release模式。于是不甘心地在本地也以Release模式编译了一把试试,果然报错了。
  那处代码很简单,就是调用boost::signals2的一个信号,明明白白Boost的文档和例程都是这么写的。于是拿出Boost的Example中的Hello World来编译,果然也不行。于是切换到VC2003去测试,居然过了!
  同事通过Proxy上Google搜索了一遍,还真发现了有用的信息,居然是boost::signals2在1.39.0版本中的Bug,所说在SVN中已经修正了该问题,具体的信息可以从一个Google group的帖子上看到,另外一点有用的信息在CU的一个Blog上有提到,临时的解决方案也提到了,就是将_SECURE_SCL宏定义为0即可。因为暂时只有那个cpp文件中用到了这种用法,于是我在那cpp文件开头处添加了这个宏定义。但是这里又吃鳖了一次,在本地是能编译过了,在CruiseControl上还是编译不过,报的错也没变,说明那个宏定义没起作用。这时我就已经没有耐心再去仔细区别其中的差别了,差别大了去了,操作系统不同,编译器使用的SDK不同,鬼知道还有什么不同。最后抱着试试看的心理,把这个宏定义从源文件移到了工程设置中,终于编译过了!
  估计那帮写Boost的人,主要还是用GCC来验证的吧!

2009-06-29

同样的操作,三处代码错误

  今天下决心修改一个崩溃的问题,只要快速打开多个文件,再通过菜单“关闭所有”窗口时,就会崩溃,通过一次又一次的崩溃实验,分析每一次的dump记录,居然意外地发现每次崩溃最后的堆栈信息都不一样,总共有三处代码。
  把这三处代码仔细推敲之后,发现确实有可能引起崩溃的原因,而这样的代码如果让我仅仅是通过正常的审视,是绝对看不出什么问题来的。修改完这三处代码,再实现,又暴露出另外一个崩溃的问题,也是那些代码附近,真是汗颜啊!

2009-06-26

想起那些人

  早上听同事说起,那个杰克逊死了。当时还没什么特别的想法,心想也就一个出名的歌手吧,死就死吧。
  下午小妞发来一个mp3,名字是《Heal the World》,很好听。突然想起初中时班上一个转学生,一个身材纤细,特长舞蹈的女生,据说她的偶像就是杰克逊。那是我第一次知道有杰克逊这么个人,看到那CD封面上的照片,心中一点都不感冒,尤其不能接受他那种打扮。心中还纳闷为什么这么一个清纯的小姑娘会把这样的人当成偶像。那个女生后来在初三最后一个学期又转学了,大概因为户口之类的原因需要回原籍去中考,再后来就一直没见过了。记得在我在高一的时候,那段混乱而手足无措的日子,还偶尔听到过一两个男生在那里对她的yy,那时我只觉得这太假了,虚伪得太明显了。
  春去秋来,不知道那些曾经从身边经过的人,现在都在哪里,过着怎样的生活……

2009-06-22

sqlite3的Blob操作,变异……

  本来想当成二进制数据一样把文本内容以Blob形式存入Sqlite3的,结果整了半天,虽然最后存是存进去了,好像不是像我预想中的那样写二进制的Blob,而是就是作为文本存入了。不过倒是知道了对于大块数据的操作,在简单的SQL语句中不能方便地实现时,可以用sqlite3_bind系列API,在SQL语句中可以用问号作为占位符,然后将数据bind到各个占位符上去。这样一来的SQL语句要用statement来执行。
  存进去后,取出来也是一个问题,本来以为Blob么,二进制么,还专门从别处移植了一段代码过来,结果那段代码可能有点点问题。反正最后我也是看出来了,因为我是原样照搬地把数据存入的,那么还是原样取出来就行了,我大汗!真不知道如果哪天我真要存入一段二进制数据时,应该怎么办了!大概,存入的方式也要跟着改吧。

2009-06-20

在CI中使用bjam构建项目

  不知道什么原因,我的机器上什么VC2008命令行来编译项目,无论是devenv.com还是devenv.exe,都会占满CPU,而真正的编译进程cl.exe却一直慢吞吞地,几个文件的小项目,也不知要等上多少时间,在持续集成时实在忍无可忍。
  不过因为之前有一段时间专门学习了一下如何使用bjam,所以我就决定在CI上,使用bjam来构建项目。统计了一下我的项目的情况,有用MFC的,有用WTL的,也有用wxWidgets的,有用VC编译的,也有用MinGW编译的,无论哪种情况,bjam都可以满足需求。
  使用bjam的一个比较方便的特性是,它能比较智能地自动为不同的编译器套件使用各自的命令行编译选项,这样使得一个bjam脚本可以同时不同的编译器套件来编译。不过实际使用过程中,还是有些需要区别对待的地方,这可能是因为bjam主要用于boost的构建这个目的而产生吧。
  比如对于VC和MinGW,可能链接的库文件是不同的,要么是文件名不同,要么是所在路径不同;链接选项可能不同,也许是boost的原因,bjam在构造exe时,默认是使用控制台子系统,所以需要自己在链接选项中自行指定使用Windows子系统,而该选项在VC和GCC中有细小的描述上的区别。
  bjam的另一个比较方便的特性是,它能自动寻找编译器套件默认的头文件路径和库文件路径。这比使用makefile要方便太多了,比如在MinGW中编译一个C++,注意,是C++,不是C,的程序,需要仔细地设置引用路径,而bjam中完全不需要,只需要一行代码就能搞定:exe Hello : Hello.cpp ;这个特性对我的情况来说也是很有帮助的,比如使用MFC的项目,鬼知道它要引入多少头文件路径,还有就是有个工程设计成既可以用VC编译又可以用MinGW编译,所以这又省了不少事。
  再扯一个跟bjam关系不是很大的东西,编译使用了wxWidgets的项目。有一个小工具程序,可以方便地得到指定路径中的wxWidgets在编译时使用的选项,那就是wx-config。通过这个小东西,在编写编译脚本时,又可以省掉不少事。在http://wxconfig.googlepages.com/上可以找到Windows的移植版本,不过在bjam中使用时,会有一点点小问题,就是它的输出内容都在最后添加了一个回车,而bjam并不能让用户方便地设定编译选项在命令行上的先后顺序,所以如果恰好它的输出结果被排在命令行的中间位置,那之后的那些选项就被断开了,shell则认为这是两条命令,所以会出错。好在这个Windows移植版本提供了源代码,下载下来,自己稍微修改一下,也就是在输出的那条语句中把最后的std::endl去掉就可以了。
  VC在支持预编译头文件时,一般是指定stdafx.h文件,然后加个编译编译选项来实现。在bjam中不能这样直接加编译选项来达到这个目的,而是专门提供了一个叫cpp-pch的规则来实现。不过,对于只在CI上才执行的bjam命令,有没有这个功能都无关痛痒。
  VC有个很好用的特性,auto-link,boost就使用这个特性,这也使得在VC上使用boost比在其他编译器套件中要方便,它默认链接的都是静态库,使得发布都省了一些事。在bjam中使用时,只要设定好boost库文件的路径,其他的就不用管了。
  在使用了MFC的项目中,会有一些特定的选项,比如它需要再指定程序的入口,这些工作本来都是IDE默默地在后台完成了,使用bjam时就需要自己留心了,一般的做法就是直接看项目属性中的命令行一栏,把一些个性化的设置都提取出来写到bjam编译脚本中去。

2009-06-18

两个boost相关网站

  如果胆子足够在,这两个网站上的都是值得试一下的,其中不乏一些好东西,比如日志库就有两个,一个叫Boost.Logging,一个叫Boost.Log。
  https://svn.boost.org/trac/boost/wiki/LibrariesUnderConstruction
  http://www.boostpro.com/vault/
  对于进入boost的侯选库,有两个存放点,一个就是上面第2个地址vault,还有一个是boost svn中的sandbox目录。从Review Schedule中看来,似乎进入Review的库从vault中来得多一点,而sandbox又有点像boost正式库的一个draft。

2009-06-17

崩溃问题分析

  这两天在抽空分析程序的崩溃记录,这些记录都按照版本号分别放在不同的文件夹中。早先exe文件的版本号并没有严格对应于svn revision号,周一时用一个修改PE资源的小程序搞定了这个事情,以后做这方面的事可要精确和省神多了。
  今天分析到一个记录,印象中以前也有过类似的记录,一时没想起来,发现是一个线程函数数,对一个std::map调用find方法,结果在STL内部引起崩溃了。开始的时候,还以为是调用find方法时传入的参数有问题,引发了STL的某个未定义行为。于是另外写了个小程序简单验证了一下自己的猜想,结果不如自己所料。后来猜测要引用这个map的对象时,包含它的指针已经无效了。但为什么无效,我却也得不出什么结论。最后突然想到,从崩溃记录分析得到的最后堆栈信息中,看到是调用find方法后,find方法中取xtree的根节点引起的崩溃,那么是不是这个map对象本身就是有问题的了呢?灵光一闪,这个map对象是做为一个类的成员的,如果这个类的实例被销毁了,这个map对象自然也跟着被销毁,而这时如果再试图访问它,当然会有问题。
  分析出来具体的原因,修改起来就比较容易了,在该类的实例被销毁时,应该通知那个线程马上结束,而且不要再试图去访问那些它的成员了。在修改这块代码的过程中,我应用了boost::thread,这大大简化和缩减了需要编写的代码。

2009-06-16

最近Boost有点疯啊

  什么Boost.DirectX啊,Boost.Debug啊,Boost.Cppgui啊,越来越疯狂了哦!

2009-06-11

我晕std::locale

  今天一个同事突然发邮件来说我的升级程序在生成文件列表时,给数字都以3个数字一段都加上了逗号,我狂晕!拿自己的debug代码试了试,发现果然如同事所言,看来是确定代码写得有问题了!
  单步跟踪,最后发现我在把数字转换成字符串时,用了std::stringstream类的流输入方法,就变成那种样子了,稍微想了一下就明白过来了,以前一直都是没有逗号的,后来有过修改的地方也就是当时用ofstream写文件时,为了能支持中文的路径和文件名,在程序启动时调用了一句std::locale::global(std::locale(""));后来就有了这个问题。我狂分特啊,跟另外一个同事分享这个经验时,他还问我为什么不用boost::lexical_cast呢,其实据说boost::lexical_cast内部也是用的std::stringstream来转换的,哈哈!

2009-06-10

总有一些自以为是的傻x

  说什么无法控制,说什么循环引用,说什么编译时间,都他妈的傻x。怎么不去关心一下CPU能不能控制,Windows能不能控制,还不是他妈的那儿老老实实用着。怎么不去用一下汇编,Fortran,狗日的哪来的循环引用。怎么不去用Debug自己输入二进制码,哪来的编译时间。
  莫装B,装B被雷劈!

2009-06-08

状态不好狂出错

  今天写代码,出了不少错,都是很低级的,状态不好啊。
  先是连接一个MySQL数据库,从一个表中Select一些记录出来,死活没数据,还以为是SQL语句有问题,用其他客户端去运行,发现SQL语句是正常的。然后换了一条SQL语句,同样是Select的,发现又可以了,一步一步排查,发现是那个表中查不到数据,另外一个表中查得到数据,突然发现原来我连的MySQL地址不是我想要的那个!
  接着是从XML格式的配置文件中读取配置信息,发现有的配置项好像怎么都读不出来,一直都是使用默认的值,后来才发现在判断值是否为空的时候,多写了个感叹号!
  好不容易磕磕碰碰地调试通过了,编译了一个Release版本,用Inno Setup打了个包,放到目标机器上去运行,发现居然没反应,而且不巧的是我什么调试手段都没有,也没有什么日志。后来通过MessageBox发现,似乎配置信息读取又有问题了。然后发现如果是直接用Debug版本是正常的,于是想调试一下Release的,结果不知道应该设置什么选项,调试不了!只好通过OutputDebugString来打些信息出来。经过一番搏斗,发现原来我把装载配置文件的那条语句外面套了一个ASSERT,我狂晕!
  以我现在的水平,状态好的时候,最多一个月也就能产1w行代码,而且代码质量还不能保证,唉,怎么才能让新写的代码跑得稳定呢!

2009-06-07

准备使用Boost.Logging

  今天在Boost的邮件列表里看到有人在项目中使用Logging v2,这是在去年3月时提交Boost后review拒绝的一个库,当时我也略微关心过,因为一直在找一个好用的C++日志库,最后看到被拒绝了,我也就有点死心了,用起log4cxx来,其实发现log4cxx也不是那么好用,而且最近公司里用的时候发现似乎会有内存泄漏的问题。
  看到邮件列表里那人对Logging的评价很高,我就心动了,它有一个优点是header only,这是我很喜欢的一点。其他的灵活性之类的我也没用过,也不好评价。不过我决定了,先在自己写的这些程序中试用一下。

2009-06-03

史上最难用Win32 API

  啊,非SHFileOperation莫属!今天要实现个文件夹复制的特性,于是就想到这个API,可是很不幸,无论我怎么整,都报个1026错误!从网上找了一圈,发现代码根本一模一样的,人品问题啊!
  后来在花费了不知道多少时间后,也许是1小时,也许更多,等不及了,花了10分钟自己用递归写了个函数来实现文件夹复制,我吐血三升!

2009-05-31

boost svn中也有编译不过的代码

  回到家,习惯性地更新了一下boost svn中的代码,从来都最多只看一下哪些文件更新了,至于具体更新了什么内容,却从来没有关心过,也不是我能关心的。
  然后整了一下自己写的一些代码。
  再之后是继续整前几天放假时写的一个小程序,结果突然发现编译不过了!开始还怀疑该不会是VC出问题了吧,重启了一下VC,没用!于是看了一眼出错信息,定位到出错代码行,是boost::signals2中的一个叫signal_template.hpp的文件中。众所周知boost的代码只有超人才能看得懂,而我很幸运地不是超人,所以就看不懂啦!折腾了一阵,也没有办法,后来猛然看到这代码中明显是语法错误嘛!我猛烈地faint啊,原来超人也会犯错儿呀!尝试了一下把这语法错误修正,结果编译时报的是其他地方的错了,这我就不晓得是什么原因了。无奈之下,为了能正常编译我的程序,只好回退到之前一次提交的内容去了!
  目瞪口呆!

2009-05-30

提高软件生产力

  提高软件生产力,意味着可以在更短的时间内交付质量更好的软件产品。软件生命周期的各个环节都对此有着极大的影响,而从开发人员的角度讲,有几件事是力所能及的。
  一、提高代码产能。单纯的代码生产力,比如一个开发人员原本一天能写100行代码,经过一段时间的学习和锻炼,相同规模相同难度相同质量的代码,只需要半天就可以完成了。
  二、提高代码质量。这个质量指代狭义的稳定性、鲁棒性,代码bug少。这一方面也是通过学习、锻炼可以获得此类能力和经验,另一方向,引入合适的工程方法,比如TDD等良好的实践,也有一定的效果。
  三、发扬“拿来主义”。现在可以从各种渠道获得并使用的代码片段、函数库、类库、框架、组件、中间件、软件等等不计其数,并不是所有特性和功能都要自己着手开发实现。只要代码功能满足实际需要,质量在可承受的范围内,可维护性也过得去,以及license没问题,就拿来直接用吧。可重用的,不光是代码,还有架构、设计思想等。形式上,无论是源代码复用,还是二进制组件,都可接受,甚至可执行程序也可以。
  四、提高代码复用性。自己写过一遍的代码,尽量不要写第二遍,复用吧!代码重构一种使得代码得以提高复用性的不错的办法。
  五、提高架构可扩展性。为了更快更好地实现后续新增需求,高可扩展性的架构是一个良好的基础。可扩展性的对面是可裁减性,假如有一天突然要求去掉其中的一些特性,这种情况也是存在的。这点对经验的要求和依赖比较高。
  六、提高架构和代码的可修改性。需求发生了变更,或者原本对需求的理解就有问题,或者发现了bug,这些情况都需要对代码进行修改,别因为一个功能上的小改动而引发代码上的大改动。
  七、使用先进的工具、方法和流程。比如眼下流行的敏捷,使开发流程减少僵化。使用优秀的IDE,智能感知、重构、自动代码生成等等。使用版本管理工具,定期对一些代码进行指标度量。
  八、宽松、舒适的环境。环境好,对开发人员的身心都有好处,长期收益极大。
  九、快捷、方便的求助渠道。向人请教是一种很省事的方法,但很可能影响其中一方的状态。上网搜索则是另一条有效途径。
  当眼下软件生产力严重不足的情况下,这是仅剩的几件我能做的事了。

鼠标手势算法

  今天看到一篇文章,讲鼠标手势算法,觉得有点意思。现在不少软件都有了鼠标手势支持,尤其是浏览器,几乎成了高级用户的标准配置了。
  这算法分为4步。
  一、过滤鼠标移动动作。如下图所示,这步过程把一连串的鼠标移动动作中幅度过小的动作过滤掉。这跟系统配置也有点关系,不同的配置下,记录鼠标移动的速率等量纲有所不同。

  二、限制鼠标移动方向。如下图所示,对于简单的鼠标手势支持,只支持上下左右4个方向,就把其他方向的动作都归并到这4个方向中。一般就是比较一下上下方向和左右方向的差值,取大的那个作为最后的方向。

  三、简化移动方向序列。如下图所示,这步非常简单,原本是右右上上右上上的方向序列,简化后变成右上右上了。

  四、匹配和推演。这步是最困难的,表面是把夹杂在长距离移动动作中的短距离动作过滤掉。大概的做法是先把整个动作序列与一组预定义的序列匹配比较,如果匹配失败,就把这序列中最短幅度的动作过滤掉,再进行匹配,如此循环往复,直到最后匹配到为止。

2009-05-17

被屏蔽了

  历经约2年的安逸生活,blogger终于又不能访问了,这次的决绝,让我惊异和始料不及,这次似乎不是伟大的GFW动的手,而是人家服务器端直接自己被我屏蔽了,悲叹啊!
  命运不能掌握在自己手中的感觉,实在太不好了!

2009-05-14

口水仗

  这两天看到TL里从MFC讨论到boost,无论是MFC还是boost,支持和反对的人都有,大打口水仗,我也是个不小的愤青,也去凑合了几下。
  因为在公司里的项目用的基本上就是MFC,其中也穿插用了不少boost的东西。说心里话,至少目前我对它们没什么不好的印象,对于boost更是有种赞赏的感情,它在C++语言的基础上创建了那么一个通用的可移植的庞然大物,确实为我节省了不少工作量,这是最让我高兴的原因。
  TL上的口水仗最后也沦为抓住一丁点儿瑕疵穷追猛打,实在无聊得紧。在我看来,这么一个东西,它确实对我有帮助,也许节省了10%~20%的时间,这就足够让我继续坚持用下去了。

2009-05-11

boost::signals的应用

  今天跟一个同事讨论起在项目中使用boost::signals,发现有些情况下很适合,比如我们的程序有时候会做些耗时的操作,而且这些操作过程中可能会去访问子窗口,而子窗口却不一定继续有效,于是可能就会有问题了。原来的办法是保存好这些子窗口的句柄,然后给它发消息。但有了signals就可以稍微做得更好一点,把子窗口的类从boost::signals::trackable继承过来,这样子窗口对象销毁时,会自动把它从信号槽中移除,不再需要自己费心管理它是否有效。另外一点的好处,也是boost的各种类惯用的手段,做为回调,只要签名相同,即有唯一确定的返回值和参数列表,而不用在乎它最终是什么形式,全局函数也好,仿函数也好,成员函数也好,都可以作为回调。
  从1.39.0开始,boost中有了signals2,相比signals,它有不少改进,但在我看来最主要的是线程安全和header only。其他的用法倒还是和signals一样。不过无论是boost::signals还是boost::signals2,似乎都没有办法解决一个问题,就是Windows GUI的多线程操作。还是得另外想办法啊!
  烦躁!

2009-05-10

WTL升级

  偶然发现WTL已经出到8.1版本了,可是好像CVS里却没有更新,原来是已经切换到SVN里去了,还枉我时不时地update一下呢!
  这次更新号称是兼容VC2008,结果我升级了一下,发现原来的一个使用WTL写的程序工作不正常了,居然接口行为都改变了,晕死!原来的8.0版本在VC2008下使用也没什么问题啊!
  郁闷!

2009-05-09

总觉得不对劲

  这两天在公司里,做那个插件架构,到昨天为止,大概实现了一半了,不过总觉得不对劲。这是一个没有经过实践检验的方案,看了那些已经完成的代码,再联想一下完全完成后的可能的模样,心里老是有点不爽,担心内存占用会不会太大了。
  现在这个设计是在原来的基础上修改来的,主要的修改是在一个插件中可以定义多个扩展,插件只是扩展的容器,扩展才是真正实现可扩展性的设施。于是相比原来的设计,现在有了两种独立的对象,插件和扩展,而原来一个插件中只定义一个扩展,在代码实现时可以只定义一种对象。于是现在这种情况下,对现实世界的描述,大约会多出近一倍的信息量吧,这些信息如果全部保存到内存中,预计消耗会很大。本来这些信息是通过配置文件描述的,如果不全保存到内存中,就要能实时解析配置文件,这对运行速度也是一个考验,而且能否快速方便地检索到需要的配置文件目前也是个未知数。
  崩溃!

2009-05-05

我怎么也用不了TDD

  TDD中说的是要先写测试用例,再写功能代码,可是无论如何,我都做不到这一点,我无论如何都要先写好功能代码,再回头来写测试用例。
  不过虽然这么做了,但我觉得还是不影响我做单元测试的,哈哈。今天是我将CppUnit换成googlettest以来第一次正儿八经地写单元测试用例,感觉还是比较爽的,有种写代码就是一种享受的感觉。用googletest可以比用CppUnit少敲不少字,而且我猛然发现,不用GUI的TestRunner,也没有什么不好的感觉。
  通过单元测试来保证类或函数的功能,最适用的场合是算法性的代码,真是太安逸了。

2009-05-04

插件依赖

  因为要使得插件支持依赖,比如在插件描述中说明,某个插件A,依赖于插件B和插件C,而插件C又依赖于插件D,那么在装载插件A之前,需要先装载插件D,再装载插件B和插件C,最后才是插件A。
  这个依赖关于跟C/C++程序代码中的头文件包含处理方式几乎一模一样,典型的做法就是把这所有的依赖关系最终用一张有向图表示,这样通过DFS可以查找到是否有循环依赖,如果有,可以彻底中断装载过程,或者只是简单地把最后一个回边打断而继续装载。
  最终整理出来的插件装载顺序也是很容易的。首先查找没有依赖于任何其他插件的插件,无论是理论上还是实际上,肯定是有这种插件存在的。找到这些插件后,把对这些插件的依赖关系都删掉,这样肯定又多出一些插件是已经解决依赖关系的。再把这些多出来的插件作为被依赖的关系删掉,重复前面一步操作,最终可以将所有插件都顺利装载。

2009-05-03

boost 1.39.0发布

  早上起来看了一眼邮件列表,发现boost 1.39.0居然发布,这次好像测试都没有多少消息,怪不得前两天就有人在邮件列表上开始说要1.40.0的release building了,真是快啊。这个节奏还真让人有点手足无措的。
  在公司里,用的都是正规release出来的版本,那样每次build出来的lib和dll一般都是没有问题的。而在家里,我都是直接从svn里check out来的,里面有的代码还存在些bug,有些则可能连编译都成问题。本来因为懒得编译的原因,我一直都是只用header only的库的,可最近因为公司里那个项目的影响,用了boost::regex、boost::program_options、boost::system、boost::file_system等的库,渐渐对这些需要编译的库也有点依赖心理了,防范心理也弱了不少,可是svn里的代码几乎天天有所更新,编译的话,就太头痛了。

2009-05-02

读《Imperfect C++》

  这书买了有一段日子了,但一直没有拿起来读过。几年前,也从公司的图书馆里借来过,好像那时候还在测试组吧,只是网上吹得厉害,于是就好奇地借来看看,结果完全不知所云,还暗骂网上吹的人不厚道。直到最近,一个同事以为自己把图书馆里借的那本搞丢了,在那说说,于是我又好奇心起,拿来翻了翻前面几页,发现讲得真好啊,原来以前是自己水平不够才看不懂的,于是上网买了一本。
  我有这样的坏习惯,书借来的时候觉得挺好挺有用,于是自己去买,买了来就丢一边了,好像里面的知识已经掌握了似的。这次也不例外,但是昨天不知怎么的,拿出来,以最近最认真的状态看了两章,有些收获,只不过,这一年多来,沉迷于网络小说,实在静不下心来认认真真做些有意义的事呀!

2009-05-01

手工皂

  托小妞在淘宝上买了块手工皂,照片很好看,那种那糕点,用小妞的话说,就是黄豆糕内嵌紫色布丁。昨天中午跟小妞他们两口子去万科城的夜郎国吃田鸡,把手工皂拿了来。包装也比较可爱,最外边是一个绿色的硬纸壳,纸壳夹起手工皂,两边没封起,可以看到里面,用一个塑料袋装着手工皂,塑料袋和纸壳在最上边还用一根绿色的小绳串起来打了个活结,很是吸引那些小女生的样子。
  手工皂很小一块,小妞说大概每天用一下,能用两三个月吧。嗯,我明明记得以前是小妞跟我说的,男人过了25就应该保养了,可是上次她硬是说自己说的是女人,我大汗,不过看着自己脸上日渐粗大的毛孔,我就只认我记得的了,哈哈。

2009-04-30

高质量就是装B吗

  google上有个讨论组,一直标榜自己高质量,可是为什么我订阅了这么些日子后,越来越感觉到这些个主题不知所云了,难道所谓的高质量在我看来就是装B的意思?
  我狂烈地晕啊!

2009-04-24

《Exceptional C++》看得郁闷

  这两天都是干点儿活,看两眼《Exceptional C++》,看得我异常郁闷!这是一本老书,讲的内容也都是我没关注过的,可能也正是这个原因,使得我看起来非常吃力,老是觉得不知所云,偶尔看到一些似乎有点儿明白的“原则”之类的tips,也是翻过一页立马就忘了,而更多的时候是不知道为什么要这样。
  极度郁闷啊!

2009-04-23

多线程下载有问题

  给升级程序加了多线程http下载后让同事试用,结果今天他打来电话报怨下载速度太慢了,甚至比以前单线程的时候还慢。用netmeeting看了一下,发现本来预计有5个线程同时下载的,现在居然是5个线程一个接一个地进行下载,这样明天会比单线程的要慢了,因为多了几次等待网络连接的时间,而且事实上自从改用多线程后才明显感觉到有时候一个网络连接的等待是好久,可能是十几秒。我在虚拟机里又试用了一下,还是5个线程并行的,那就郁闷了!
  后来又想到一些可以改进的地方,但没动手,只是记到todo list里了。
  唉,钱好少!

2009-04-21

线程阻塞在连接操作

  今天终于把升级程序的http下载部分改成多线程下载了,用的WinINet用5个线程同时下载,发现总是会有1到2个线程会阻塞在连接操作上,这是花费时间最多的地方!暂没想到好的办法,先这样了,对于大文件来说,确实能提升不少下载性能。

2009-04-17

再一次无奈的叹息

  我的自负掩盖不了内心深处的自卑,灿烂的阳光抵挡不住浑身的寒冷。
  难道我真的错了?我不知道,我更不愿意承认。
  我要继续坚持自己的信念,即使满身伤痕,也要蹒跚着勇往直前!

2009-04-16

挨批评了

  唉!!!

2009-04-11

内嵌wxLua

  之前因为使用wxLua,发现如果宿主exe使用的wxWidgets动态链接库如果和wxLua使用的一样,则不能在Lua中正常加载wxLua。虽然可以通过使用不同的wxWidgets动态链接库文件来规避这个问题,但实在是很丑陋的一件事情。
  今天想起来,能不能把wxLua内嵌到宿主exe中呢,这样就都使用同一份wxWidgets了。于是看了一下wxLua的代码,代码文件组织是很清晰的,很快就明白了各个文件夹下的文件是什么作用的。使用wxLua的一种方案是,它最后需要编译生成一个叫wx.dll的文件,Lua中可以直接require这个wx模块来使用。打开它的VC工程来看,也就是那么几个文件编译出来的,直接照它的样子把这些需要的文件添加我的工程中,然后编译,看情况需要修改的有几处:因为我的工程用了WXUSINGDLL宏,但这个宏会让wxLua中的一些类变成dll linkage的,所以要把这几处编译开关修改一下;有一个for wxLua的wxApp派生类,删掉;还有一处最主要的,luaopen_wx函数,有一个wxLuaState的对象,要改成直接在创建对象时把lua_State指针传过去,其他有些分析命令行参数之类的代码也是多余的可以删掉。另外还有个问题,在VC9里cell控件的代码中有一个GetRef方法,链接时报错,很诡异,搞不明白,暂时屏蔽掉那行代码。
  经过这么一番折腾,终于可以直接在exe中用内嵌的Lua解释器使用wxLua了!

原来我的id没有了

  今天用Foxmail收邮件的时候,才稍微留意了一下出错信息,原来我在学校bbs上的id已经没了,消失了,生命值减到0了!
  上去试了一下小丫头的id,也没有了,离开学校的日子,终于超过1年没有记得要登录bbs。
  用guest登录上去看了看,以前经常驻留的几个版面也找不到了,像People,留下回忆都没有!
  翻出Foxmail中保存的一些bbs上的聊天记录,还有邮件,想起在学校或开心或悲伤或迷茫或骄傲的时光。
  一站的终结,逼近着新的开始。

2009-04-09

超级牛掰的升级程序即将现世

  发现在有很多事可以做,发现自己一件都不想做,哈哈!
  今天老大跟我说,是不是忘了件事,那个gem升级也得做进升级程序中去。我说那不是说另外还要用wxRuby做个程序的嘛。老大说,这种建功立业的好机会,怎么可以让。我狂晕!
  找原来开发这个功能的同事咨询了一下基本原理,发现简单得令人发指,当然这种实现方案在我看来也是丑陋得一塌糊涂。看了一下他的代码,发现原来真的是那么一回事,基本的代码结构和思路都是很清晰的,除了有一些废旧的注释和代码。
  我开始头痛界面的问题。原来的实现使用了XTP的ReportControl,我想这么一个原本应该尽量精简轻巧的升级程序现在已经被我弄得渐渐肥大起来了,再加个XTP就太恶心啦!而且另外一个问题也已经浮现,越来越多的命令行参数,已经让眼下那种依次比较字符串的方式不再继续适用,引入boost::program_options也势在必行,天哪!

2009-04-08

又见离职

  今天一个同事发来告别邮件,没多少意外,因为上个月就已经听其他同事提到过。这同事是个比较有趣的人,告别还要写个中英文对照的双语版,而且内容严肃,很像领导讲话。除此之外,他爱好摄影,关注电子产品,这是我比较钦佩和羡慕的一点,我一直希望自己也能拥有一项比较健康的业余爱好,不过直到现在,尝试了不少活动,最终还是没找到。
  同事邮件中说到一句话,我很是有点感触:“事业上所有的成功都弥补不了家庭的失败。”他说这是一句台词,我觉得在我的内心深处也许我是很赞同这种观点的,但这些年来,我却时常认为,男人在30岁之前应该能作出一番事业来,至少能让事业走上正规,而这之前,感情的事应该让路。但是我有时候对感情的渴望也是强烈得一塌糊涂。
  同事还说,在这个月要去四川进行一场心灵之旅。真是一个煽情而风骚的男人啊!曾经我有一段时间也是对自己对生活失去了信心,小思宇问我想干活,我说想去旅游,小思宇说总不可能旅游一辈子吧,于是让我打消了这个念头。现在回想起来,小思宇真是个感觉敏锐的人,还是说很多女人真的有这种被人称之为“女人的第六感”的能力。
  同事要走了,引发我一些思考,最近甚至想到我这半年或一年中要做哪些事,可是我往往最终只留下一个美好臆想。
  祝同事以后的工作生活都一帆风顺吧!

2009-04-07

让人恶寒的囧问题

  今天发现一个很囧的问题,在保存文件的时候出现堆破坏。跟踪了一下,看到的代码才让人一阵恶寒。先到文档类中的保存代码,该函数会在之后向主窗口Post一个消息,主要是把保存的文件路径发送过去,这个文件路径保存的空间是在堆里动态分配的,到了主窗口中处理该消息的代码直接把该消息Send到另一个视图,而这个视图中处理该消息的函数会取出这个路径,最后销毁这块内存。问题就出在这块内存上,原先发送方把这字符串以ANSI形式表示,后来整个工程从ANSI转换到UNICODE后,只修改了处理消息的那边,而没有跟着修改发送消息的这边,于是两边认为的内存块大小就不一样了,于是就堆破坏了。而让我恶寒的是另外一个原因,为什么要向主窗口Post个消息,再由主窗口转发一遍。一方面运行效率低下,另一方面就是接口变动后不容易找到受影响方,这次就是活生生的例子。我想,用个observer就可以缓解这些问题吧!

2009-03-31

听《白狐》

  在Kugoo上随便按排行榜搜索的歌曲列表,几乎是重装一次才会更新一次的列表。这次偶然发现列表中一首很抒情的歌曲《白狐》。虽然听了也好些日子,而且都是一个人在静谧的夜里听,但直到今天,我才实在忍不住心中的好奇,上网搜索了一把。
  搜索了才知道,原来还有一个合唱版本的,于是马上开Kugoo来听,男声稍微有点让我失望,也许是从小看电视《聊斋》而被先入为主的思想主导,我特别希望男声是像电视中的那种书生腔调。不过总的说来词、曲,以及女声,都很打动人,在百度百科中,也有不少相关的信息,原来蒲松龄不是第一个写书生和狐独的故事的,只不过是他把如此凄美而又有新意的情爱带到了大众面前。
  不知怎的,尽管让人觉得悲伤,我却有点神往。

2009-03-30

烦躁

  真的。
  现在一个Lua解释器的封装类,一个插件注册表类,这样的结构是不够用的。至少需要有一个管理插件运行的模块,它可以屏蔽掉解释器的底层差异,使得使用这个模块的用户不用知道他们到底是在运行Lua脚本还是其他Ruby或是Python之类的东西。另外一点是这模块需要向不单是C++代码提供接口。也就是说,用脚本写成的插件需要使用这个接口。这个需求很合理,也很必要,不然的话,很明显的一大限制是不能再对插件进行扩展了。

2009-03-22

果然是编译问题

  因为怀疑,所以去确认了一把。自己也懒得编译了,直接从网上找了个人家编译好的wxLua,人家是用VC8编译的,我的工程是用MinGW编译的,结果真的可以加载了,我狂晕!不管了,大不了到时候提供个Lua for Windows的安装包链接。
  再回来说插件的问题。昨天主菜单是能运行起来了,不过发现有个不爽的地方。因为插件是放在一个指定目录下的,搜索出来的顺序可不是人能控制的,于是添加到菜单上的顺序也是乱的,这就不好了。由此引出另一个需求,对于一个插件应该可以定义多个菜单项,这样至少可以让这一个插件中的菜单项保持可控的顺序。

加载不了wxLua

  弄了一整天,插件框架基本上运行起来了,至少主菜单部分能出来了,证明这套机制是没什么大问题了,剩下的没解决的都属性主菜单的问题,而不是插件框架的。
  不过之后郁闷地发现,用MinGW编译的使用了wxWidgets的工程,不能使用wxLua,无论是通过C API加载,还是通过Lua代码加载,都不成功。用VC编译的工程就不存在这个问题,如果是普通的工程,没用wxWidgets的工程,也是可以加载的。于是我猜测是不是因为加载相同的wxWidgets而冲突了啊!因为wxLua是用MinGW编译的,还没试过用VC编译来wxLua来试试。开始还怀疑是不是因为路径的问题,或者是用了Luabind的问题,或者是用了SWIG的问题,后来实验发现好像都不是。
  用VC编译了wxLua来试试吧,如果还是不行,就没办法了,只好不在扩展脚本中使用wxLua了,唉!本来还以为用wxWidgets的工程用wxLua是绝妙的搭配呢,大不了再试试IUP之类的呗。

2009-03-20

今天进度不错

  花了近半天时间,把about对话框弄完了,不过有点小问题,不能设置tab页的title。整天琐事很多,最近把崩溃问题解决方案稍微整理了一下,说实在的,我心里是没底的,这个任务对我来说似乎要求高了些。我也希望程序能7×24小时地运行不中断,可是无论怎么弄,稍有点复杂的功能肯定就埋藏了大量的bug。

2009-03-18

要被Excel整死了

  今天发现原来我的机器上一直跑得好好的程序,到其他机器上就不行了。于是另外找了个装了Win2003和MS Office2003的机器上调试,发现首先我用了一个Excel2003中不存在的接口,Excel2007中是有的。于是绕了个圈子也勉强达到原来的目的了。
  接着发现把图表copy到系统剪切板中再取出来存成图片的文件不行了,根本枚举不到GIF类型的剪切数据。在网上找了个剪切板观察工具,发现确实没有那个数据,不是程序的问题,原来Excel2003的接口功能就是要比Excel2007弱。于是绕了好远的路,换个接口,把图表当成图片copy到剪切板中,发现剪切板中有Meta file picture类型的数据,再用Win32 API把这数据保存成文件。很扯的是这文件不知道是不是需要什么句柄释放操作,反正直接是不能修改或删除的,不过还好能复制。接着用GDI+把这个wmf文件转换成jpg或gif。结果郁闷地发现,转换后的图表背景变成全黑的了,想了好多办法,转来转去都是黑的,只有wmf和png看起来是白的。其实大概是透明的,整了好久都没办法,下班的时候跟老雷说起这个事情,老雷说先设置一下背景色。我真想拍一下自己的脑袋,怎么就没想到这点呢!
  明天再去弄了。

2009-03-16

VC操作Excel生成统计图

  老雷说,画图功能可以让Excel来做,我曾经还抱着试试看的心理在网上找有没有免费的代码可以方便地生成各种柱状图、饼图的,没找到,也实现懒得自己去画了,于是硬着头皮去用Excel了。
  VC的程序操作Excel,而且不光是为了存取数据,所以用COM是最正规最有效最理想的方法。如果是用MFC,可以让IDE自动生成其中的某几个接口的封装类,这样的好处是封装得比较高层,使用起来更方便简单一点。如果直接导入类型库,会生成整个类型库内所有的接口和类型的封装,不过是底层一点,最多是用一下COM智能指针封装的接口,可以少处理一些引用计数的问题。
  先可以录一个宏,查看VBA代码,就基本知道要用哪些接口的哪些方法了,但实际上如果用VC通过COM来操作,会发现跟VBA的结果经常有些出入。这让我有点纳闷。不过今天整了一天,总算勉强完成任务了。

2009-03-14

插件开发环境

  想起插件的事,如果一个软件具备插件扩展能力,同时又希望其用户自行开发插件,那么这个软件一般说来需要提供一个良好的插件开发环境。纵观Eclipse,MSVS、MS Office等都是这样的,而且VS和Office中的扩展形式除了插件,还有宏,准确地说来,是开发宏的环境是随软件一起提供的,而插件的开发是要用其他工具的,比如VS。而我前面说的插件开发环境,其实对应的是宏开发环境。
  这个开发环境简单的做法,应该是一个独立的程序,但与软件主程序之间有很深入的进程间通信,不然调试功能是不可能那么完善的。也有可能是还有一个中间的宏解释执行器,主程序和宏开发环境都是只与宏解释执行器进行通信,同时该解释执行器也有调试功能。这样做的一个明显的理由是,当调试宏时,主程序可能会被挂起,而这时调试界面则要求是活动的,所以放在主程序中是不容易处理的。
  Eclipse可能不需要这么做,它也许可以在不同的实例中使用不同的配置,这样可以在一个实例中调试另一个实例。
  还有一种比较极端或者不负责任的做法是,不提供开发环境,让插件开发者痛苦去吧。也许用比较高级的脚本语言来做为扩展架构的底层执行器,这个问题的影响会变小,因为脚本语言对调试的需求可能会小于像C++这种编译型语言。但我觉得无论怎么样,提供一点基本的用于调试的设施还是必要的,比如至少可以加入一种打印输出机制之类的。

2009-03-13

google test和google mock

  今天在公司里看到google test和google mock,又好奇心起,看了看,觉得比起CppUnit来真的简洁不少,而且CppUnit没有一个好用的用于插桩的框架,那Mockpp和Mockcpp都有很明显的很影响使用的这样那样的限制和缺点,而google提供的这两个框架则比较好地解决了这些问题。
  回到家又上网下载gtest试试,它有msvc的解决方案文件,也有GNU make的makefile。不过msvc倒是能直接编译通过,而MinGW则是不行的,要改些地方。首先要将MINGW的识别宏添加到GTEST_OS_WINDOWS宏定义中,然后在几处使用了SEH的地方,__try/__except都是VC才用的东西的,只要再在编译开关处把MINGW去掉。这样gtest库是可以编译通过了,不过有一个自测试文件gtest_unitest.cc却还是有问题,要加个文件头包含limits.h,这样就可以全部编译通过了。
  又顺带看了一下googlemock,它依赖于tr1。在公司里,我什么都没修改,直接用VS2008编译过了,在家里却不行,总是报VC的xtr1common文件中的什么tr1没开启。上网找了下解决方法,加入boost就行了,不过我试了发现从trunk中取到的boost不知为什么不行,最后还是从googlemock的官方网站上下载到一个他们从boost 1.36.0中提取出来的tr1中tuple部分,这样在编译包含路径中添加boost和tr1的路径,就可以编译过了。但它在源代码中明确限制了只能使用VS2005或更高版本来编译,看来要用在MinGW中是有点困难了。

2009-03-12

可以用bjam做点事了

  今天把一个用了wxWidgets、Xerces-C++、luabind的工程用bjam写了个构建脚本,可以用MinGW来编译,也可以用VC来编译,这就是bjam的好处,很少需要关心不同编译器的命令行参数。
  经过这次实践,还发现alternative的选择原来是要跟它的属性完全对应上才行。比如一个目标,写了toolset和variant以及threading,则在命令行中要把这三个全都设置上,不然它就说找不到匹配的目标。这让我有点头痛,假如我只想在命令行上选择toolset,而让其他两个属性固定死,那怎么办。
  不过还算可以了,目前基本能满足实际需求了。

2009-03-11

继续bjam

  bjam编译Windows下的rc文件不用特别指定,就像其他C/C++代码文件一样,直接放个文件名就行了,它能自动认出来,并调用相应的资源编译器来,只不过生成的二进制文件后缀还是跟代码文件编译出来的一样,不过并不影响最后的链接过程。
  对于debug或release下使用不同配置的情况,需要至少每一种情况写一遍目标,同样如果是不同的toolset也这么处理。
  今天把一个使用WTL写的工程用bjam系统编译了一把,果然也不是很麻烦,不过需要写不少特定的参数,bjam的默认配置不够用了,所以这样的情况还是不适合用bjam的。简单地说来,适合用bjam的场合,至少是没有一个好用的IDE的情况下。
  想起直接在bjam里用wx-config来生成的编译选项,居然会出错,因为wx-config的输出后面多了个回车,用make就没影响,而链接选项在bjam中也没影响。没办法,去网上找到wx-config的源代码,只有一个文件,不过有几千行,稍微找了一下输出的代码,把末尾的回车换行去掉,再试了下,果然可以了。

2009-03-10

bjam使用点滴

  要在工程的根目录下放一个Jamroot文件,可以没有扩展名,也可以加.jam为扩展名,这样其他子目录下的可以命名为Jamfile了。
  基本上是个大小写敏感的系统。
  还没找到怎么让它来自己编译Windows的资源文件。
  用path.glob-tree可以指定目录来进行glob-tree,如果光用glob-tree,是只能从当前Jamfile所在目录开始全部搜索的。

2009-03-09

bjam有点意思

  在编译Luabind成独立的库时,顺便学了一下bjam。bjam在某些场合下挺有用的,如果之前用的是make,再来用bjam,感觉就是用过汇编之后来用高级脚本语言。
  折腾了一阵子后,我的感觉就是bjam几乎是完全给boost设计的。其中最突出的特点是,编译器无关。它在这方面做不了少工作,只要指定一个toolset,它就能自动查找编译器所在的路径,而且不用关心编译命令行,因为大部分常用的选项它都提供自己的抽象方式来表达。还有就是它似乎有良好的可扩展性,在boost的源代码目录下有几bjam的工作目录,下面有几个子目录,里面分别存放着一些jam文件,在写jamfile时,可以import模块进来用。
  我捣鼓了一阵,把自己一个工程,里面有几十个C++源文件,从GNU make转成了bjam工程。感觉有点新鲜,但总的说来,它应该是适用于这样的场合:工程是可移植的,即要跨操作系统,跨编译器。

2009-03-08

Luabind还是有必要的

  写着写着,发现Luabind不是先前想的那样有了SWIG就可以抛弃了的,还是有用武之地的。
  主要体现在两个地方。
  一、从C++调用Lua函数。Luabind提供了两个方法,在Boost的协助下极其出色地分别实现了调用全局函数和其他可被作为函数调用的对象的调用。这两个方法都以C++模板实现了可变参数类型和可变参数个数的调用,而不是C库中printf使用的那种方法。这得益于强大的Booster::mpl。
  二、提供C++中自定义类型的高度灵活的部分封装。用SWIG通过分析C++声明一股脑儿全都封装了,当然可以通过预定义宏SWIG可标识不想被封装的部分,但这需要修改C++源代码,以致于破坏C++源代码的整体风格,而且灵活性明显不行。用Luabind可以随心所欲地把任意一个类的其中几个方法封装了,这个优点是我在看了Luabind中自带的几个example才想到的,比如它演示封装了boost::regex,boost::filesystem,如果想用SWIG,几乎不可能达到目的。

2009-03-06

研究了半天SMTP发送HTML

  主要的内容生成功能基本完成90%了,于是就研究一下怎么把这html内容通过SMTP发送出去。原来有一个从CodeProject上找的SMTP类,直接用WinSock写的,倒是也可以正常工作,接口也很简单,而且当时沾沾自喜经过自己修改,是可以支持中文的,其实是自己土了。现在只是想把这个类再修改一下,可以直接发送html内容,不过看起来似乎有点儿复杂。
  用Outlook Express发了几个邮件,用Wireshark抓来看了看,看得云里雾里。后来找到另外一个示例程序,有源代码,也可以发送html,用Wireshark看了看,发现差别还真大。最后看到一个说明里提到,RFC2110里就是讲这个的,于是马上找来瞄了几眼,终于有点儿理解了。原来Outlook Express在一封邮件里包含了2封邮件的内容,分别是html和plain text的,这样无论对方的客户端能不能支持html阅读,都可以方便地看到真正的邮件内容,当然html中的链接嵌入对象除外。

这个进展还真是慢

  回来整了几个小时,没搞出什么花样,只是把tab页对象换了个地方。本来是没想到要换的,只是今天突然想起,要截获tab页关闭的消息,于是折腾了那么久,发现在原来的那个类里搞不定,不知道是什么原因。开始我只是想试试直接用wxFlatNotebook对象的Connect方法,可是根本不知道那些参数要怎么写。后来我把那类从wxEvtHandler继承了啊,消息映射也加了,但就是不行。后来看到wxFlatNotebook官方的sample里是把这消息映射放在一个wxFrame里处理的,于是我也照样学样把它放到MainFrame里去了,果然能截获了,而且从设计角度讲,这也比之前那样好,因为这个wxFlatNotebook应该是全局使用的。只是没弄懂原因,有点不爽。

2009-03-04

MySQL还不错

  老雷要一个自动发汇报邮件的程序,这个任务落到我的头上,于是我这周开始整这玩意儿。所有的数据几乎都是存放在MySQL里的,一个是RedMine的内容,另一个是组里自己开发应用程序使用情况统计工具,服务器端也使用的MySQL。今天我发感叹地跟开发那应用程序使用情况统计工具的同事说,幸亏你们当时选用了MySQL,不然还要更麻烦一点,比如万一选了个Access或者Sqlite3,还不能方面地远程访问。如果是SQL Server,还得使用一套不同的数据库访问方法。
  通过这两天使用mysql++来访问MySQL的经历,我感觉无论是MySQL引擎,还是暴露的API,都挺不错的。大概是因为ADO访问SQL Server用得有点反胃了。那段经历让我觉得最为不爽的是,如果SQL写得有点问题,就会抛异常,而且这异常就是catch不了,因此很难排错。这次用mysql++在这方面就做得不错,异常可能catch到,而且给出的出错信息很准确,定位很方便。另外有点值得一提的是,mysql++里有一个重载了很多类型转换符的String类,呵呵,这也是一种办法啊,不过使用operator+时却遇到了点小麻烦。
  开发这个程序最大的收获大概就是这个了,大部分情况下,可以用MySQL来代替MS SQL Server了。再扯远点,Access也有点不好的,打开的时候特别慢,用Sqlite就挺快的,而且Sqlite3还是是弱类型的,呵呵。

不要盲目追求最新版本

  无论是软件,还是程序库,应该选定一个并不太旧的稳定版本,一段时间内,比如半年,一直使用该版本。
  一个明显的例子是之前用4.3.2版的gcc编译好的wxMSW,后来年到4.3.3版的gcc了,就下载下来试用了一下,发现原来的工程死活链接有问题,报什么虚函数找不到云云,再换回4.3.2就又可以了。由此看来,4.3.2和4.3.3编译出来的.o文件可能在内存等方面有变化。我也不想再花时间去用4.3.3编译一遍wxMSW了,太浪费时间了,而且如果要换,就要把其他相关的库都换一遍,至少包括boost,Xerces-C++,wxLua,青春啊,不能就这么耗费掉啊!

2009-03-02

基于Lua的可扩展架构设计

  首先,这个架构是一定程度上模仿Eclipse的,并做了大量简化,所以模仿得并不像,不过不得不说最原始的灵感和很多想法是从它那里来的。
  支撑起整个可扩展架构是一个类,大概名叫pluginsRegistry的类。这个类一般可以使用singleton模式,它做一些与插件相关的最基础最底层的事。比如,在宿主程序(这里一般是一个C++程序)启动时,它负责扫描指定的一个目录(包括子目录)下所有文件,找出其中的plugin.xml文件,也就是插件描述文件。它并不一定要是xml来描述,也可以是用Lua来描述,毕竟Lua最早的用途就是作为配置文件的,而且这个文件并不会被程序动态修改,Lua也够用,但要注意的是名字空间污染的问题,一个办法是每个描述文件都使用完全相同的结构和变量命名,但每次都单独启动一个Lua解释器来解析。一般说来一个描述文件中声明一个插件,也就是一些基本的插件信息,包括插件id、插件扩展点和插件对应脚本文件路径这三项最主要的不可或缺的信息,以及一些诸如被扩展点,版本号,依赖关系,版权信息等等不那么重要的信息。扫描到一个插件后,这个类会把插件id,脚本文件路径对应着保存起来,并把该插件添加到指定的被扩展点上。所谓扩展点,其实准确一点说,应该是被扩展点代表一个插件会被激活(其中一部分)的时机。被扩展点一般也是由其他插件提供的,也就是说其他插件会在某个它认为合适的时刻,激活所有注册到被扩展点下的所有插件。所以插件描述文件中很重要的一点是,需要声明自己是扩展了哪个被扩展点,不然就永远不可能被激活。由此可以看出,另外一个需要注意的是,一个插件一般会依赖于另外一些插件,至少是依赖于提供它要扩展的被扩展点的插件。一个插件必须要指明它要扩展的被扩展点,这是别的插件提供的,另外它也可以提供自己的被扩展点,让另外的插件来扩展自己的功能,当然这不是必须的。为了说起来不那么拗口,在描述文件中,一般我称前者为扩展点,称后者为被扩展点,这是把同一类事物在一个所属中以其不同的功能或角色来区别而得来的。再回到这个类的功能上来,除了前面讲的这几点功能外,它还应该能向外界提供一个服务,通过该服务外界可以根据指定的被扩展点查询到所有注册在该被扩展点下的插件,理论是希望只是通过一个id就能唯一标识一个插件。这样每当时机合适时,提供被扩展点的插件就可以通过这个服务来做一些事情,最常见的是通知所有该被扩展点下的插件即可,那些插件自己应该清楚这时应该做些什么事情,还有种情况是,提供被扩展点的插件应该可以从中区分出一些插件,而不是全部,来通知。一个很常见的应用场景,主菜单,可以是一个菜单项对应一个插件,这时每当用户点击了菜单项,那么只要激活该菜单项对应的那个插件即可,而不是所有主菜单下所有的插件都要激活一遍。
  通过前面的阐述,可以知道,这个类需要一个清晰简单的接口,这样才可以比较方便地既给C++代码提供服务,又给Lua脚本提供服务。尽管Lua从设计上就考虑了很多跟其他语言交互的情形,但这里我还是得小心一点,少使用一些只有某种语言特有的一些元素或特性。
  最后,以一个实例来简单叙述一下这个架构是怎么真正让一些功能运作起来的。就还是以主菜单为例,主菜单也是一个插件,当然,它是用C++还是用Lua实现的我们不关心。重要的是主菜单插件首先有一个自己的id,并提供了一个被扩展点,假设叫view.ui.menu,这时假设那个类已经把所有扫描到的插件正确地分析过了,这个被扩展点下已经有一些插件了,比如file_open,file_save等等。主菜单插件在自己被初始化时,调用那个类来得到所有view.ui.menu下的插件,并依次激活这些插件的get_caption方法,根据这些个方法返回的内容,作为菜单项的标题,逐个添加菜单项,并把菜单项的id和它对应的插件(可能就是用插件id来表示)保存起来,当用户点击一个菜单项时,主菜单插件可以得知用户点击的菜单项的id,并从之前保存的信息中得到它对应的插件id,以此来再调用那个类来激活唯一对应的那个插件,那个插件知道用户是点击了菜单点,就执行一些操作,比如打开文件等等。
  大致的流程就是这样。

2009-03-01

西安出差

  昨天晚上6点半的飞机,回到深圳住处已经10点了。在西安过了6晚,有4晚出去转了转,给我印象比较深刻的是那里的吃的和人文景观。
  22号傍晚到的西安,结果最郁闷的是被人忽悠了,居然有两个名字一样的酒店,而且我明明说得很清楚是哪条路上的,结果司机还是把我带到了另一个酒店那里,太扯了,比国产007还要傻冒。马上出来打车走,结果说明了是东仪路,那个司机硬是开到了东一路,我大汗!好不容易啊,才到了最终目的地。一个小小的窄窄的地方。放下东西,去酒店附近一个砂锅店吃了些东西。第一天,给我最大的感受是,西安美女挺多的!在的士上时,就看着路上的行人中,很多漂漂的mm,后来去砂锅店,就又看到一个很漂漂的mm一个人在那里很优雅地吃着粉丝。
  23号晚上,听从王同学的建议,去大雁塔喷泉广场转了转。到了那里才郁闷地发现,这明显是个情侣才来的地方啊。可怜我一个人,天又冷,好凄凉!随便走到路边的一个店里,买了点东西,回去哄一下家里的大小美女。其中一条街上,一家一家的小店,叫百工坊,都是当地特有的手工艺作品,主要特色是可以让游客DIY,但是我看到基本没有什么人进去,不知道是不是因为已经是晚上的缘故,或者是天气冷,游客不多的缘故。正当我无所事事准备回酒店的时候,广场广播突然说有什么水舞表演,于是好奇地停下来等。水池旁边站满了人,中间也有很多人,我挤进一个地方,原来就是喷泉配合音乐,喷出不同的水柱,所谓水舞。不过也是第一次看到这种东西,还是有点新鲜感的。
  24号晚上,我本想去买些玉的,因为蓝田就在西安附近,所以西安就有很多卖玉的,问了下王同学,她说去书院门买。结果等我打车到了书院门,那里的店铺都已经关门的了。没办法,于是只好走到对面去看城墙,反正王同学也说,这好歹也是西安一景啊,哈哈。门票40,我不清楚这种行情,直接进了里面。刚好有什么大唐灯会,于是在城墙上走了一两个小时,天还是很冷,回!
  25号晚上开始下雪了,而且从公司出来打车,居然打不到,走来走去,过了一个小时才打到,汗!直奔回民街,跟司机聊起,他居然是上海人,一下就猜出我老家是哪儿的,哈哈。到钟鼓楼下了车,绕了一圈才发现那条回民街,真是条小巷子,而且人不是很多,我不知道是因为天气不好人少了,还是因为已经有点晚了人少了。走到另外一边,时代盛典那里,也有一条回民街,人更少,店都关门吗。没办法,打电话找王同学确认一下,王同学也跟我说不清楚,不知道她哪里找来另外一个mm来跟我说,我才明白,第一次进去的那条小巷子是对的。于是对跑过去,吃了笼贾三灌汤包就很饱了,晃悠晃悠晃到另一边,随便找了个泡馍店,进去点了个牛肉泡馍,不得不说,这味道真的比公司食堂的好太多了,肉嫩味美。
  26号,下大雪了,早上起来发现屋顶上,车顶上都积了几厘米厚的雪,白天也是飘着鹅毛大雪。只好不出去了,老老实实呆在酒店里看小说看电视。
  27号,想着第二天就要回深圳了,下了班立马又跑到回民店买些吃的带回去。顺便去吃了下之前那个mm推荐的红红炒米,感觉跟蛋炒饭差不多,不过是里面有些肉丝和酸菜。又叫了几个羊肉串,还有不知道是什么肉串,味道都不错,至少这羊肉串我是比较得出来的,比起其他地方吃过的,这里的特别嫩,而且膻味不大。吃完后,晃悠了两圈 ,进到一个大一点的店铺里,买果脯,就挑那种平常在深圳不太有得买的,我也不知道价格上有没有问题,不过想来再亏,数量也不大,也就不去计较了。
  偶尔去下这种从没去过的地方,还是挺有意思的,不过一个人毕竟乐趣就少了些了。

2009-02-22

今天去西安

  一周前就被告知要去西安出差,心里还是有点忐忑的,说起来有点丢人,还是第一次一个人去一个陌生的地方,真正的人生地不熟啊,哈哈。以前去重庆,有同学一起,来深圳,也有校友一起,去贵阳,有表哥和小妞接待,这次可是真的一个人咯。

2009-02-21

终于解决Lua中任意使用C++对象

  仰天长啸三声,哈哈哈!一直没想出或找到什么办法可以比较方便地解决这个问题。我的需求是,在C++中随时可能生成一些对象,而这些对象随时可能需要被Lua获取并进行一些操作。
  在网上转了很久,看过LuaBind,还有一些文章。只有LuaTinker有一个可以把C++对象映射到Lua全局变量的方法,似乎有点接近了。但实际上离我的需求还是有点距离,比如它需要由C++代码知道这是给Lua用的,并主动把对象映射过去,而且是映射到Lua的全局变量中,通用性不够。我希望C++代码可以不跟Lua交互,其次,应该由Lua主动去获取它需要的C++对象,这样Lua可以自己决定把这个对象保存到什么地方。
  今天又看了一下SWIG的文档,发现SWIG对Lua的封装做得实在太好了,它可以封装出自定义类型的指针,例子代码中以FILE *作了示范,想了想,我在C++中写个函数,返回相应的对象指针不就行了!试了试,果然可以,这下终于搞定了,可以继续写编辑器了。昨天还在想,有一部分Scintilla的初始化工作,代码好长,虽然初始化内容是保存在配置文件中的,但是配置项多,而且互相之间比较独立,C++代码仍然需要一个配置基一个配置项地写,我就觉得这部分应该交给Lua去做。这里Lua的功能除了原有的保存配置的功能外,还兼任了使配置生效的责任,而且仍然保留了原来配置文件的不需要重新编译主程序就能修改行为的优点。这个思路是对的,但技术难点就是Lua如何操作那个编辑器,这下好了,没问题啦,SWIG真是个好东西,还要Luabind之类的C++ Wrapper干什么!

我真是个换肤论者

  之前说到,用多了chrome,竟然不知不觉地想着firefox的界面也想跟chrome一样,说干就干,先去找theme,要能尽量模仿chrome的,还真找到一个chromize extreme,不过它只能支持3.1b1以上版本,没办法了,我现在是3.0.6,只好去重新down了一个3.1的装上,还是像以前一样,用命令行参数,将配置目录指向3.0.6的目录下,这样就可以使用3.0.6中已经有的一些东西,不过有些扩展不兼容,暂时不管了。装了chromize extreme后,挺像chrome了,除了多了个标题栏。找到一个hide caption扩展,不过发现它不能用在3.1版本中,于是另外想办法。最后只好用custom button扩展,加上一段脚本,叫max的脚本,会自动将firefox弄成最大化并隐藏标题栏。
  搞定!界面看起来真的像chrome,感觉舒服多了,哈哈,就是不能用3.0中的一些扩展了有点遗憾!

2009-02-20

傻掉了

  突然发现,怎么Firefox有标题栏,怎么有菜单栏,怎么这theme就不正常了呢!使劲点了几下菜单,也没变化,切换了一下theme,重启firefox,菜单栏不见了,再切换回去,怎么标题还在!
  突然想起来,这标题栏似乎一直都在的!是chrome用多了吧,傻掉了,汗!

2009-02-19

黑社会?

  8点钟才从公司出来,坐上B666回住处,也不算太晚。快到小区门口的公车站时,我已经站起来走到门边,一辆SUV却超上前拦住了公车。从SUV下来五六个30来岁的男人,都是圆寸头,拍开驾驶室的窗很嚣张地对司机说,叫他以后不准再开,要是明天再被他们看到要怎么怎么的。这样捣鼓了几分钟,终于走掉了,我还一直担心他们会不会上车来抢劫。
  记得从上中学开始,每当说起以后娶妻生子的事,我就有点烦躁和恐惧,我总是担心自己的小孩没教好,变成流氓。我妈知道我的想法后,曾也和我说过,只要好好管教,是会好的。今天看到这些人,我又想起这些来,不禁又想,这些人的家人都是做什么的,他们有没有父母,有没有妻儿。
  这个世界,真让人憎恶。

2009-02-18

开始使用wxWidgets进行界面开发

  半生不熟地用着wxWidgets,全是照着代码改的,勉强能运行起来,不过似乎用gcc编译出来的release版本有时候会报错,不知道是哪里出的问题!可能是哪里资源没正确释放什么的吧!
  界面的框架算是搭起来了,排除掉那个报错的bug就比较完善了。接下来完成插件扩展机制,一大块功能就可以用脚本完成了。

2009-02-16

差不多封装完Xerces-C++了

  其实只是封装了其中很小很小的一部分,DOM的那部分,DOM中DOMElement相关的那部分。接口是模仿那个同事的MSXML封装类来的,应该说,只有最初的灵感是来源于那个MSXML封装类的,后面真正用得最多的部分,实际上已经是被我修改过的。其中最有用的部分是,对属性和文本内容的设置和获取,针对不同的数据类型作了特化,以及对NodeList部分增加了iterator来适配STL算法。
  对于这个封装的意义,或者说价值到底有多少,自我感觉,至少在现在手头这个项目中,在boost::bind、boost::lambda配合STL算法的联动操作下,让代码少了好些,不过也许带来的问题是,可读性变差,以及不再去思考是否有其他更好的方案的惰性增加。

2009-02-15

终于搞定boost::program_options

  再次尝试,终于可以在WIND中使用boost::program_options了,其中付出的代价是,不能使用STLPort了,因为最直接的原因,不知道为什么,在release模式下,编译就会出错!
  这之前提到过,用vc9编译boost时,会只生成几个静态链接库,而且这几个静态链接库实际上并不能正常使用,现在发现原来只要在编译前,在命令行环境中设置好vc9的路径,也就是先运行一下那个vsvars32.bat,就再进行编译就可以了。
  这次编译我在命令行中添加了--buid-type=complete,耗时巨大,至少1个多小时还是因为磁盘空间不够而结束了。不过确实生成好各种类型的链接库,以目前program_options来看,共有7个库,其中2个是动态链接,分别有debug和release两个模式下的,从文件名中可以区别开来,debug下的文件名中有gd字样,而release下的是没有的。静态链接的库文件共有5个,都是libboostxxx.lib的形式,可以看到其中s表示编译器的runtime是静态链接进来了,gd则还是表示debug的意思,mt则是多线程的意思。所以照这个原则推测,其中有一个是单线程静态runtime链接版本,不过这对我来说,也没什么用了,一般说来,我写的程序肯定是使用多线程版本的runtime的。
  终于搞定了,除了不能用STLPort有点遗憾!

搞不定boost::program_options

  还是搞不定,郁闷!
  首先我用vc2008编译从svn中取出的boost代码,总是有问题,只能生成一些libxxx.lib,没有生成动态链接版本,这个问题在boost正式发布的版本中是不存在的,真奇怪。

  其次,无论是正式发布的还是从svn中取出的代码,编译的boost::program_options,在与STLPort一起用时,是必定出错的。debug模式编译基本能过,但链接死活不行。release模式则索性编译不过,这个问题在取消STLPort时,就不会存在。但是如果不用STLPort,在链接时,会报符号冲突。
  如果实在不行,也没办法了,只好不用boost的需要编译的库了。boost中大部分的库是header only,这总算让我觉得比较欣慰。不过昨天看到Boost.Log以提交review了,而且看了一下它的文档,需要依赖了boost中的好多库,其中好几个需要编译的,比如boost::filesystem、boost::system、boost::date_time等等,郁闷,一直在等着boost出一个log库,准备工作还那么麻烦啊!

2009-02-14

强大的Firefox,强大的扩展

  在复杂的Internet环境中,用惯了firefox,用惯了它的扩展后,再也不想用其他浏览器了。这两天又装了几个扩展,太好用了。
  noscript,自动禁止了一些无用的页面脚本。Coolpreviews可以在鼠标指针移到超链接上时,自动弹出页面预览窗口。FireGestures,就是现在在很多软件中流行的鼠标手势。
  像chrome这样的实验品,我最多只是在局域网环境中用一下,一来忽略了外网环境中特有的复杂性对它的过高要求,二来它的超快速度在局域网中也能更好地体现。

2009-02-11

又靠了次老外

  前不久为了使得MSXML输出到文件的内容有良好的缩进格式,在网上又转了一圈,最后终于在一个老外的论坛里看到一段代码,使用SAX的方法,果然可以几乎完美运行。再也不用以前找到的使用xslt的方法了,使用xslt的方法是很久以前在国内哪个网站上找的一段javascript代码,自己翻译成了C++的,最后效果不是很令人满意,主要有两个缺点。一是文档开头的处理指令没了,要在额外添加,二是对于没有文本内容的节点,它还是会添加一个结束标记,累赘冗余。
  这两天在写WIND时突然遇到的问题,本来只是使用STLPort替换了VC9自带的STL实现,用着也没什么问题,后来想试试boost::program_options,结果在编译时,报boost::program_options的lib跟WIND编译时使用的标准库不一致,一猜就晓得是因为当初编译boost::program_options没有使用STLPort编译,于是想重新编译一把。翻了好几遍boost自带的文档,也没找到具体的方法,在网上又转了几圈,还是没发现有效的方法。今天又是偶然在一个老外的论坛上,看到一个回复,简单试了试bjam果然可以正常运行下去了,唉!
  又靠了次老外。

2009-02-10

小妞说我麻木了

  今天突然说起情人节来,小妞问我有没有约mm,我说我一到关键时刻就哑火了,小妞就说约一下又不会死人,我说没有想约的人,是不是我要求太高了。小妞就说觉得我是麻木了,什么样的人都觉得一般。我无语。也许吧。

2009-02-09

孙同学好贤惠

  今天是元宵节,据说今天晚上10点多将是近50多年来月亮最圆的一次。下班后跟F还有孙同学一起去超市买了点汤圆,然后回家自己煮。三个人吃两包汤圆明显是过量了,最后实践证明,我们顶多只能吃掉一半。当然我们也煮了一点米饭,一人不到一小碗,不过也刚好用来调味,汤圆毕竟太甜了。
  吃完后,孙同学又一次把我的厨房大清洗了一遍,灶台上厚厚的油渍也被擦干净了。我开玩笑说,你是不是现在故意来刺激我的啊,以前怎么没发现你有这么贤惠啊!她就说,可惜了吧,后悔了吧。呵呵!
  真的很有点心灰意冷了,要撤退了!

2009-02-08

炒点小菜,喝个小酒,惬意人生

  去超市买了点儿肉和菜,回来哧哧喳喳弄了大半个小时,整出3个小菜来,跟同屋的那个江西老表一人倒了大半小饭碗的女儿红,打开电视,看着国内国际时势政治,摆下龙门阵,点评下天下时局,人生真是惬意啊!

sqlite3编译脚本有问题

  这两天才发现,用msys运行configure生成的makefile是直接编译链接有问题的,最后链接的时候总是报找不到一个什么sqlite3Backup函数。今天在源代码文件中查找了一下,发现这个函数是在backup.c文件中定义了的,所以代码有问题的可能性不大。于是看了一下生成的makefile文件,赫然发现没有编译backup.c的动作,加上后,再make一把,果然通过了。
  看来sqlite3的configure脚本没有跟着更新啊!

2009-02-05

有点想通了

  昨天又看了一下《Contributing To Eclipse》,思路有点清晰了,应该有一个任何地方都能访问到的地方,Eclipse中叫插件注册表,存放着所有插件的描述信息,这样无论是核心还是插件,都可以随时读取到与自己相关的插件的所有必要信息,实现相应的功能。
  之前还疑惑,与界面相关的插件处理流程会自相矛盾,现在看来应该能解决了。首先需要确认一点,暴露被扩展点的,无论是核心还是插件,都可以从插件注册表中读取扩展的信息,比如菜单项名称,这样就可以添加一个新的菜单项,以及维护一个处理关系,Eclipse中有个某某proxy的机制,这形式不重要,关键是暴露被扩展点的那个部分应该在添加了新的菜单项后,能在菜单项被点击时,准确地知道哪个菜单项被点击了,并依赖插件注册表找到对应的处理流程,以我目前的情况而言,即对应的Lua脚本文件,Lua脚本模块名称,以及处理函数名称,然后将这些信息提交给插件运行模块来运行,插件运行模块则又要与插件装载模块合作,如果运行时发现没有对应的模块或函数,则要先装载对应的脚本文件到解释器中。

2009-02-03

我们都在被人追逐的过程中追逐着其他的人

  真是捉弄人的命运啊!有句话说,命运负责洗牌,而我们才是玩牌的,牌太差,无论技术如何,都是于事无补的。
  今天在公司里用错了_tcsncpy和_tcsncat,因此引发的bug找了半天才明白原因,汗颜。
  另外,Launch里好严重的内存泄漏,确实,都是些很低级的失误,review很重要。

2009-02-02

克制求知欲很重要

  感兴趣的事情太多,精力太有限,克制自己的求知欲真的很重要!

2009-02-01

向SQLServer2005存xml

  今天终于解决了向SQLServer2005存xml时使用的字符集的问题了。
  年后第一天上班,看到年前一个邮件,说是有个文件导不进去。拆下来跟踪了一下,发现是中间生成的xml文件不完整,通过简单的试验,直觉告诉我是字符集的问题,因为我将这xml文档的字符编码设成gb2312了,而鬼知道别人会写些什么特殊字符进来。把文本粘贴到UltraEdit中看了一下,果然有一个非gb2312范围的字节。我想,这种情况都出现了,无论如何都不能再继续用gb2312了,只能往utf-8之类的方向去考虑了。实验证明,以utf-8编码的xml文档是完整的,可是又回到一年前的问题,utf-8编码的xml文档写不到SQLServer 2005中xml类型的字段中,只能写gb2312的。
  实在没办法,这方面没有人可以提供相关的经验指导,只好去看SQLServer2005的online help,好在是中文的,随便翻了一会儿。中间联想到,会不会是MSXML保存时并没有真正转换编码,实际上当然不是。又联想到,该不会是因为没有BOM吧,结果我没有去试。后来偶然看到其中一段说明,可以用utf-16,而且不在乎有没有BOM,如临大赦啊!
  用ADO写回去时,先读出整个文件,因为开头的BOM的原因,需要再用SysAllocStringByteLen生成一个BSTR,不能直接用_bstr_t,再填回数据库,就可以了!

孙同学要去武汉了

  唉,一起玩的人又少了一个……

2009-01-31

Launch项目小结

  这次回老家过春节,年前几天没事,就着手做那么个小东西。这个小东西的原始需求是从老大那里听来的,老大则是从其他同事那里收集来的,主要的需求是,能自动从指定的路径下找出所有可执行文件,并添加到菜单项上,点击菜单项便能运行这个可执行文件。
  这个需求的意义不考虑,直接进到主题,如何实现。刚开始我以为从网上应该很容易找到一个免费或者开源的项目来满足需求,在网上逛了几圈后,只注意到两个有点关系的项目,分别是LaunchyTextBox。我想了想,决定整合这两个项目的特点来做,至于最原始的那个菜单项的需求,就变成一个次要特性了。
  主要的设计和开发大概花掉我8天时间,而且每天投入时间应该超过8小时,所以还是比较可观的,尽管功能看起来确实简单,而且迄今还有些未实现的。
  程序最终使用VS2008开发,界面用WTL写,VS2008没有WTL相关的向导,装个VisualFC插件可以简化一部分这种工作。其他用到了嵌入式数据库sqlite3,用于保存搜索到的文件信息;用到了msxml,基本是必备的配置文件解析和保存方式;用到了Lua,实现了一定程度上的外部脚本扩展;还有boost、STLPort等等。
  总的说来,这个项目的最大几点收获是:一、熟悉了WTL的使用;二、熟悉了Sqlite3的嵌入;三、对用脚本语言实现C++程序的扩展,有了一点心得。还有一些与技术无关的收获:思维导图确实是一种辅助思考过程的好工具;在wiki中随时安排计划,记录进度,撰写文档是一种好的实践。

2009-01-17

要回家了

  可是怎么我一点都不激动,不兴奋呢,反而有种失落的感觉!

  上午在公司开了两个小时总结会,总的说来,对自己在这一个季度的表现还是比较满意的,虽然中间过程有些不爽,但总体上结果还是勉强能够自我安慰的。
  下午在公司,又开了两个小时的会,这倒没什么内容了,我本来这一年来就没有参与这些项目活动,冷然一个旁观者的身份。
  下了班,跟F还有两位cm0大大一起去华强北吃海鲜,虽然那家叫明香的馆子屡次路过,也听人提起过很多次,但来深圳3年半了,还没去过一次。吃得很安逸,也正是这一刻安逸的短暂,才让人觉得眷恋。
  吃完晚餐,4个人一起去逛街,真是不得不感叹作为一个男人,F居然那么喜欢逛街,我本来一直以为自己逛街的能力也够强的了,被小妞她们培养了近一年的,结果我发现差距了,我完全是被动型的那种耐力好,而F是主动型的精力充沛,兴趣盎然!倒是把两位cm0折腾惨了,还穿着高跟鞋,最后是见到凳子就想坐了,哈哈!
  回家了,明天的现在,我应该是在家里的床上了,哈哈,这世上没有过不去的坎!

2009-01-16

Chrome不是只快一点点

  今天在公司网上看到有人在发Chrome的绿色可执行包,于是又好奇心起,下载下来玩了一下。以前也装来玩过,不过当时的版本还没有足够的稳定,时不时崩溃或挂起,于是摆弄了三两下就不玩了。这次下载下来的似乎则要稳定得多,毕竟是经过好久的beta测试了。

  这次给我最大的震憾是chrome的速度真的很快,比IE和Firefox不是快一点两点,而是快很多!也许在外网环境还没有这种体会,因为等待的时间全耗在网络延时上了,在局域网环境下,网络延时极小,浏览器的渲染速度差异就很明显了。
  本来在公司里,我也只是用3.0版的Firefox,因为用它来浏览redmine比用IE快多了,其他的应用场合主要是写wiki,用的mediawiki程序,以及看CruiseControl的报告,还有一些静态页面,比如用doxygen生成的html文档。换用chrome后,只能用震惊来形容,除开网络延时不说,真的是一点链接即出来整张页面!
  不过总的说来,以chrome目前的状态,我也只能在公司里用用,写写wiki,看看报告,换到外网复杂的网络环境,chrome还不能胜任。首先,firefox有Adblock plus扩展,对广告的拦截效果非常好,用过它的人已经离不开它,chrome就没有类似的东东。其次,firefox下的鼠标手势,增强标签浏览,内嵌IE等,都是日常需要的功能,chrome也不行。再还有,似乎chrome的稳定性还是有待提高,偶尔还是会出现阻死在那儿的现象。
  要是哪天firefox能弄得这么快就好咯!

2009-01-15

用xmind画思维导图

  最近几次在网上看到有人提到xmind这个东东,今天偶然看到有安装包,也不用上网下载了,就装来试了试,是一个基于Eclipse RCP开发的,简单看了下,快捷键的使用习惯上跟MindManager是一样的,其他的常用功能也基本都有,而且还有官方的中文版本,又是免费的,实在是最佳的思维导图绘制工具。
  之前试过FreeMind、MindMapper和MindManager,最终觉得MindManager最好用,但这三个只有FreeMind是免费的,用盗版的MindManager心里总有点疙瘩。现在好了,换xmind了,哈哈!

2009-01-14

ACE还可以try...catch

  今天稀里糊涂地试了一下,把线程里的run_reactor_loop给try了一下,居然有用,欣喜若狂。现在想想真是糊涂啊,这也太扯了吧!
  不过话说回来,那块的代码在上次写完后,一直心有余悸,不敢再去动它,也不愿意再看它,有点反胃的感觉。今天下决定看了看,还真发现几处bug,有没关闭句柄的,有没有添加消息映射的而处理代码却是写好了的。今天这么一整,至少崩溃问题又要少一些吧!
  虽然现在用ACE已经勉强能够运行了,可我还是有点想再换回用boost::asio去。当年用boost::asio也不知道是哪里出错了,死活不能好好地连接上再互相发点数据,现在有了这点儿用ACE的基础,而且比起当初boost::asio也是正式release的版本很久了,也许一方面是我用得不对,也有可能另一方面是因为我用的从svn里直接check out出来的代码确实有点儿问题呢!

2009-01-13

有点儿疯

  星期天去参加1783的石头河溯溪,结果被小路晃点了。溯溪回来跑去南山找阿布拿手机,然后去他家吃晚饭,见到了他老婆,真没想到啊,他儿子居然2岁多了,真以为是90后的呢!吃完饭,坐了一会儿,一群人决定去唱K,结果不知道喝了多少酒,反正第二天是谁把我送到的士上的我也不记得,迷迷糊糊回到家,倒头就睡,于是,一天没上班!
  今天去上班,不知怎么的,情绪很低落。整了个版本给人测试,发现根本没法用,几个严重问题,崩溃、崩溃、还是崩溃!以前的设计有问题,或者说压根没考虑到后来会需求会变化那么大,这实在是可以原谅的。这次还有一个重大的变化是,模板文件中加入了宏,于是用COM操作后再关闭会问你要不要保存修改,尝试了半天,发现只要先关闭workbook,再关闭workbooks,再退出就可以了。因为关闭workbook时有个参数可以指定是否要保存修改,而且关闭workbooks时则没有,会自行弹出个消息框来确认。

2009-01-10

浮云

  昨天晚上是网络产品线的新年晚会,下班后从公司坐车去宝安体育馆。今年的待遇好很多,每个座位上都有一包小东西,里面有几个小面包,一盒牛奶,还有造势用的东东。
  晚会内容倒是没什么新意,年复一年,于我无关。
  一切皆是浮云。

2009-01-09

继续Lua

  又看了一会儿PIL,以及云风blog上的一些文字,以及Lua Manual等等,终于有点眉目了。
  完全可以下放权力给Lua,让它来创建wxScintilla对象,然后通过某些方法,把这个对象返回给C++,在C++看来,就是一个lua_touserdata吧。
  至于如何保存和索引这个userdata,从PIL中看到,可以保存到registry中,也可以在registry中再多加一层索引,放个weak table。而索引方式,可以用一个C++指针,在Lua看来是lightuserdata,作为索引的最好方式之一,不过我还没想明白,到底怎么转化为实际生产力。倒是看清楚了些registry的操作方式,就是一个表,只不过该表在Lua栈中的索引是固定的值。还看清楚了些weak table的工作方式,weak table,也许叫weak hash或weak map更能让习惯用C++的人明白些,里面的每一条记录,都是一对key和value,而这个weak table可以将它的key,或者value,或者两个都是设置成weak的。weak的意思是说,假设我这个值(key,或value,到底是哪个,要看这table的属性,把metatable中的__mode域设成\"k\"或\"v\")已经没人用了,就把这条记录在垃圾回收时都删掉。原来就是这么一回事,PIL中说,应用lightuserdata时,要结合weak table来用才好,并一笔带过举了个窗口消息处理的例子,我没看明白,晕!其实,我想我是知道作者的意图,我在Lua中创建了userdata,一方面要能方便地供C++和Lua两边都能使用,另一方面,要防止Lua自作主张地把userdata回收掉。用weak table大概就是为了能让程序员来控制userdata销毁的时机吧。

2009-01-07

看PIL一脸茫然

  今天没干什么事,看PIL去了,结果一脸茫然。昨天兴奋地以为用luabind就能把那些问题都解决了,实际上高兴得早了点,而且我也想偷懒到极致,用SWIG生成胶水代码后,再在C++和Lua中进行无差别地使用对象。我现在的应用场景是,在C++中创建了对象,如何在需要的时候,调用Lua脚本的某个函数时,在那函数中能方便地访问到那个对象进行操作。看LuaTinker是可以把C++对象直接映射到Lua脚本的某个全局变量中去,不过我感觉使用全局变量不太好,而且是C++在主动做事。我希望的是这样一种形式,C++在调用Lua函数时,把该对象的指针作为参数传递过去,Lua函数能根据这个参数取得实际的对象,进行操作。在网上又看了些文章,感觉这应该是一种很常用的用法才对,也不知道别人是怎么用的。
  初步的想法是C++创建对象时,用lua的newuserdata来分配空间,在这空间内进行创建。然后有了个地址,即指针,把这对指针和数据保存起来,可能用得到设施有registry和weak table,还有lightuserdata这种东西,但具体怎么用,还没搞明白。看PIL云里雾里,还是得写些代码实验一下。