【问题标题】:CppUnit expect exception with Assert Throw compiles with warning C4127 [duplicate]CppUnit期望带有Assert Throw的异常编译并带有警告C4127 [重复]
【发布时间】:2013-03-11 07:46:00
【问题描述】:

目前我正在使用 CppUnit 用 C++ 编写单元测试。最近我需要使用 CppUnits 宏检查在特定情况下是否引发了异常:

CPPUNIT_ASSERT_THROW(
    boost::get<FooClassInBoostVariant>(m_boostVariantFooOrBar),
    boost::bad_get);

测试编译期间的警告让我感到惊讶(在 VS2010 上,但在其他编译器上也会出现警告......):

warning C4127: conditional expression is constant

我查看了CppUnit的宏定义,发现如下:

do {                                                            \
  bool cpputExceptionThrown_ = false;                           \
  try {                                                         \
     expression;                                                \
  } catch ( const ExceptionType & ) {                           \
     cpputExceptionThrown_ = true;                              \
  }                                                             \
                                                                \
  if ( cpputExceptionThrown_ )                                  \
     break;                                                     \
                                                                \
  CPPUNIT_NS::Asserter::fail(                                   \
                 "Expected exception: " #ExceptionType          \
                 " not thrown.",                                \
                 CPPUNIT_SOURCELINE() );                        \
} while ( false )

好吧,我完全理解它是如何工作的,do while 循环只执行一次,因为 false,并且 break 用于不执行 Asserter::fail() 部分。但是他们为什么要这样做呢?它 - 当然 - 触发编译器警告,因为 while 循环的中断条件显然总是“假”。但是没有更优雅的方法来做到这一点吗?我通常坚持无警告编译原则,所以这真的让我很烦恼。

所以我的问题真的是,他们为什么不这样实现它:

{                                                               \
  bool cpputExceptionThrown_ = false;                           \
  try {                                                         \
    expression;                                                 \
  } catch ( const ExceptionType & ) {                           \
    cpputExceptionThrown_ = true;                               \
  }                                                             \
                                                                \
  if ( !cpputExceptionThrown_ ) {                               \
    CPPUNIT_NS::Asserter::fail(                                 \
                 "Expected exception: " #ExceptionType          \
                 " not thrown.",                                \
                 CPPUNIT_SOURCELINE() );                        \
  }                                                             \
}

提前致谢!

-汉尼斯

【问题讨论】:

  • 很可能是无能,但也可能是一种编码风格。顺便说一句,这不是 cppunit 的唯一问题。尝试比较无符号短整数而不进行强制转换。
  • 我建议改用更好的单元测试框架(比如 gtest)
  • 这不是一个有用的提示,因为许多其他限制可能导致几年前在一个大型项目中采用 CppUnit 的决定,并且在这个框架内编写了数千个单元测试......但从从头开始,我想我不会再选择 CppUnit 了,对吧。
  • 我想没有人能告诉你为什么这段代码是这样写的。为什么不改一下呢?
  • 我做了,但我想知道是否有正当理由接受这个我不知道的编译器警告。

标签: c++ unit-testing compiler-warnings cppunit c4127


【解决方案1】:

原因是为了使断言成为一个陈述。考虑一下宏的这两种用法:

CPPUNIT_ASSERT_THROW(foo(), MyException);  // a
CPPUNIT_ASSERT_THROW(foo(), MyException)   // b - without trailing `;`!
doSomething();

使用他们的代码,//b 会出现错误,因为代码扩展为 do { ... } while (false) doSomething(); - 在条件之后您会丢失 ;

使用您的代码,//b 会很高兴地编译,但 //a 可能会给您一个“空语句”警告,因为该行将扩展为 { ... };,在块之后带有多余的 ;

为什么他们强制你使用//a我不知道-但我更喜欢//b,因为在每行之后都有一个;是一致的。不必将带有断言的行与普通语句区分开来。

PS: 我不确定,但{ ... } 块和do {...} while(false) 语句之间可能存在更多差异,这将允许将断言宏放在不允许简单块的地方。

编辑: 对于 C++11,您可以使用 lambda(在一个地方定义并调用它):

#define CPPUNIT_ASSERT_THROW(expression, ExceptionType)         \
[&]() -> void {                                                 \
  bool cpputExceptionThrown_ = false;                           \
  try {                                                         \
     expression;                                                \
  } catch ( const ExceptionType & ) {                           \
     cpputExceptionThrown_ = true;                              \
  }                                                             \
                                                                \
  if ( cpputExceptionThrown_ )                                  \
     return;                                                    \
                                                                \
  CPPUNIT_NS::Asserter::fail(                                   \
                 "Expected exception: " #ExceptionType          \
                 " not thrown.",                                \
                 CPPUNIT_SOURCELINE() );                        \
}() 

但是,可能会有一些警告,例如由于 lambda 捕获了您在表达式中使用的变量。

【讨论】:

  • Lambdas 是一个很酷的主意,但不幸的是我不能在这个项目中使用 C++11 :-/
【解决方案2】:

好的,我想我自己找到了答案:

http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/给出解释。

do { } while (false); 中包装多行宏实际上是一种常见的做法。这是一种允许使用这些宏的解决方法,例如,在无支撑的 if else 中。

if (condition_a)
    MULTI_LINE_MACRO();
else
    MULTI_LINE_MACRO_2();

结果将是出乎意料地只有第一行被执行,这肯定会导致意想不到的行为。所以我猜他们并不是完全无能......

http://kernelnewbies.org/FAQ/DoWhile0 还解释了为什么我的解决方案不起作用。 if 中的 MULTI_LINE_MACRO(); 将扩展,例如, 如果(条件_a) { /* 宏的东西 */ } ; else //

所以我想我必须禁用警告。 GCC 对此有一个解决方法(({ MACRO })),称为Statement Expression,但我认为这不适用于 VS2010。

【讨论】:

  • 在下面查看我对我的答案的编辑 - 如果你有 C++11 支持,你可以使用 lambdas
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-13
  • 2022-06-21
  • 1970-01-01
  • 2021-06-11
  • 2021-11-17
  • 2019-10-18
  • 1970-01-01
相关资源
最近更新 更多