如何在发布版本中使用依赖于仅调试代码编译的assert()?

时间:2013-04-01 17:25:33

标签: delphi delphi-2010 assert

等待! - 这听起来并不是一个愚蠢的问题。标题简洁明了。

我有一些调试代码来验证数据结构的正确性和一些断言来检查这种正确性,我只想在Debug版本中启用它:

{$ifdef DEBUG}
  function Sorted : Boolean;
  function LinearSearchByValue(const T : TType) : NativeInt;
{$endif}

以后的方法:

assert(Sorted);

例如。

在启用断言的调试版本中,一切都很好。但是,在我的发布版本中,断言已禁用,行assert(Sorted);会导致编译器错误E2003 Undeclared identifier: 'Sorted'。这是正确的,标识符未声明,但断言也被关闭,不应被评估或生成任何代码。 (试图通过声明方法来欺骗断言但没有实现导致正常错误'不满意的前向或外部声明'。它显然也在寻找方法体。)

这导致一些混乱的代码,其中必须声明Release版本中不应存在的方法并且有一个正文,以便编译断言,这些都是关闭的。

如何在Delphi 2010中声明仅存在于调试版本中的方法,并在断言中使用那些也应仅存在于调试版本中的方法?

更多信息:

  • 我尝试用{$ifopt C+}包装方法声明,检查断言是否已打开。对assert的调用仍然以'未声明的标识符'失败。

  • 编译器选项绝对是关闭断言的。 (我查了:))

  • 我尝试将assert的调用打包到{$ifdef DEBUG}使用这些方法。但是,这很麻烦,不应该被要求。有一次,它让我担心即使在发布版本中也会编译断言,出于性能原因,我根本不需要它们。 (这不会发生 - 不会生成断言代码。)

  • 我目前的策略是始终声明这些方法,但是在发布版本中if ifdef方法体并填充虚拟Result。这里的目标是编译所有断言,但对方法的开销尽可能少,并且它们的返回值(如果它们在发布版本中实际上已被调用)显然是错误的。

    < / LI>
  • 在Delphi中是否有任何与C / C ++类型的宏相当,其中ASSERT(x)宏在发布版本中只是被定义为什么都没有,导致编译器既不看也不关心语句断言里面?这是在C ++中使用宏的少数干净方式(IMO)之一。

因此,虽然没有生成断言,但它们是经过编译的。这回到我的问题:我如何最好地混合仅调试方法和断言,并发布构建?

2 个答案:

答案 0 :(得分:4)

不要从发布版本中排除代码。保留代码,并无条件地编译。

你反对让代码存在于发布版本中的论点是它“混乱”。但是你已经编写了代码,所以无论编译与否,它都会变得混乱。你也可以让编译器编译它;毕竟,编译额外的代码并不需要花费更长的时间。

尝试排除与断言相关的代码只会通过要求条件编译指令使代码更混乱

断言和调试信息是正交设置。如果没有调试,则可以启用断言,反之亦然。

另一种方法是将与断言相关的代码移动到单元测试中。然后,它们会自动从您的应用程序的所有版本中排除,但它们仍可用于测试。

答案 1 :(得分:3)

断言通常在编译过程的链接步骤中“省略”或不包含在输出可执行代码中。传递给assert函数的源代码符号和表达式必须在编译步骤中定义,以便编译器可以解析并生成断言及其表达式参数的代码。

断言是否包含在输出exe中是由链接器在将代码复制到输出文件时确定的。根据编译器,即使未包含对assert函数的调用,传递给assert的表达式仍可能包含在可执行代码中。

正如其他答案和许多评论中所述,断言并不是调试所独有的。断言在发布代码中也很有价值,以验证必须从不发生的条件不会。

允许您在释放代码中保留断言,同时使断言表达式中使用的函数仅存在于调试版本中的一种解决方案是为发布版本定义调试函数的do-nothing存根。像这样:

// Declaration:
  private function Sorted: Boolean;

// Implementation

{$ifdef DEBUG}
  function Sorted : Boolean;
  begin
// work work work
  end;
{$else}
  function Sorted: Boolean;
  begin
  end;
{$endif}


// used in an assertion:

  assert(Sorted);

这允许您使用调试代码和发布代码中的断言,使用断言周围的ifdef包装来污染源代码,并保证您的发布代码中不存在调试实现。