Fan 的个人资料missdeer 之编出来的幸福照片日志列表更多 工具 帮助

马峦山溯溪

  今天去溯溪了,在一个叫马峦山的地方,早上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是一直开着的,出现这种冲突的机会也不多。

穿越东西涌

  这次是从西涌到东涌这样的路线。以前听很多人说过,就是自己没去过,这次可是切身体会了一把。
  本来自己的负重差不多,不会让自己太累到,结果出发前有2个mm把一些水和水果放到我包里了,结果真的重了好多。
  今天才知道,原来东涌和西涌也就是两处海滩游泳场,穿越就是沿着海岸线从一端走到另一端。
  沿着海岸线很不好走,大半的是礁石,剩下小半的路程是在山上爬,真的是爬哦,我还带了跟登山杖去,没用上,太难走了。中途一mm要虚脱了,于是我发扬不怕苦不怕累乐于肋于舍已为人的国际共产主义绅士风度骑士精神,帮忙背上她的包,果然又重了很多!
  非常不幸的是,在从一高处跳到低处时,左脚踩到一块不平的石头上,于是扭伤了。不幸中的大幸是,已经离终点并不远了,咬咬牙总算也坚持下来了。
  拍了一些照片,总的说来,海景很不错,怪不得总会有拍婚纱照的要去海边,今天就碰到两对,呵呵。

软件工程大会

  今天上午去听了软件工程大会的几个议题,发现挺没意思的,没有多少收获。大部分都是炒冷饭,没有激情,没有让人耳目一新的感觉。全是敏捷来敏捷去,持续集成来持续集成去,TDD来TDD去的。
  于是下午就没去了,写了几行代码,把log4cxx加到工程中了,用xml格式的配置文件,看起来比较统一,所有的配置文件都是用xml的,哈哈,不过我的配置文件也太多了点。发现还是不能直接在代码中用unicode,编译时没问题,但是链接不通过,不知道什么原因,难道是哪里设置的问题。不过中文倒是可以了,在家里编译的不行,输出全是乱码,而且同事也一直说是乱码,不知道是编译的时候用的操作系统影响的,还是编译器影响的。

感觉像是一种耻辱

  不分还好,一分就郁闷了,感觉像是一种耻辱。

软件可测试性设计

  可测试性设计,即所谓的DFT,今天又一次重重地感觉了一次它的重要性。
  系统中有一个权限管理模块,同一个物理用户可以不同的逻辑角色登录系统,系统中以各个逻辑角色为最小管理单元进行权限管理。一般情况下,用户可选择的逻辑角色范围是极有限的,绝大多数用户应该有只一个逻辑角色,当然在测试情况下,情况刚好反过了,基本上每个物理用户会有几个逻辑角色,容易暴露出问题。主要集中在,角色拥有的权限不正常,比如看到了不该看的,看不到该看的,可以做不该做的,做不到该做的。
  一直以来,对这块的调试/测试都是很困扰我的问题,主要就是DFT没做好。最开始,把所有业务逻辑和界面绑定在一起,差不多每一条规则都要在界面上点来点去,构造很多与真实环境类似的数据去测试,非常麻烦,不容易遍历到各种情况。
  后来将这模块重构了,把业务逻辑和界面分离,用单元测试来保障逻辑的正确性。从DFT的角度讲,这种代码架构方式,就是一种改进,对DFT的改进。
  有了单元测试,还不能保证程序能完全正确地运作,因为除了逻辑正确,还有可能其他地方出错,比如输入数据就有问题。在前些天他们测试的一个版本中,依然发现了一些权限相关的问题,让我觉得比较郁闷,因为我一直以为应该是万无一失了的。不过事实摆在眼前,也逃不掉,今天老老实实地在那里定位,不过开始的时候还是异常困难,因为测试场景的千奇百怪,我的开发环境下,有些场景很难模拟。
  后来突然灵光一闪,我应该为了方便测试,多写一点代码,多加点功能,比如当前这个问题,我可以加一个特性,能在某种特定条件下,使我可以以任意角色登录系统。说干就干,果然很容易地定位到了测试发现的几个问题的原因。
  今天的经历告诉我,在软件可测试性设计方面,有很多值得研究的东西的,天呐!

