All Stories

看到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。  由此我想到我们现在的自动升级程序,都是完整文件下载替换的,土了啊!

Loki::Factory挺好用

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

AST

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

PCLint应用尝试

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

该死的_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了!

线程问题

  发现前段时间写的那些代码很不稳定,总是会崩溃!今天被人翻出来批判了一把,惭愧啊!  问题出在多线程上。一个编辑器对象创建后,会创建一个新的线程来分析编辑器中的内容,然后把分析后的结果保存在成员变量的。这时如果在保存结果之前,编辑器对象已经被销毁了,则访问成员变量也是不可行的,所以我在编辑器对象被销毁的时刻(析构函数中)强行打断线程的执行。  另一个问题,同样是编辑器对象创建后,要创建一个新的线程来分析编辑器的内容,然后根据分析后的结果,来对编辑器做些特定的操作。这里我使用了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在等待的时候,会有死锁的现象!  我狂分特啊!到底是哪里出错了,而且这样的问题还都不是必然重现的,郁闷哇!

《实现模式》

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

bjam编译不了资源

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

遭遇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来验证的吧!