<

Unique assertions

Published on
4,888 Points
1,788 Views
1 Endorsement
Last Modified:
Assertions are very helpful to find problems or bugs. But, sometimes there's a problem
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:

 
class Lock
{
	HANDLE	GetMutex()
	{
		static HANDLE hMutex = ::CreateMutex( NULL, FALSE, NULL );
		return hMutex;
	} 
public:
	Lock()
	{
		::WaitForSingleObject( GetMutex(), INFINITE );
	} 
	~Lock()
	{
		::ReleaseMutex( GetMutex() );
	}
};

Open in new window


Here's the macro itself:

 
// 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 )

Open in new window


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): first assertion for expression 'j % 2 != 1'
> c:\test\testassert.cpp(15): known assertion for expression 'j % 2 != 1'
> c:\test\testassert.cpp(11): first assertion for expression 'i < 2'
> c:\test\testassert.cpp(15): known assertion for expression 'j % 2 != 1'


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 ); \
1
Author:Zoppo
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free