开发者

How to print additional information when assert fails?

开发者 https://www.devze.com 2022-12-19 15:06 出处:网络
Often one wants to print out additional information if an assert fails. A way to d开发者_开发知识库o that is this:

Often one wants to print out additional information if an assert fails. A way to d开发者_开发知识库o 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?


#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


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. :)


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


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.


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)


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().


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


I use either an if statement or wxASSERT_MSG from wxWidgets.

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


I made this for plain C, based on Notinlist answer (thank you!):

my_assert.c:

void _assert_int(char *astr, char *oper, char *bstr, int a, int b, char *file, int line) {
  printf("\nAssertion failed: %s %s %s\n%s = %d\n%s = %d\nfile: %s\nline: %d\n", astr, oper, bstr, astr, a, bstr, b, file, line);
  exit(1);
}

void _assert_str_equal(char *vara, char *varb, char *a, char *b, char *file, int line) {

  if (a == b) {
#ifdef TREAT_BOTH_NULL_STRS_AS_ERROR
    if (a != 0) return;
    goto loc_failed;
#else
    return;
#endif
  }

  if ((a == 0) || (b == 0) || (strcmp(a, b) != 0))  {
loc_failed:
    printf("\nAssertion failed: %s == %s\n%s = %s\n%s = %s\nfile: %s\nline: %d\n", vara, varb, vara, a, varb, b, file, line);
    exit(1);
  }

}

my_assert.h:

#define TREAT_BOTH_NULL_STRS_AS_ERROR

#define assert_int(left,operator,right) do { if(!((left) operator (right))) _assert_int(#left, #operator, #right, left, right, __FILE__, __LINE__); } while (0)

#define assert_str_equal(left,right) do { _assert_str_equal(#left, #right, left, right, __FILE__, __LINE__); } while (0)

usage:

assert_int(a,==,b);

assert_str_equal(str1,str2);

check also the seatest unit test framework


How about using "fprintf" to replace the std::cerr? "fprintf" returns number of characters printed (>= 0 when succeeds) and you may use it like this (very easy to remember and clear to read ... at least for me):

assert(vec.size() > i || (fprintf(stderr, "False: %d > %d\n", vec.size(), i) >= 0);

You can then make it a inline function (in C) for convenience, name it like "assert_vecSize_largerThan(...)".

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号