3

等待!- 这并不像听起来那么愚蠢的问题。标题很简洁。

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

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

后来在一个方法中:

assert(Sorted);

例如。

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

这会导致一些混乱的代码,其中必须声明根本不应该存在于 Release 构建中的方法并具有主体,以便编译关闭的断言。

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

更多信息:

  • 我尝试用 包装方法声明{$ifopt C+},它检查断言是否打开。调用assert仍因“未声明的标识符”而失败。

  • 编译器选项肯定是关闭了断言。(我检查了 :))

  • 我已经尝试assert{$ifdef DEBUG}. 但是,这是混乱的,不应该是必需的。在某一时刻,它让我担心即使在发布版本中也会编译断言,出于性能原因,我根本不想要它们。(这没有发生 - 没有生成断言代码。)

  • 我目前的策略是一直声明这些方法,但是在 Release 构建中,如果定义方法体,则用一个虚拟结果填充它。这里的目的是编译所有断言,但方法的开销尽可能小,并且它们的返回值(如果它们实际上在发布版本中被调用)显然是错误的。

  • 在 Delphi 中是否有任何与 C/C++ 风格的宏等效的ASSERT(x)东西,其中一个宏在发布版本中被简单地定义为空,导致编译器既看不到也不关心断言中的语句?这是在 C++ 中使用宏的少数简洁方法 (IMO) 之一。

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

4

2 回答 2

4

不要从您的发布版本中排除代码。将代码保留在那里,并无条件编译。

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

试图排除与断言相关的代码只会使您的代码更加混乱,因为它需要条件编译指令。

断言和调试信息是正交设置。不调试时可以启用断言,反之亦然。

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

于 2013-04-01T18:13:31.350 回答
3

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

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

正如在其他答案和许多评论中所指出的那样,断言并不是调试所独有的。断言在发布代码中也很有价值,可以验证绝对不会发生的情况。

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

// 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 包装器污染您的源代码,并保证您的发布代码中不存在调试实现。

于 2013-04-02T22:11:59.850 回答