炒冷饭

  说话那天还在抱怨嵌入Python真辛苦,后来突然灵光一闪,马上解决了那个执行外部脚本文件的问题!既然现在已经能做到这一步了,接下来就应该考虑,能利用这个能力做些什么事呢?
  总的说来,现在我已经有了嵌入4个不同风格各有千秋的脚本语言解释器的经验:Lua、TCL、Ruby、Python。以后如果有需求,我估计TCL是不会再使用了,总觉得它的语法太枯燥了点。剩下三者,对Lua和Ruby的了解稍微多一点点,但也还没达到能用来做独立应用的程度。相比之下,Ruby的那种怪异变态的语法,还是比较吸引人的,还有就是它带了那么多稀奇古怪的库,可以大大减少重复劳动,这比Lua来说,省事不少。但Lua的轻巧,却一直让我觉得是嵌入的最佳语言。实在让人难以取舍呀!
  想了想,WIND就只用Lua嵌入了,因为这是一个相对比较追求性能的应用。而 WallpaperHelper其实用什么似乎没多少影响,而且照现在的趋势,WallpaperHelper的体积已经不成为主要关心的问题了,功能才是最重要的。另外还想过要写打谱程序的,也要嵌入扩展。StoneBase的功能让人比较满意,但从写代码的角度看,它的架构不行,可扩展已经成了继续发展的必经之路了。但我还没想好,是用MFC,还是WTL,或者wxWidgets来写呢?

让SMTP支持中文

  项目里要有发送邮件的功能,想法非常简单,公司有个SMTP服务器,通过它直接就可以发送到notes中了。网上SMTP的源代码也是一堆一堆的,找了一个CodeProject上的,加上base64编解码也只有4个文件,接口非常简单,于是稍微改了一下,base64本来我就有,就只需要加入2个文件就可以了,附件也能正常发送,很是满意。不过上周五的时候突然发现,主题和内容中的所有中文都变成乱码了,有点气馁,随便又找了几个其他的代码,也是一样。
  今天闲起无事,就想改一下。只知道在哪里看到过说要用base64,就能解决这个问题。尝试着先把主题编码了,发现收到后就是编码后的字符串了,这也是预料之中的。上公司的论坛发了个帖,没人回复。后来自己忍不住了,用Outlook Express写了个邮件,主题和内容都是中文,然后打开Wireshark,抓包,比较了一下CodeProject上那个代码发送的,看出区别了,原来也就是加上个charset="gb2312",再base64,就搞定啦!

