All Stories

该死的_SECURE_SCL

该死的_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编译不了资源

bjam编译不了资源

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

遭遇VC不被Boost承认

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

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

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

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

想起那些人

想起那些人

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

sqlite3的Blob操作,变异……

sqlite3的Blob操作,变异……

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

在CI中使用bjam构建项目

在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编译脚本中去。