Extreme Debugging - Smart asserts

22b3033832c5c699c856814b0cf80cb1
0
bladder 101 Sep 12, 2004 at 01:18

I read this article a while back about using recursive preprocessing to enhance the capabilities of an assertion. The article was written by one Andrei Alexandrescu, the author of Modern C++ Design. Here’s a direct link to the article that explains the recursive process in details, and the following is the full and IMO minimal implementation of it. This is an excellent tool to have with you. It greatly increases the information you can get out of an assertion.

// Header file.

#ifndef CUSTOM_ASSERT_H_
#define CUSTOM_ASSERT_H_

#include <string>
#include <fstream>

#ifndef __FUNCTION__
#define __FUNCTION__ "???"
#endif

#ifndef __FILE__
#define __FILE__ "???"
#endif

#ifndef __LINE__
#define __LINE__ 0
#endif

class CustomAssert
{
    std::string m_Expression;
    std::string m_Filename;
    int m_Line;
    std::ofstream m_File;
    int m_Count;

public:
    
    CustomAssert& CUSTOM_ASSERT_A;
    CustomAssert& CUSTOM_ASSERT_B;

    CustomAssert();
    ~CustomAssert();

    template< typename T > CustomAssert& Print( const char* str, T val )
    {
        m_File<< "<font face="verdana" size="1" color="#DDDDFF">    "<< str;
        m_File<< ": <font color="#dd88ff">"<< val<< "</font></font><br>";
        return *this;
    }

    CustomAssert& Msg( const char* str );

    void Break();

    CustomAssert& operator () ( const char* expr, int line, 
                                const char* func, const char* file );
    
}; 

extern CustomAssert g__SmartAssert__;


#define CUSTOM_ASSERT_A(x) CUSTOM_ASSERT_OP( x, B )
#define CUSTOM_ASSERT_B(x) CUSTOM_ASSERT_OP( x, A )

#define CUSTOM_ASSERT_OP( x, next ) CUSTOM_ASSERT_A.Print( #x, (x) ).CUSTOM_ASSERT_ ## next


#ifdef _DEBUG

#define Assert( expr ) 
    if( (expr) ); 
    else g__SmartAssert__( #expr, __LINE__, __FUNCTION__, __FILE__ ).CUSTOM_ASSERT_A 

#else

#define Assert( expr ) 
    if( true ); 
    else g__SmartAssert__( #expr, __LINE__, __FUNCTION__, __FILE__ ).CUSTOM_ASSERT_A 

#endif 

#endif // CUSTOM_ASSERT_H_

// Source file

#include <cassert>
#include "assert.h"

#undef CUSTOM_ASSERT_A
#undef CUSTOM_ASSERT_B


CustomAssert g__SmartAssert__;


CustomAssert::CustomAssert()
    : CUSTOM_ASSERT_A(*this), CUSTOM_ASSERT_B(*this)
{

    m_File.open( "asserts.htm" );
    m_File<< "<html>
    <HEAD>
    <CENTER>
    <TITLE>Asserts</TITLE>
    <H3><font face="verdana"color="ffffdd">Asserts</font></H3>
    </CENTER>
    </HEAD>
    <BODY bgcolor="000000" text="ffffff">
                  <BR><BR>";

    m_File.flush();

    m_Count = 0;
}

CustomAssert::~CustomAssert()
{
    m_File.close();
}

CustomAssert& CustomAssert::operator () ( const char* expr, int line, 
                                          const char* func, const char* file )
{
    m_Expression = expr;
    m_Filename = file;
    m_Line = line;

    static char buffer[32] = {0};

    itoa( m_Count, buffer, 10 );

    m_File<< "<p><b><u><font size="2" face="verdana" color="#ff0000">#";
    m_File<< buffer;
    m_File<< ": "<< expr<< "</font></u></b><br><br>";
    m_File<< "<font size="1" face="verdana"><li>file: <font color="#C1F0FF">";
    m_File<< file<< "</font><br>";
    m_File<< "<li>line: <font color="#C1F0FF">"<< line<< "</font><br>";
    m_File<< "<li>function: <font color="#C1F0FF">"<< func<< "</font><br></font>";

    m_Count++;

    return *this;
}

CustomAssert& CustomAssert::Msg( const char* str )
{
    m_File<< "t"<< "<font size="1" face="verdana" color="#aaff88"><b>msg: </b>";
    m_File<< "<font color="#ffaa88">"<< str<< "</font></font>";
    return *this;
}

void CustomAssert::Break()
{
    m_File.flush();
    _assert( (void*)m_Expression.c_str(), (void*)m_Filename.c_str(), m_Line );

}

The code is readily compilable on msvc. Im not sure of other compilers because Im not sure if they have the same prototype to make an assertion box appear (Called in the Break() function). An example of using the assert follows:

int a = 0;
int b = 3;
int c = 32;
char* str = "string";
float f = 3.45f;
double d = 345.7;

Assert(0 == 1)(a)(B)(c).Msg("Passing ints");
Assert(3 == 4)(f)(d).Msg("Checking floats");
Assert(3 != 3)(str).Msg("Checking string variable");
Assert(0 && 0)(a)(B)(c)(f)(d)(str).Msg("Checking ALL and breaking").Break();

4 Replies

Please log in or register to post a reply.

218341be2587d9bdef38af0c2066c308
0
Francois_Hamel 101 Sep 13, 2004 at 01:33

interresting…
but the style is very ugly :)

6ad5f8c742f1e8ec61000e2b0900fc76
0
davepermen 101 Sep 13, 2004 at 04:39

i’ve played around with such code for a while, by myself, too.. i wouldn’t have mixed it with the logging directly, thought.. but for the example purpose it’s fine..

E26db8686cae4ec080e0c9e4c2d18676
0
knackered3 101 Sep 15, 2004 at 12:05
m_File<< "<font face="verdana" size="1" color="#DDDDFF">  "<< str;

doesn’t compile out of the box.
you’re missing some \ where you have quotes in quotes.

Fdbdc4176840d77fe6a8deca457595ab
0
dk 158 Sep 15, 2004 at 14:05

knackered: the code initially contained the backslashes, but it seems a bug in the syntax highlighter removed them. This has been fixed, but hasn’t been updated on the site yet.