All Stories

马峦山溯溪

  今天去溯溪了,在一个叫马峦山的地方,早上7点半起了床,出门大概8点,之前想得好好的,到梅林关去坐车到银湖汽车站,再转车到小梅沙,可是到了出门了,就突然改变主意了,觉得转车两次也太麻烦了。于是直接到小区对面坐380B,可以直达目的地小梅沙海洋公园。以前没坐过,对需要多少时间也没有什么概念,但总觉得保守估计也是2个小时应该绰绰有余了。结果实践证明,大约1个半小时就够了,可能是因为早上路上车少人少开得快的缘故吧。  大概提前了半个小时就到了目的地,倒是在车上遇到2个同事,他们本来是参加另一个户外团的活动的,就是出来晚了,人家已经出发不等他们了,于是就让他们加入我们的团。  最后出发的时间基本上比预定的晚了近20分钟,唉!这次溯溪路程不长,而且人太多了,中间遇到好几个其他的团的人。这次大概就走了2、3个小时就到了吃饭的地方,结果等到吃饭的时候,又是差不多3个小时后了,而且最最重要的是,味道实在不咋的,而且7个菜,最后还要平均每人25元,划不来啊!  下山就快多了,不过真的是上山容易下山难,路比较陡,而且很滑,得非常小心!比较累,呵呵。

谁是最可爱的库

  这个标题很恶心,不过暂时想不出更能容易我现在心情的话来了,就先这么着吧。我就是想表示一下对boost的崇敬和感激之情!  昨天发现用boost.graph可以解决我的问题。今天还是继续昨天的遗留问题,boost.graph确实跟STL那样,提供了抽象的,类型无关(真的无关么?我不确定,但我猜应该是)的算法,但是为了能用它,也着实费了我好些功夫。  我只是为了用它的DFS算法,找出一个有向图中的环。为了配合它的使用,我把之前的代码都丢掉了,重新写了一遍,不过逻辑似乎更清晰了。为了取得里面的back edge,要提供一个回调函数,而这个回调函数的参数类型都是模板参数,而这个参数却是包含了我需要的back edge信息。这就困扰了我大半天。翻了很久的帮助文档,也胡乱看了不少它的examples,最后终于发现有一个source函数和target函数,可以取出edge中的前后两个vectex。  解决了,就像一个同事说的,舒了一口气!  昨天还顺便统计了一下我在工程中主动用到的boost库,一共有9个,分别是utility、foreach、bind、function、lambda、graph、conversion、format、tribool,如果没有boost,我的日子应该会难过得多吧,呵呵。

牛哄哄的boost.graph

  偶然发现需要一个遍历有向图的算法,而且遍历不是主要目的,而是为了找出所有环。翻了一下《算法导论》和《算法概论》,对遍历算法有了个大概的印象,用DFS或BFS就可以,不过要找出环,就晕了。  同事说只要记住游走的路径,如果在一条路径遇到已经访问过的节点,就是环了。虽然说起来简单,但我想用代码实现起来我还是觉得吃力的。  另一个同事说,用boost.graph好了,我早想到了,但出于天生的那点畏惧感,总觉得没那么简单。抱着试试看的心理,看起boost.graph的文档来,惊讶地发现,原来这些常用的图算法都已经封装好了,只要以规定的格式准备好图的数据信息,然后调用一两个函数,就能实现遍历或找出环,实在是太牛了!

用boost.function和boost.bind解耦

  话说昨天被XML摆了一道,发现一种不同模块间交互的方式,就是使用回调。在早期的C语言实现中,回调函数的使用极其简单,Windows的API中就有很多使用这种方式的,可以是一个普通的全局函数,也可以是一个类的静态函数。在C++时,可调用体多了一种类成员函数,这种调用体在被调用时依赖于一个实例指针,因为编译器会自动在该函数的第一个参数前再插入一个函数,即实例指针,作为回调,就稍微麻烦一点。关于可调用体的封装和泛化,可以参考《Modern C++ Design》,中文版《C++设计新思维》。  昨天最后的结果是用boost.function,不过并没有发挥出它应有的作用,还是傻乎乎地把实例指针也传过去了,这跟直接使用原始的成员函数指针没有什么区别。后来想起来,有boost.bind的配合,可以把一个可调用体再作封装,以boost.function的形式而行为上类似于那些脚本语言的closure。在这个案例中,就是把实例指针绑定上去,这样另一个模块就根本不需要知道回调函数的原始类型了,无论是C风格的全局函数,还是类成员函数,在boost.function和boost.bind的联手作用下,都是一样的外表。这带来一个极大的好处,两个模块从此彻底解耦了。