嵌入Python真辛苦啊

  两年前,就嵌入过python,那时宿主程序是用C++Builder开发的,python还是2.4,没用第三方库,完全用python C API直接操作,而且当时要暴露的接口不多,才4个C函数,当然还是费了不少力气。嵌入python的目的是为了主程序可以实现外部脚本扩展,只有一个很简单的应用场景,点击一个菜单项,就执行一个py文件。首先是要让它能执行起来,python文档有很多C API,例程却极少,记得当时连接1个半月每天至少到后半夜2点才睡,最后累得上wc连掀马桶盖的力气都快没有了。而且很大的问题是,在家里自己的电脑上终于可以跑起来了,拿到公司的电脑上却不行,每天只能晚上回家调,白天再到公司测试,很郁闷,我已经忘了最后是怎么搞的,反正是最后发现在公司的电脑上缺少一个site.py文件,直到这一刻才发现,这是python官方发布包中的一个文件,当时是自己放了一个0字节的文件在那里搞定的。那次这么多精力的投入真是不值,做出东西来本来想得个满分的,结果只打了个C,而且当时指定了4名专家评审,结果只有2个专家发表了评论,另2个还没评论就走到下一环节,直接C了,唉!
  今天又看到很久之前写的计算hash值用的小程序CryptTool,核心功能早已完成,就是计算字符串,或文件的hash 值,包括多种算法,比如MD2、MD4、MD5、SHA1、SHA256等等等等。后来出于尝试的心理,想给程序加上外部脚本扩展的功能,准备支持 Lua、Python、Ruby三种脚本,而且不自己写粘合代码了,而是采用SWIG自动生成。Lua和Ruby的支持很快就完成了,可是Python的却一直有问题,网上翻了不少代码,以前C++Builder中的那个代码也翻出来,都是有问题,那时就搁下来,今天又心血来潮地想弄一下。
  最主要的困难还在于对Python缺少了解,但这并不能成为借口,Lua和Ruby不就好好地嘛!这里我用的方法是把外部脚本文件作为 module,import进来,但是一直返回一个空指针。实在没办法,PyErr_Print打印的内容又是输出到控制台的,而我的是个标准的MFC写的GUI程序,所以只好另外建了个控制台的测试工程,幸好模块独立性较好,几个文件拷过去略作修改就可以了。通过控制台输出的出错信息才知道,原来是 import时,还会连带着import一些python自己的库,于是把python的lib目录添加到sys.path里。另外,如果说import 这外部脚本文件时,报找不到该名字的module,就把脚本文件所在的路径也添加到sys.path里。终于可以在import时,执行一遍该脚本了!不过还有另外一个问题解决不了,就是调用Py_Finalize崩溃!在网上找了一圈,也有一些老外遇到这个问题,不过要么就是没有下文,要么就是给出的解决方案不适合我这种情况。因为我是import来执行脚本的,当没有Py_Finalize,而同一进程内再import时,就不会再执行了,作为外部扩展,有一个办法是定义一个基本规范,脚本中一定要实现一个指定名字的方法,再由宿主程序来调用这个方法,但这太不友好了。其实如果能用 PyRun_File之类的API来强制运行一个文件的话,也是可以达到目的的,但还是绕回原来的路上去了,我这里PyRun_File这类API死活不能正常执行一个脚本文件,天呐!
  于是还剩下一个遗留问题,怎么能解决脚本每次都能执行的问题呢?嵌入Python真辛苦啊!

杨柳坑快乐游

  部门组织去杨梅坑游玩,之前只看别人去玩过,自己却没去过,所以在出游前,一直有点儿兴奋。
  早上等车等错了地方,有点囧,有点恼。坐了一个多小时车,终于到了目的地。每人都去挑了自行车,好久不骑了,有点感怀。租好了车,却一直不出发,等几个自驾车过来的人,有点不耐烦。估计等了有十几二十分钟吧。
  一路逆风,而且风很大,路上看到不少前面的人被风吹掉的旅游帽。大概骑了半个小时,到了目的地,一个别墅,再过去就是悬崖了,悬崖不高,下面是海!在这么偏僻的地方看到悬崖,不禁想起很多肥皂剧里的那些跳崖的剧情,有点无语,当时的人们想像力还真是不够丰富啊。
  悬崖上也风很大,拍了几张合影,也没什么玩的,就回去了。回去是理所当然的顺风,大概15分钟就当了,然后就是吃中饭。中饭比较合我口胃,是海鲜,但称不上大餐。价格不便宜,但也算不上贵。海鲜也是最常见的虾、蟹、鱼、花甲、扇贝,其他的就没什么值得说了,不过我还是吃了3碗饭,哈哈。
  吃过中饭,就去桔钓沙。上一次去桔钓沙应该是2006年9月9日吧,记得当时小丫头不在深圳,说得她很遗憾的样子,小丫头应该很喜欢去海边玩吧。这次时节不适合下水了,风也很大,于是我只是躺在席子上用手机上网看小说,就这么过了一下午,直到4点半,打道回府。