Chris
Chris

Reputation: 3405

Assert with dynamic message?

In my program I want to use asserts that show an error message. Apart from the well known workarounds for C and C++ there's the "real" solution as BOOST offers BOOST_ASSERT_MSG( expr, msg ) (see also assert() with message)

But a static message isn't enough for me, I also want to show sometimes the failed variables, e.g. in a case like

BOOST_ASSERT_MSG( length >= 0, "No positive length found! It is " << length )

As you can see I'd like to format the message "string" as an stringstream or ostream as that'd allow me to easily show custom types (assuming I've defined the relevant formating function).

The problem here is that BOOST_ASSERT_MSG is by default requiring a char const * so that's not compatible.

Is there a way to redefine / overload assertion_failed_msg() in such a way that using a stream as message will work? How?
(My naive approach failed as the compiler first wanted to do an operator<<("foo",bar) on the message itself...)

Upvotes: 13

Views: 10747

Answers (5)

Vladislav
Vladislav

Reputation: 394

If you are working on Windows only, you can take a look to assert macro. Under the hood it uses _wassert. You can write your own assert macro using it. For instance in my case if I get some point, I want to show assert without conditions:

#ifdef DEBUG
    const std::wstring assert_msg = /* build the string here */;
    _wassert(assert_msg.c_str(), _CRT_WIDE(__FILE__), (unsigned)(__LINE__));
#endif

I think on other OS you can do the same trick, just take look at assert macro.

Upvotes: 0

Mateo
Mateo

Reputation: 1604

Here is a solution that doesn't rely on macros. Instead, it uses a tiny bit of templating and lambda syntax.

template<typename Fn> 
void assert_fn( bool expr, Fn fn) {
  if (!expr) {
    fn();
    abort();
  }
}

The argument fn can be any callable.
For instance you can call it like so:

assert_fn( a==b, [&](){ cout << "Assertion failed: a="<< a << 
                                " is different from but b=" << b << endl; } ); 

The advantage is that the output is that you are not calling abort explicitly and the output is fully customizable. The this advantage, of course, are the seven extra characters of lambda function boilerplate: [&](){} )

Upvotes: 3

rahul.deshmukhpatil
rahul.deshmukhpatil

Reputation: 997

I use the BOOST_ASSERT_MSG with my own wrapper around it, so that specifying the assert message with multiple operator<< seems less complex.

#if defined ASSERT_ENABLED 

    #define ASSERT(cond, msg) {\
        if(!(cond))\
        {\
            std::stringstream str;\
            str << msg;\
            BOOST_ASSERT_MSG(cond, str.str().c_str());\
        }\
    }
#else
    #define ASSERT(...) 
#endif

usage example, provide custom message like you are outputting to cout:

  ASSERT(execSize == (_oldSize - remaining), "execSize : " << execSize << ", _oldSize : " << _oldSize << ", remaining : " << remaining);

What it does is, if ASSERT_ENABLED is defined,enable the assertion messages. if(!(cond)) part is optimization, which avoids the costly string operations specified by macro parameter msg, if cond is true

Upvotes: 1

Greg
Greg

Reputation: 1660

You could define your own macro

#define ASSERT_WITH_MSG(cond, msg) do \
{ if (!(cond)) { std::ostringstream str; str << msg; std::cerr << str.str(); std::abort(); } \
} while(0)

Upvotes: 7

Puppy
Puppy

Reputation: 146988

It's relatively trivial to achieve this.

BOOST_ASSERT_MSG( length >= 0, (std::stringstream() << "No positive length found! It is " << length).str().c_str() )

Upvotes: 6

Related Questions