Currently I am writing unit tests in C++ with CppUnit. Recently I needed to check that an exception is thrown in a specific case using CppUnits macro:
CPPUNIT_ASSERT_THROW(
boost::get<FooClassInBoostVariant>(m_boostVariantFooOrBar),
boost::bad_get);
the warning during the compilation of the test surprised me (on VS2010, but will be a warning on other compilers as well...):
warning C4127: conditional expression is constant
I looked into the macro definition of CppUnit and found the following:
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 )
Well, I totally understand how this works, the do while loop is executed only once, because of the false, and the break is used to not execute the Asserter::fail() part. But why are they doing it like this? It - of course - triggers the compiler warning, as the break condition for the while loop is obviously always "false". But isn't there a more elegant way to do this? I usually adhere to the no-warning-compilation principle, so this really bugs me.
So my question really is, why didn't they implement it like this:
{ \
bool cpputExceptionThrown_ = false; \
try { \
expression; \
} catch ( const ExceptionType & ) { \
cpputExceptionThrown_ = true; \
} \
\
if ( !cpputExceptionThrown_ ) { \
CPPUNIT_NS::Asserter::fail( \
"Expected exception: " #ExceptionType \
" not thrown.", \
CPPUNIT_SOURCELINE() ); \
} \
}
Thanks in advance!
-Hannes
OK, I guess I found the answer by myself:
http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ gives the explanation.
It is actually a common practice to wrap multi-line macros in do { } while (false);
. This is a workaround to allow for using those macros, e.g., in un-braced if else
.
if (condition_a)
MULTI_LINE_MACRO();
else
MULTI_LINE_MACRO_2();
The result would be that unexpectedly only the first line gets executed, which definitely leads to unexpected behaviour. So they were not entirely incompetent I guess...
http://kernelnewbies.org/FAQ/DoWhile0 also explains why my solution would not work. The MULTI_LINE_MACRO();
within the if would expand, e.g., to
if (condition_a)
{ /* macro stuff */ }
;
else // << never executed because of the ; above.
So I guess I have to disable the warning. GCC has a workaroud for this ( ({ MACRO })
), called a Statement Expression, but I don't think this works on VS2010.