views:

331

answers:

8

Often one wants to print out additional information if an assert fails. A way to do that is this:

assert(vec.size() > i || 
  !(std::cerr << "False: " << vec.size() << ">" << i))

This way the actual sizes are printed when the assert fails. But it's ugly, and also it's easy to forget the ! , which will make the assertion condition true and the program will just continue.

What do people use instead to print additional information on assertion failure, like above?

+3  A: 
#define ASSERT(condition) { if(!(condition)){ std::cerr << "ASSERT FAILED: " << #condition << " @ " << __FILE__ << " (" << __LINE__ << ")" << std::endl; } }

Usage:

ASSERT(vec.size()>1);

Result:

ASSERT FAILED: vec.size()>1 @ main.cpp (17)

You can optionally put DebugBreak() or exit(-1) or watever into the macro, depending on your needs.

Updated macro with separated left and right side:

#define ASSERT(left,operator,right) { if(!((left) operator (right))){ std::cerr << "ASSERT FAILED: " << #left << #operator << #right << " @ " << __FILE__ << " (" << __LINE__ << "). " << #left << "=" << (left) << "; " << #right << "=" << (right) << std::endl; } }

Usage:

ASSERT(a,>,b);

Result:

ASSERT FAILED: a>b @ assert2.cpp (8). a=3; b=4
Notinlist
But it will print the actual size of the vector, not "vec.size()", right?
At least for g++ this code still gets included for -DNDEBUG (assert isn't).
honk
@dehmann: It will print `vec.size()` not 235. You do not need more because you can call DebugBreak() or whatever so you will have a debugger at the right place and you can watch it. @Benjamin: You can define the `ASSERT()` macro as empty depending on an other macro's existence.
Notinlist
Right idea -- put the extra info into the definition of assert. (Typical implementations already do this.) But there are subtleties in getting the definition just right, and this one doesn't have all the details quite right. Game Programming Gems Vol I has some good articles on building a quality assert macro.
Adrian McCarthy
Hmm, I'd like it better if it actually printed `235` or whatever the size is, like the code I gave in the question. That might save me time since I might not actually have to start the debugger (the program might run for a long time before it actually reaches that point).
In this case you have to handle the left side, the right side and the operator separated. You can construct a similar macro like that. #define ASSERT(left,operator,right) { if(!(left operator right)){ ... << #left << "=" << (left) << "; " << #right << "=" << (right) << ... } }
Notinlist
+1  A: 

assert() compiles to nothing in Release build of many compilers. It is not something that has any value for production code.

I use a construct like this:

#include <cstdlib>
#include <vector>
#include <algorithm>
#include <ctime>
#include <iostream>
using namespace std;


template<typename T> inline bool Verify(T const& t,char const* Expression, char const* File, unsigned long Line)
{
    bool b = !(!t);
    if( b )
        return true;
    // verify failed -- report it 
    std::cerr <<  "Assertion '" << Expression << "' Failed @ " << File << ":" << Line << endl;
    return false;
};

#define verify(exp) (bool)( Verify(exp, #exp, __FILE__, __LINE__) )


template<typename Iter> void doit(Iter const begin, const Iter & end)
{
    for( ; begin != end; ++begin )
        ;
}

int main()
{
    int n = 1;
    n *= 2;
    verify( n == 3 );
    return 0;
}

Program Output:

Assertion 'n == 3' Failed @ .\main.cpp:32
John Dibling
warning: unreferenced function `doit` :)
avakar
+4  A: 

What do people use instead to print additional information on assertion failure, like above?

Generally I'd just add a string literal describing the meaning of the condition:

assert(v.size() > i && "The vector really needs to be larger");

But perhaps a macro like this:

#include <cassert>
#include <vector>
#include <iostream>

//#define NDEBUG

#ifndef NDEBUG
#define ASSERT_EX(condition, statement) \
    do { \
        if (!(condition)) { statement; assert(condition); } \
    } while (false)
#else
#define ASSERT_EX(condition, statement) ((void)0)
#endif

int main()
{
    std::vector<int> v;
    unsigned i = 1;
    ASSERT_EX(v.size() > i, std::cerr << "i = " << i << ", v.size() = " << v.size() << '\n');
}

Here it would be nice, though, if statement wouldn't have side-effects, changing how condition evaluates. :)

UncleBens
Note that macros don't play well with commas in the operands — careful about that…
Potatoswatter
+1  A: 

Most extended assertion handlers are of the form:

assert_x(CONDITION,EXPLANATION);

what you want is something along the lines of

assert_args(condition, explanation, ...);

So:

extern string build_assert_string(const string&, explanation, ...);

#define ASSERT_ARGS(CONDITION,build_assert_string EXPLANATION)

call as:

ASSERT_ARGS(x > 0, ("x should be > 0 but it is %d", x));

The function build_assert_string is trivial.

lilburne
A: 

I use something like this:

#define ASSERT(lhs, op, rhs) assert_template((lhs##op##rhs), "(" #lhs #op #rhs ")", lhs, rhs, __FILE__, __LINE__)

template <typename t1, typename t2>
void assert_template(const bool result, const char expr[], t1 lhs, t2 rhs, const char file_name[], const long line_number)
{
    if (!result)
    {
        std::cerr << "Assertion failed";
        std::cerr << "    " << expr;
        std::cerr << "    lhs = " << lhs;
        std::cerr << "    rhs = " << rhs;
        std::cerr << "    File: \"" << file_name << "\"";
        std::cerr << "    Line: " << std::dec << line_number;

        throw "Assertion failed";
    }
};

The use syntax is a little weird as in ASSERT(vec.size(), >, 1) or ASSERT(error, ==, 0). The upside is that it also prints out the values of the left and right hand side. On Windows I also like to throw in GetLastError() and WSAGetLastError().

kipkennedy
A: 

Here's what I use, breaks on the actual line that failed, rather than elsewhere in the stack. Works on MSVC and GCC, and uses a little boost magic and generates an assertion dialog:

#include <boost/current_function.hpp>

#if defined(NDEBUG)
# define MY_ASSERT(expr) ((void)0)
#else
    int assertion_failed(char const *expr, char const *function, char const *file, long line);
# if defined(_WIN32)
#  define debugbreak __debugbreak
# else
#  define debugbreak __builtin_trap
# endif
# define MY_ASSERT(expr) ((expr) || !assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__) || (debugbreak(), 0))
#endif

#if !defined(NDEBUG)
int assertion_failed(char const *expr, char const *function, char const *file, long line)
{
#if defined(_WIN32)
    return ::_CrtDbgReport(_CRT_ASSERT, file, line, NULL, "%s", expr);
# else
    return !0;
# endif
}
#endif
Matt Joiner
A: 

I think the following makes sense. Instead of this:

assert(vec.size() > i || 
  !(std::cerr << "False: " << vec.size() << ">" << i))

just do this:

assert(vec.size() > i || 
  assert_msg(vec.size() << ">" << i));

where assert_msg is defined as something like this:

#define assert_msg(x) !(std::cerr << "Assertion failed: " << x << std::endl)
A: 

I use either an if statement or wxASSERT_MSG from wxWidgets.

If you use a framework, see if it provides some useful assertion tools.

Thomas Matthews