using them, for example within a loop or a very often called function, because if such an
assertion fails it may happen that tons of assertion-dialogs pop up (at least in VC++
with 'ASSERT'-macro that's quite a mess).
The following macro can be used easily just the same way as VC++'s 'ASSERT'-macro or
CRT's 'assert'. To make its usage thread-safe first I introduce a simple class which does
synchronization using a mutex in Windows - if you want to use the macro with other
OS you'll have to adapt that class:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: | class Lock
{
HANDLE GetMutex()
{
static HANDLE hMutex = ::CreateMutex( NULL, FALSE, NULL );
return hMutex;
}
public:
Lock()
{
::WaitForSingleObject( GetMutex(), INFINITE );
}
~Lock()
{
::ReleaseMutex( GetMutex() );
}
};
|
Here's the macro itself:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: | // Handle the first time the assertion fails
#define assert_once_out_first( x ) \
std::cout << __FILE__ << "(" << __LINE__ << "): first assertion for expression '" << #x << "'" << std::endl;
// Handle further assertion fails
#define assert_once_out_next( x ) \
std::cout << __FILE__ << "(" << __LINE__ << "): known assertion for expression '" << #x << "'" << std::endl;
// Generates a file-wide unique variable name
#define assert_once_var __bAsserted__ ## __LINE__ ## __
// The unique assert macro
#define assert_once( x ) \
do { \
Lock _lock; \
static bool assert_once_v = false; \
if ( !(x) ) \
{ \
if ( !assert_once_v ) \
{ \
assert_once_v = true; \
assert_once_out_first( x ); \
} \
else \
{ \
assert_once_out_next( x ); \
} \
} \
} while ( 0 ) |
The usage is just simple, i.e.:
> int _tmain( int argc, char* argv[] )
> {
> for ( int i = 0; i < 3; i++ )
> {
> assert_once( i < 2 );
>
> for ( int j = 0; j < 3; j++ )
> {
> assert_once( j % 2 != 1 );
> }
> }
>
>
> return -1;
>}
The output will be:
> c:\test\testassert.cpp(15)
> c:\test\testassert.cpp(15)
> c:\test\testassert.cpp(11)
> c:\test\testassert.cpp(15)
For VC++ it's even simple to use the macros ASSERT and TRACE by just replacing these
two macros as shown here:
> #define assert_once_out_first( x ) \
> TRACE( "%s(%d): Assertion for expression '%s'\n", __FILE__, __LINE__, #x ); \
> ASSERT( x );
>
> #define assert_once_out_next( x ) \
> TRACE( "%s(%d): Known assertion for expression '%s'\n", __FILE__, __LINE__, #x ); \