几天没有正儿八经写代码了,今天又咬牙写了一会儿。在完成一个新功能后,开始重构原来的代码,把其中用于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中,但那实在是遥遥无期啊!
Category Archives: CPPOOPGPXP
炮轰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上去。总之,现在怎么整都可以了。
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方式的好。不过我想,我在这方面的顾虑很小吧。
Loki::Factory挺好用
这两天在做一个特性,其中一段很经典的根据不同的类型id来创建不同的对象的代码,想到了《Modern C++ Design》中的那个实现,马上找来Loki用。
非常简洁的代码,定义一个基类,然后就可以根据类型id来创建对象了,如果创建对象的时候需要一些参数,也很容易,另外定义一个Loki::Seq就可以了。
每个派生类可以在源文件中用一个匿名的namespace保护起来进行注册,这样就彻底达到了只要加入该源文件,就有该类,不加入该源文件,就没有该类。不需要修改其它任何代码。
挺好用的!
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在等待的时候,会有死锁的现象!
我狂分特啊!到底是哪里出错了,而且这样的问题还都不是必然重现的,郁闷哇!