又被XML摆了一道

  要做一个配置界面,用户通过该界面可以修改一些系统配置项。本来配置项是一直都有的,不过不需要用户修改,都写死在配置文件中,这次有了新需求。大体上分成两个部分,一部分用于操作配置文件,另一部分当然是用户界面!  配置文件是XML格式的,用MSXML组件的CString封装版本来操作,用这样封装版本的好处主要有三点:一、所有接口都用CString而不用BSTR,方便与MFC程序交互;二、封装的方法中有一些简单的出错处理;三、有一种简单的用于跟STL中算法适配的迭代器,方便操作节点列表。  问题出现了,图省事,我直接在界面类中获取配置操作类中的DOM数据结构,想直接在界面类中遍历XML,结果发现每个节点想取属性时都会异常。开始怀疑是迭代器封装有bug,于是换了几种访问元素的方法,还是异常。后来猜测难道是不同线程使用COM的原因,想想也没有多线程啊,加上CoInitialize和CoUninitialize,还是异常!于是病急乱投医,想会不会把操作XML的代码全都放在配置操作类中,就好了呢。说干就干,因为要根据读出的配置信息操作界面,所以需要一个通知界面类的方式,我选择了callback,又因为callback是个界面类中的一个成员函数,所以越弄越复杂,最后为了能少写点代码,或者说看起来直观点,动用了boost.function,其实不用它也不会麻烦多少。好不容易全都改过来了,还是异常!我已经接近崩溃的边缘了,再异常就是我神经要异常了!  所谓柳暗花明,最后无意间看了看配置文件的内容,猛然发现根节点下的第一个子节点是条注释!用DOM解析时,注释也是一种节点啊,昏,耗费掉我差不多2个小时,又被XML摆了一道。

又一次尝试失败

  周末无聊,就尝试着用VS以外的工具来写代码,都决定用MinGW+makefile了,所以不存在编译器的问题,不过最后还是放弃了,因为代码编写的原因。  尝试了Eclipse3.4、Netbeans6.5,Nightly Build的Code::Blocks和CVS snapshot的CodeLite,总之一句话,写C++程序,VS+VA的王道!确切地说,其实我是离不开VA了。  今天在公司的论坛看到别人在讨论代码编辑器要怎么做,有一个老员工列举了他用编辑器的需求,我都觉得那已经不是编辑器了,而是一个IDE了。  看来,如果要有好用的IDE,除了VA,只有自己做了……

深受MS毒害的代码民工

  昨天下班为止,也没把预定的版本发出去,因为预定的需求没有完全实现,一个大的特性不能正常工作。所以今天上午就跑去公司捣腾了一把,两个小时就搞定了,比较安慰的是架构没有错,只是几处很细微的细节上犯了迷糊,尽管调试到解决了花了近一个半小时。  我有点怀疑的是有人说TDD的最高境界是不用调试,但之前的经历已经证实,我尽管有单元测试,但单元不通过时,我还是需要调试来查找原因!难道就因为我TDD没到最高境界?我想狡辩,不全是我的原因,肯定是他们的业务逻辑太简单了。这样又扯到另外一个问题,为什么我的业务逻辑会复杂,照Martin Fowler大叔的说法,如果一处代码太复杂了就,就要分解成几个简单的。可是以我现在的水平和智商看来,这是个悖论,分解后还不是要再组合在一起,仍然是复杂的,仍然逃不过调试的命运。但我又怕万一有不幸的人看到这篇文字,会提出另一个观点是,如果组合在一起仍然是复杂,那是架构有问题。那刚好把问题的责任转移到另一处,哈,我争辩不过。不过还好,我一点都不排斥调试,因为不得不赞美几句MS,他们做的VS里的调试器太好用了,或者说他们已经做出了世界上最好的调试器,别拿那些啥gdb之类来的扯淡。  在公司的时候突然想起来,我要用MinGW+wxWidgets写GUI程序,是否可以用Eclipse+CDT呢,从此可以摆脱MS的诱惑了。下午回到家整蛊了一个小时,不得不说,我还是继续用VS+VA吧,我想到的也就是个自动补全、智能提示功能而已,可惜Eclipse+CDT还做不到那样。  总之,我是个深受MS毒害的代码民工。

rdoc流程

  今天听同事讲了rdoc的处理流程,他完全是通过阅读rdoc的源代码来理解的,而且似乎他根本没有多少编译原理的基础的,汗!  总的说来,通过他的讲解,我了解到,rdoc的处理流程基本也是分为词法分析和语法分析阶段。首先,应该说有一个词法分析器生成器,叫SLex,然后由RubyLex提供规则,其实是一组正则表达式,SLex可以说是有一个正则表达式引擎,根据这些规则,生成token,提供给RubyParser使用,基本上是lex/flex做的事情。然后RubyParser根据这些token解析出class和model,建立起相关的实例,解析出相关的注释信息,通过yaml写到文件中。这里有点虎头蛇尾了,不过大体上也能表达清楚想要的意思了。  比较佩服的就是同事仅凭这些ruby源代码,就看到流程来,我怀疑要我上的话,如果没有这些编译原理的知识作铺垫,应该达不到他这样的程度吧!

SVNLabel

  今天把CruiseControl上的label改成svn的revision号了,在公司发现个问题,有一个项目里总是不能正确取到svn的revision号,虽然构建了,但从web上看不到结果,没有日志,晕!回到家,也改了一下,又发现另外一个限制,其实这个可能要算是svn的限制,当时我把源代码从vss迁移到svn时,所有项目都是放在一个目录下的,所以现在svn里各个项目的源代码commit后revision号是累加的,并不是各个项目独立的,于是在CruiseControl上显示的就是几个项目用了相同的revision号了,晕!不过第二个问题应该影响不大,因为照我现在的应用场景,一个时刻只会对某个项目的源代码修改,只要CruiseControl是一直开着的,出现这种冲突的机会也不多。