2013-03-11 66 views
1

目前我正在用CppUnit編寫C++單元測試。最近,我需要檢查一個例外是在特定的情況下,使用CppUnits宏拋出:CppUnit預計異常與斷言拋出警告編譯C4127

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) 

好吧,我完全理解它是如何工作的,而循環的做我只執行一次,因爲該錯誤,並且該中斷用於不執行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

+2

最有可能無能,但也可以是一種編碼風格。順便說一句,這不是與cppunit唯一的問題。嘗試比較未簽名的短整型而不投射。 – 2013-03-11 07:51:31

+0

我建議改爲更好的單元測試框架(如gtest) – 2013-03-11 07:58:23

+2

,這不是一個有用的提示,因爲許多其他約束可能導致多年前決定在一個大型項目中使用CppUnit,並且編寫了數千個單元測試在這個框架內...但從頭開始,我想我不會選擇CppUnit了,對。 – 2013-03-11 08:03:32

回答

1

OK,我想我找到了答案由我自己:

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) {/ * macro stuff * /} ,則擴展到 ; 其他// < <從未執行,因爲;以上。

所以我想我必須禁用警告。海灣合作委員會有這方面的工作(({ MACRO })),稱爲Statement Expression,但我不認爲這適用於VS2010。

+0

請參閱我的編輯以下我的答案 - 如果您有C++ 11支持,則可以使用lambdas – 2013-03-11 09:25:01

2

原因是要斷言一個聲明。考慮宏觀的這兩個用途:

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

在他們的代碼,你會得到一個錯誤與//b,因爲代碼擴展到do { ... } while (false) doSomething(); - 你丟失的情況後;

與您的代碼,//b將愉快地編譯,但//a可以給你一個「空語句」的警告,因爲該行會後擋;擴大到{ ... };,與superfluos。

爲什麼他們您使用//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捕獲您在表達式中使用的變量。

+0

Lambdas是一個很酷的主意,但不幸的是我不能在這個項目中使用C++ 11: -/ – 2013-03-12 05:54:49