Boost logo

Boost :

From: John Torjo (john_at_[hidden])
Date: 2003-04-24 03:30:47


Hi all,

Is anyone interested in a slightly better assertion technique?
(to be used only in debug mode, of course)

It struck me when BOOST_CHECK_EQUAL macro.
What if I want something like: assert( (i == j) || (k < 0) );
If this fails, I would like to see some output similar to:
(2 == 2) || (5 < 0).

So, I came with a solution:
Transform assert( (i == j) || (k < 0) ); into
OP_ASSERT( (i op_ == j) op_ || (k op_ < 0) );
( op_ is a macro, of course).
Which means: prepend each operator with 'op_'
(in order to keep the assertion kind of readable)

A simple example to ilustrate the technique follows:
(the output when the assertion fails is a little scrambled;
 don't let that cool you off - it will get much better)

If I improve the technique a little,
OP_ASSERT( (i op_ == j) op_ || (k op_ < 0) ) can generate output similar to
this:

(i == j) || (k < 0)
(2 == 2) || (-5 < 0)
             ^ assertion failed here

-------------------------------------------------
#include <assert.h>
#include <sstream>
#include <iostream>

// helper
struct test_helper
{
    test_helper( std::stringstream & out, bool & bTestOk )
        : m_out( out), m_bTestOk( bTestOk) {}

    std::stringstream & out() const { return ( std::stringstream &)m_out; }
    bool & test_ok() const { return ( bool&)m_bTestOk; }
private:
    std::stringstream & m_out;
    bool & m_bTestOk;
};

template< class type>
struct value_keeper
{
    value_keeper( const type & val, std::stringstream & out, bool & bTestOk)
        : m_val( val), m_out( out), m_bTestOk( bTestOk)
    {}

    std::stringstream & out() const { return ( std::stringstream &)m_out; }
    bool & test_ok() const { return ( bool&)m_bTestOk; }

    const type & m_val;
private:
    std::stringstream & m_out;
    bool & m_bTestOk;
};

// note: operator+ (could have been any operator; anyway,
// we just need it to return a value_keeper, which will
// further be compared.
template< class type> inline value_keeper< type> operator + ( const type &
first, const test_helper & second)
{ return value_keeper< type>( first, second.out(), second.test_ok() ); }

template< class type1, class type2>
    inline bool operator==( const value_keeper< type1> & first, const type2
& second)
{
    first.out() << " (" << first.m_val << " == " << second << ") ";
    first.test_ok() &= ( first.m_val == second);
    return ( first.m_val == second);
}

template< class type1, class type2>
    inline bool operator||( const value_keeper< type1> & first, const type2
& second)
{
    first.out() << " (" << first.m_val << " || " << second << ") ";
    first.test_ok() &= ( first.m_val || second);
    return ( first.m_val || second);
}

template< class type1, class type2>
    inline bool operator<( const value_keeper< type1> & first, const type2 &
second)
{
    first.out() << " (" << first.m_val << " < " << second << ") ";
    first.test_ok() &= ( first.m_val < second);
    return ( first.m_val < second);
}

#define op_ + test_helper( out_str, bTestOk)

#define OP_ASSERT(x) do { \
    std::stringstream out_str; \
    bool bTestOk = true; \
    (x); \
    if ( !bTestOk) std::cout << "Assertion failed: \n'" << #x << "'\n" <<
out_str.str() << std::endl; \
    } while(0)

int main()
{
    int i, j, k;
    k = 1;
    i = 2;
    j = 3;
    OP_ASSERT( i op_ == j );
    // ... note: output a little scrambled, we can fix that!
    OP_ASSERT( ( j op_ < i) op_ || (k op_ < 0) );

    std::cin.get();
    return 0;
}

-------------------------------------------------

Best,
John

--
John Torjo
-- "Practical C++" column writer for builder.com.com
Freelancer, C++ consultant
mailto:john_at_[hidden]

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk