165

Is there a way to add or edit the message thrown by assert? I'd like to use something like

assert(a == b, "A must be equal to B");

Then, the compiler adds line, time and so on...

Is it possible?

vines
  • 5,160
  • 1
  • 27
  • 49
Killrazor
  • 6,856
  • 15
  • 53
  • 69
  • You can define a macro, like [this](http://stackoverflow.com/a/3767883/1070480). – HelloGoodbye Dec 18 '15 at 12:02
  • @JoeHuang Well, I haven't programmed in C#, but C++ offers you a greater level of control, hence you need to write more code. But of course C++ isn't a perfect language, and there are ongoing attempts to improve it (e.g. Herb Sutter's [syntax 2.0 or Cpp2](https://herbsutter.com/tag/cpp2/)) or create programming languages to replace it (Rust, D, Carbon, Val, etc.). – HelloGoodbye Nov 14 '22 at 15:44

11 Answers11

299

A hack I've seen around is to use the && operator. Since a pointer "is true" if it's non-null, you can do the following without altering the condition:

assert(a == b && "A is not equal to B");

Since assert shows the condition that failed, it will display your message too. If it's not enough, you can write your own myAssert function or macro that will display whatever you want.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • 34
    Another option is to reverse the operands and use the comma operator. You need extra parentheses so the comma isn't treated as a delimiter between the arguments: `assert(("A must be equal to B", a == b));` – Keith Thompson Jan 08 '12 at 06:16
  • 5
    It would be nice, though, to be able to print the values of variables, as in: `assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");` – Frank Jul 03 '13 at 18:43
  • 8
    @Frank, `printf` returns a non-zero value if it printed anything, so you could do something like `assert(a == b && printf("a (%i) is not equal to b (%i)", a, b))`, though at that point you should probably write your own assert wrapper. – zneak Jul 04 '13 at 04:32
  • 2
    Bad code! I don't understand this! If a==b is false, the and-expression should also be false, and, therefore, the string should not be evaluated. – ragnarius Nov 07 '14 at 17:31
  • @ragnarius, de Morgan's law applies here, and since the string always "evaluates" to true, it cannot change the result of the condition. It's in the source code only as an annotation, since the `assert` macro prints the whole `assert` line when it fails. – zneak Nov 07 '14 at 18:06
  • @zneak `assert` does not works for me, I'm using ` Assert::AreEqual(2, HelloWorld::getTwo());` can similar principle be applied also to this syntax? – Wakan Tanka Oct 26 '16 at 12:38
  • @WakanTanka, no, you need control over the condition. – zneak Oct 26 '16 at 14:35
  • @zneak sorry but I do not follow, I'm following this tutorial http://www.codeproject.com/Tips/1085171/How-To-Do-Unit-Testing-with-Cplusplus-in-Visual-St can you please post link that will clarify your recommendation. Thanks – Wakan Tanka Oct 26 '16 at 20:21
  • @WakanTanka, I'm not recommending anything; you can't use this workaround for a message. It works because `a == b && "foo"` is the same as `a == b`. If your code does not do that comparison itself, you can't do it. – zneak Oct 26 '16 at 21:09
  • Wouldn't something like `assert(a==b ||(0&&"A is not equal to B"));` be better since it allows the program not to evaluate the string any time it pass through the assert? – TUI lover Aug 09 '19 at 08:38
  • 4
    @TUIlover, that’s not how C string literals work; they’re compile-time constants and their use in this context is trivially optimized away. There is no runtime cost. – zneak Aug 09 '19 at 08:46
72

Another option is to reverse the operands and use the comma operator. You need extra parentheses so the comma isn't treated as a delimiter between the arguments:

assert(("A must be equal to B", a == b));

(this was copied from above comments, for better visibility)

ChrisF
  • 134,786
  • 31
  • 255
  • 325
Andrei Bozantan
  • 3,781
  • 2
  • 30
  • 40
  • 4
    This is a great approach, with one tiny issue, it will display "warning: left operand of comma operator has no effect" when compiled in g++ with `-Wunused-value – v010dya Jan 30 '16 at 11:02
  • 1
    or with a macro: #ifndef m_assert #define m_assert(expr, msg) assert((msg, expr)) #endif – Szymon Marczak Aug 03 '17 at 10:50
  • 1
    Using a macro wrapper lets you avoid the gcc warning: `#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))` – Jander Sep 24 '18 at 13:34
38

Here's my version of assert macro, which accepts the message and prints everything out in a clear way:

#include <iostream>

#ifndef NDEBUG
#   define M_Assert(Expr, Msg) \
    __M_Assert(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
#   define M_Assert(Expr, Msg) ;
#endif

void __M_Assert(const char* expr_str, bool expr, const char* file, int line, const char* msg)
{
    if (!expr)
    {
        std::cerr << "Assert failed:\t" << msg << "\n"
            << "Expected:\t" << expr_str << "\n"
            << "Source:\t\t" << file << ", line " << line << "\n";
        abort();
    }
}

Now, you can use this

M_Assert(ptr != nullptr, "MyFunction: requires non-null argument");

And in case of failure you will get a message like this:

Assert failed:  MyFunction: requires non-null argument

Expected: ptr != nullptr

Source: C:\MyProject\src.cpp, line 22

Nice and clean, feel free to use it in your code =)

Community
  • 1
  • 1
  • I'm a little confused. Is #Expr treated as a string for direct substitution? What is the difference between #Expr and Expr? – Minh Tran Sep 22 '16 at 17:25
  • @MinhTran Let's assume that your assert condition is `x == y`. Then Expr will expand into `if( !(x == y))` and this is where condition is checked, and #Expr will expand into string literal `"x == y"`, which we then put into error message. – Eugene Magdalits Oct 14 '16 at 07:27
  • 1
    Unfortunately, this solution causes undefined behaviour due to using reserved identifiers. – Remember Monica Mar 12 '20 at 10:44
  • Nice answer. However, names shouldn't start with double underscores `__` because they are reserved. See compiler intrinsics, like `__declspec`. – Agent49 Aug 11 '23 at 15:21
22
BOOST_ASSERT_MSG(expre, msg)

http://www.boost.org/doc/libs/1_51_0/libs/utility/assert.html

You could either use that directly or copy Boost's code. Also note Boost assert is header only, so you could just grab that single file if you didn't want to install all of Boost.

Zero
  • 11,593
  • 9
  • 52
  • 70
11

As zneak's answer convolutes the code somewhat, a better approach is to merely comment the string text you're talking about. ie.:

assert(a == b); // A must be equal to B

Since the reader of the assert error will look up the file and line anyway from the error message, they will see the full explanation here.

Because, at the end of the day, this:

assert(number_of_frames != 0); // Has frames to update

reads better than this:

assert(number_of_frames != 0 && "Has frames to update");

in terms of human parsing of code ie. readability. Also not a language hack.

metamorphosis
  • 1,972
  • 16
  • 25
2

assert is a macro/function combination. you can define your own macro/function, using __FILE__, __BASE_FILE__, __LINE__ etc, with your own function that takes a custom message

Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
1

If the assert is done within a class, an alternative approach is to call a static predicate function with a self-describing name. If the assertion fails, the message will already contain the predicate's pretty and self-describing name.

E.g.:

static bool arguments_must_be_ordered(int a, int b) {return a <= b;}

void foo(int a, int b)
{
    assert(arguments_must_be_ordered(a, b));
    // ...
}

You may even want to make that predicate function public so that the class' user can verify the precondition themselves.

Even if assert is not disabled for release builds, the compiler will likely inline the predicate if it's fairly trivial.

The same approach can be used for complex if conditions needing a comment. Instead of a comment, just call a self-describing predicate function.

Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
0

You could also just write your own custom assert function. A very simple example:

bool print_if_false(const bool assertion, const char* msg) {
    if(!assertion) {
        // endl to flush
        std::cout << msg << std::endl;
    }
    return assertion;
}

int main()
{
    int i = 0;
    int j = 1;
    assert(print_if_false(i == j, "i and j should be equal"));
    return 0;
}

play with the code.

The assertion reads Assertion print_if_false(i == j, "i and j should be equal").

User12547645
  • 6,955
  • 3
  • 38
  • 69
0
int x=10, y=25;
assert(x > y);   // Add message along with this assert

Option 1) Since fprintf returns number of characters printed so we can or assert expression with !fprintf. Using stderr here since this is an error message

assert((x > y) || !fprintf(stderr, "Error: x %d is expected to be greater than y %d \n", x, y));

We can wrap this inside a macro for convinient use.

// Define macro over assert
#define assert_msg(cond, fmt, ...) assert( cond || !fprintf(stderr, fmt, ##__VA_ARGS__))
// Use above macro
assert_msg(x > y, "Error: x %d is expected to be greater than y %d \n", x, y);

Option 2) Define error message wrapped inside lambda.

auto err =  [x, y] { fprintf(stderr, "Error: x %d should be greater than y %d \n", x, y); return false; };
assert((x > y) || err());   // Assert calls lambda func only when condition fails

Here is the dumped message.

Error: x 10 should be greater than y 25 
File.cpp:10: int main(): Assertion `(x > y) || err()' failed.

Option 3) Or we can refine above solution to do it in one line with help of immediately invoked lambda

assert((x > y) || ([x, y] { fprintf(stderr, "Error: x %d is expected to be greater than y %d \n", x, y); return false; }()));
Shakti Malik
  • 2,335
  • 25
  • 32
0

@Shakti Malik, thank you very much for your solution!

I have reduced it to:

#define ASSERT(cond, msg, args...) assert((cond) || !fprintf(stderr, (msg "\n"), args))

Excellent works)

JML
  • 1
  • 2
-7

For vc, add following code in assert.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
Jichao
  • 40,341
  • 47
  • 125
  • 198