Enhanced Assertions[1]

[入库:2005年8月19日] [更新:2007年3月24日]

本文简介:选择自 pongba 的 blog

                                enhanced assertions
                                        --by andrei alexandrescu and john torjo
                                        --ppliu_echo 译

    --这篇与john torjo合著的文章描述了一个特性完备的,工业强度的assertion设施。这个工具包的特性包括多重debug级别,日志记录,还有一个搜集详细状态信息的方法。

    好吧,我承认:我正在体验"writer's block"。材料都在这儿,很酷。我享用了我最喜爱的早餐(自制的牛奶什锦早餐,我的私人配方:50%燕麦片,50%坚果,50%葡萄干--试着干掉它们),我所信赖的open source编辑器正在动人的闪烁着(我知道你在想什么:“动人的”?滑稽透顶),然而,我无法想出一个好的介绍性的开头。为了缓解我的焦虑,亲爱的读者,原谅我在文章的开始就用这些滑稽的话来令你烦恼。然而这是一个meta-programming专栏,不是么?再加上你是个c++程序员,所以你几乎不会介意一些额外的语法。

    既然我们已将第一段的问题放至一边,请允许我向你介绍我的朋友john torjo,c++专家,顾问,"practical c++"的专栏作家(http://buider.com/),我们长期由e-mail来往。john和我合著了这篇文章,描述了经john的改善过的assertion framework。

    john在读过我的关于assertions的文章[1](其大部分是jason shirk和我讨论的结果)后,发现它有所欠缺。更准确的说,他发现他自己需要assertion设施提供更多的特性。这一切是因为我那篇关于assertions的文章和附的源代码使用了simple world assumption的环境。这是个非常专业的术语,你可能也可能没有听过它,所以请允许我将它说得详细一点。

    在simple world assumption下,程序员们有正常的工作时间,有合理的进度表。他们有时间并且被鼓励去进行代码评测。因此代码中任何失败的assertions都会在单件和整体的测试中招来一连串的批评。程序员测试并调试代码,确保在全部测试环境中都没有assertion失败,最终在ndebug宏被定义的情况下编译并将小而快的可执行文件发送给他们的项目经理,然后项目经理再将它发送到恰当的消费者基群。顺便一提,在simple world assumption之下,项目经理被认为有能力帮助程序员完成他们的工作,并且不给他们施加压力和负担。(正如它所表现出来的,simple world assumption在现实中并不存在)

    在一个更为现实的世界中,程序员得忍受进度表所带来的相当大的压力,于是写单件的测试被取代为直接将没怎么经过测试的程序不负责任地扔给black-box测试组,而后者为bug出现的情况写bug报告。

    指出一段会进展至bug出现的事件序列并不总是简单的。在多种情形并存或多线程,事件驱动下的编程会使bug的重现变得相当困难。使用未初始化变量所产生的随机行为,错误的转型,或者缓冲区溢出只是增加了一些调味料而已。咳,我几乎忘记了名目繁多的系统配置,比如被安装的dll和注册表设置...(曾经写过一个能在你的系统上完美运行却在另一个上面神秘失败的应用程序吗?)

    一个对这种情况有所帮助的方法,john说,是设计一个更好的assertion framework来扩展assertion的能力。明确如下:

      * 存在assertions的多重级别、设计错误比白纸黑字更明显。在一个极端,拥有最多的checks,而随着软件的成熟只有最少的会失败。在另一个极端,有"低成本,高效用"的checks,你可以将它们为专业测试人员、beta版的测试者、有时甚至可以为你的软件的最终用户保留着。我个人并不喜欢让最终用户看到assertion message,但是john令我相信这种情况是很可能发生的。此外,继续往下读,因为失败的check可以有多种方式来报告。

    * 仅仅显示消息是不够的,特别是在开发和测试组分开工作的时候。一个日志记录的设施是极其有用的,这样,在某一次运行失败后,开发者就能够看到哪个assertion(或者哪些assertion)失败了。

    * 消息的质量有极大的改善,不仅包括有关失败的表达式、文件、行的详细信息,还包括相关变量的值(在程序员的控制之下)。

    所有这些额外的特性的加入组成了一个工业强度的assertion工具包,在原来的assertion代码之上,john添加了一些在我的文章"enforcements"中也被用到的技巧,还有许多属于他自己的技巧。我们会在下面详细介绍经改善过的assertion工具包是如何工作的。

提供额外的状态信息(providing extra state information)
    当一个assertion失败时,就意味assert表达式被求值为false。然而,你可能常常想要了解到底是哪个关键变量的值导致了assertion的失败。例如,考虑在某个你确信两个string都为空的时刻,你写出如下代码:

      string s1,s2;
      ...
      assert(s1.empty()&&s2.empty());

    如果这个assertion失败了,你很可能想要知道mystring(译注:泛指string对象,如s1或s2,下同)里面到底保存了什么,这能提供对它最后一次更新地点的洞察。john的framework允许你以如下的语法做到这一点:

      smart_assert(s1.empty()&&s2.empty())(s1)(s2);

    注意到圆括号的使用提供了一个给assert以额外的参数的方式,并且这是可扩展的,这与enforce[2]类似。(当然,enforce并非是以这种特殊的风格使用operator()的第一个组件)

    当这样做的时候,如果assertion失败了,显示并被记录为日志的消息看起来像这样:

      assertion failed in matrix.cpp: 879412
      expression: 's1.empty()&&s2.empty()'
      values: s1="wake up,neo"
              s2="it's time to reload."

    这就是魔法如何工作的。准备接受一些很棘手但却无疑是值得了解的东西吧。(为了能够理解并受用,你需要唤醒你内心深处对宏的热爱)首先,基本的想法是:要获得变量名称和变量的值,你需要使用"stringizing operator #"(译注:一种特殊的操作符,能使其后面的文本变成c/c++字符串形式,如#class 被编译器替换为"class"),在以上的例子中,你需要对mystring使用stringizing operator。但是它只能在宏内部使用,并且以下的事实让事情变得很棘手:你需要一个能“无限扩展”的宏机制--一个被扩展开来的宏仍然可以继续作为一个宏(这样你才能收集更多的变量s3,s4...的信息)。但是我们都知道这种递归式的宏并不能工作。

    然而,如果你能在踩下油门的同时打开汽车顶棚并用你的左手举起天线,你就会发现这技巧虽然困难但毕竟是可行的。荣誉属于paul mensonides,是他(就我们目前所知)发明了这个技巧。这儿是你所需要做的:

    首先,在你的assert类(这个类和我前一篇文章里定义并使用的同名的类很相似)内部添加两个成员变量smart_assert_a和smart_assert_b。它们的型别是assert&。

      class assert
      {
         ...
       public:
           assert& smart_assert_a;

本文关键:C++,generic,泛型
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top