作者: admin

  • 大连Python用户组活动简记

    在王元毅(@yuanyi_wang)孜孜不倦的努力以及大连天维科技李丕总经理的大力赞助支持下,大连Python用户组终于在一个大雨滂沱的早晨成立了!

    大连这几天都是湿漉漉的,动辄就是下雨刮风。我从家里出来的时候还没有下雨,结果车坐到一半就开始大雨瓢泼,等到下车的时候,车站路边积了很深的水,只有趟着过才行。

    天维科技就在七贤岭车站边上的高层办公楼,16楼。我去的时候还不到九点(约定开会时间九点半),这一带高楼大厦那是多多,可就是没有买吃的地方,与其等着不如去买点吃的,就跑到黑石礁买了麦当劳早餐。结果雨越下越大,再回到天维科技办公楼的时候已经是九点五十,会议室里已经坐了不少朋友,扫一眼大概有八九个人。

    大家闲聊了一阵,然后元毅主持会议开始,首先是各自做自我介绍,除了twitter上的ouland、gamtin,其他朋友不是很熟悉。除了天维的李总,基本上都是搞程序开发的,还有几位朋友是做测试方面的工作。

    介绍完了各自情况,元毅做了一个对Python的简单介绍。然后我做了一个Lua的简单介绍以及代码简介。

    大家除了讨论Python的相关问题,也对天维公司www.5itvi.com非常感兴趣,我在李总作介绍的时候问了不少问题,主要是对他在贝尔实验室的经历非常感兴趣,要知道贝尔实验室那是牛人多多啊。

    最后李总给我们演示了天维媒体中心这个产品。这里要着重提一下,天维这个产品有些类似盛大以前的盒子,可以非常高效使用高速宽带,实现游戏、视频点播、高清播放甚至网络电话的这样一个平台。由于不知道产品细节适不适合在这里细说,就不多说我看到的演示过程,但是可以说这个产品是非常有意思的,就我个人而言,对其中网络电话这部分很感兴趣,遥控器可以作为网络电话的话筒。用一句话来说就是:我很有对这个产品的购买欲望。天维这个产品的界面以及服务器部分使用Python完成,就我所知是大连地区唯一一个使用Python做产品的公司。

    整个活动其实还是比较简单,没有其他地区用户组活动比较深入的讨论,这是很遗憾的事情,但是比较符合大连整个软件产业的现状。尽管官方大力吹捧大连是所谓的软件产业为主,个人觉得这里面水分太多,所谓的软件开发大部分是日韩或者欧美的外包业务,少有核心研发业务,而且官方几乎也没有组织过什么软件开发方面的论坛或者研讨会。唯一参加过的就是微软在大连的广告宣传(而且也挺水的)。我在讨论中也稍微有些激动的提到,对大连成立Python用户组那是期待已久,其实讨论Python也好,研究DotNet或者Java也罢,Topic是一部分,最重要的是大家有这样一个平台可以线下互相交流。Python大连用户组的成立填补了我这个遗憾,即使现在大家还没有什么深入的内容讨论,也许将来讨论的也未必高深到哪里,但是我真的很爱这种可以跟其他喜欢技术的朋友交流的感觉。

  • C语言中的表达式求值问题

    在细读《C programming Language 2nd》(K&R)到53页的时候,看到作者举了这样一个例子:

    a[i] = i++;

    如果你知道这个表达式有什么问题,就不需要继续看下去了,下面内容对你而言有些浅显。

    如果你也像我一样,觉得这个很容易理解啊,i++这个表达式就是先取i的值返回,然后对i自加。a[i]就是i的值,然后i自加1。

    这其实也是C语言陷阱之一,在K&R中反复强调(page52以及page202),函数参数也好、某个操作符中的操作数也罢,表达式求值次序是不一定的,每个特定机器、操作系统、编译器都不一样。(特例是&&,||,?:以及逗号操作符,它们会确定表达式求值顺序的)

    还有类似的例子如下:

    f() + (g() * h())

    或者 int i = 7;  printf("%d\n", i++ * i++);

    在我们一开始提出的问题中,a[i]取下标操作与i++自增的运算顺序是不一定的。这就是一种不确定性。

    在第一个表达式中,可以确定的是这些内容:g和h函数的结果会先做乘法运算,然后与f函数的结果做加法运算,但是f,g,h谁先被调用,谁后被调用,这是不一定的,C语言标准没有对此作规定。

    第二个表达式中,第一个自增操作和第二个自增操作以及乘法操作的顺序是不一定的,所以结果根本无法确定,即使我们给i++都包裹上括号 (i++) * (i++)也是一样的。括号并不会得到确定的操作数计算顺序,括号只能保证操作数的值(就是表达式或者函数求值的结果)相互计算的顺序。

    K&R提供了几个建议,首先是函数调用嵌套赋值语句(或者可能改变参数值的操作)或者自增操作,都会有"side effect”,我们应该确保这种边际效应不会影响程序运行结果,如果某个表达式对同一个变量同时修改两次,那么一定要非常注意这是不是你想得到的结果。

    如果不知道特定机器上实现如何,就不要依赖表达式计算顺序;即使知道了实现方式,这种依赖也不是一种好的编程方式。比如f、g、h函数计算,可以用赋值给临时变量来决定需要的顺序,对于print(i++)这个表达式也是如此,在printf之外先计算i的值。用K&R第一版的话来说就是if you don’t know how they are done on various machines, that innocence may help to protect you.

    参考资料:

    http://www.eskimo.com/~scs/cclass/notes/sx7c.html

    《C Programming Language second edition》

  • Visual C++ Project uses asm files

    As you know, C++ project could embed asm in function. But in some cases, we want to use asm file in our project. How to do?

    1, open the project, and insert existing xxx.asm file into project. Or you could generate new file and name it as "xxx.asm".

    image

    2, in C/C++ source code, if you want to use the function, just add declaration before using it. Please note: in C++ code, extern "C" should be added (like above image).

    3, edit and save the asm function.

    image

    4, In asm1.asm custom project setting, add following in command line:

    ml /c /Cx /coff -Zi "-Fl$(InputDir)\$(InputName).lst" "$(InputPath)"

    Add following in outputs:

    $(InputName).obj

    image

    Try to compile the asm1.asm, the asm1.lst and asm1.obj files should appear under project folder.

    5, Build the solution. All done.

    If there are some errors, you could try to copy the asm1.obj to "Debug" or "release" folder, if it could fix the issue, you could modify the path of command line and outputs setting of asm1.asm.

  • 软件开发中单元测试unit testing

    尽管现在已经有了大量的软件开发方法论及辅助工具帮助开发团队提高软件质量,防止、检测bug,但是一些很简单实用的手段依然是提升软件质量必须的手段,比如单元测试,比如Code review。

    单元测试是一种很基本的软件质量保证方法,随着敏捷开发、持续集成的流行,一个高质量软件,如果没有单元测试是无法想象的。比如Java,C#都有非常成熟的单元测试框架,Google也开源了他们的C++单元测试框架以及mock框架。

    这篇文章将探讨几个方面的问题,什么是单元测试?单元测试为何如此重要?什么时候进行单元测试?如何进行单元测试?单元测试要注意避免什么问题?

    什么是单元测试?其实单元测试的本质就是assert,比如c语言就有内置的assert函数,在不满足某个条件的时候返回错误码,而MFC内置的ASSERT和VERIFY就更强大。如果你已经使用assert函数,其实你已经进入了单元测试的初级阶段,稍微学习一下就可以掌握单元测试方法了。对于C++编程,可以使用google的googletest框架做单元测试,这种unittest框架的好处是集成了单元测试常见的需求,避免了重复开发。

    常见的开发任务大致为两种:维护legacy code或者偶尔加入一些小的feature,另外就是搭建一个新软件。这两种任务都不可避免的要反复修改某个feature代码,或者根据测试人员或用户的反馈修改bug。我们如何保证修改以后的代码没有引入新的问题?如果你说你用人品担保,那我服了。对于一个正规流程来说,应该有一种正式的手段来确保修改一个bug没有引入两个三个新的bug,或没有导致以前正确的功能出错,这就是单元测试的重要性,有了足够的单元测试,你就可以理直气壮的说新代码没有问题。

    单元测试另一个重要性是帮助你理清设计。对于反对单元测试的一个常见借口是,我们的应用太复杂了,没法写单元测试。不是应用复杂,其实是软件设计有问题,导致没法测试,可测试性也是软件很重要的一个本质特性。如果设计中保证了某个函数某个接口只完成单一责任,没有过多的耦合依赖,那么测试其实是很简单的事情。

    单元测试另一个优点是可以集成到持续集成过程中,或者通过脚本简单快速反复运行,不需要手动干预,这对于提高开发效率而言非常重要。

    什么时候我们应该写单元测试?是软件代码都写完了,实际运行的时候再写么?单元测试其实应该在设计阶段就开始写,单元测试完成以后再写实际功能部分代码。设计应该是基于接口设计,单元测试也应该基于接口测试,另外针对某些复杂的内部逻辑,也应该有比较多的单元测试保证覆盖率。对于某些核心部分,单元测试的代码甚至应该超过实际代码。而且要注意的是,应该将单元测试部分的代码与工作代码等同看待,一样要做版本管理放入ClearCase或者SVN,而且单元测试部分的代码也要review,保证测试代码也是正确的。个人感觉单元测试(包括部分集成测试代码)在整个代码实现部分要占30%到40%的任务量,这样才比较正常。引入单元测试会在前期导致一些延迟,这是无法避免的,相应的会大大减少后期的维护工作,这是我自己的亲身体会。

    那么该如何写单元测试呢?单元测试能不能测GUI点击输入?首先要明确的一点是,单元测试不会替代其它测试手段,单元测试只是白盒测试的一种。单元测试是由开发者编写测试,保证正确完成某个逻辑功能的一种测试方法。而且单元测试不应该涉及到其它外部依赖或者其它的模块,比如GUI点击、网络通讯、数据库通讯或者需要安装某个第三方软件等等,这就需要开发者做好设计,尽量把可能有耦合依赖的部分提取隔离,在集成测试或者其它测试的时候再检查。

    我们用一个简单的例子解释一下。某个GUI界面,当按下一个按钮,它要变成另一种颜色,功能完成以后恢复原状,或者是一个控件允许用户输入,输入完成以后校验,根据校验的结果保存或者提醒用户出现问题。这些都是比较常见的流程。这些流程显然不是原子的(atom),涉及到model、View、control各个方面,某些程序员往往在CXXXDialog这样的类里面实现所有这些功能,还感觉封装的非常好,“这不是面向对象封装了么?我把它们都封装到类里面了啊?!”

    我们就拿输入校验来说明一下如何分解这个MVC过程。第一步,用户输入,点击OK。这部分显然是View和Control方面的,这部分可由tester方面做检查测试,开发人员需要保证功能实现完整,简单运行正确即可。第二步,检查输入数据,进行逻辑运算。这部分显然是比较复杂的逻辑,涉及到Model和Control,一般不涉及到界面显示,输入部分就是数据,输出部分就是检查的结果。显然,这部分应该做单元测试。第三步,如果出错,反馈给用户出错的结果。这一部分基本上也是以界面显示为主,是需要tester测试的部分。第四步,数据正确,保存用户的输入。这部分涉及到数据运算,也是可以进行单元测试的,涉及到数据库的部分,可能需要做集成测试。

    从前面的分析可以看出,涉及到GUI界面的测试一样可以有单元测试,需要开发人员做更多的工作,抽象逻辑计算部分代码。这不容易实现,但是值得去做。

    当进入后期开发阶段,当用户或者测试人员发现问题,开发者就应该把这些问题转化为测试用例,这样既保证了修改后的代码没有导致其他bug重新出现,也是对代码逻辑的一种很好的覆盖。针对某些需要依赖其他模块的功能,我们可以mock接口,也可以编写实现模块间的集成测试。另外,开发者还可以进一步定制自己需要的单元测试框架功能,比如我就针对现在工作的项目,设计了一个灵活的添加测试用例的方案,测试用例用类似ini或者xml格式编写,单元测试程序读取测试用例进行测试。这样引入一个新的测试用例就非常容易方便,不需要修改编译代码。当然这种设计也是针对我们项目的输出主要为COM接口而定制的,更像是一种集成测试。

    前面就是一些泛泛之谈,没有涉及到实际技术方法,只是鼓吹了单元测试的优点。希望各位程序员或技术领导能更加重视单元测试,在工作中使用单元测试,让它真正成为日常工作的工具来保证软件的高质量开发。