目前我正在用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循环的中断条件显然总是"false"。但是没有更优雅的方法来做这件事吗?我通常坚持无警告编译原则,所以这真的让我很困扰。
所以我的问题是,为什么他们不这样实现呢:
{
bool cpputExceptionThrown_ = false;
try {
expression;
} catch ( const ExceptionType & ) {
cpputExceptionThrown_ = true;
}
if ( !cpputExceptionThrown_ ) {
CPPUNIT_NS::Asserter::fail(
"Expected exception: " #ExceptionType
" not thrown.",
CPPUNIT_SOURCELINE() );
}
}
提前感谢!
hannes
原因是要使断言成为一个语句。考虑以下两种宏用法:
CPPUNIT_ASSERT_THROW(foo(), MyException); // a
CPPUNIT_ASSERT_THROW(foo(), MyException) // b - without trailing `;`!
doSomething();
使用他们的代码,您将得到//b
的错误,因为代码扩展到do { ... } while (false) doSomething();
-您将错过条件后的;
。
在您的代码中,//b
将愉快地编译,但是//a
可能会给您一个"空语句"警告,因为该行将扩展到{ ... };
,在块后面有多余的;
。
为什么他们强迫你使用//a
我不知道-但我更喜欢//b
,因为每一行后面都有一个;
。不需要区分带有断言的行和普通语句。
我不确定,但{ ... }
块和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捕获了表达式中使用的变量
好吧,我想我自己找到了答案:
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();
将展开,例如为如果(condition_a){/*宏文件*/};Else//<<从未执行过,因为;以上。
所以我想我必须禁用警告。GCC对此有一个解决方案(({ MACRO })
),称为语句表达式,但我认为这在VS2010上不起作用。