Question

I have an assert macro that resolves to an if, something like this:

#define assert(expr) \
if (!(expr)) \
{ \
    handle_failed_assert(); \
}

Ignore how handle_failed_assert() works, and you don't need to cite the do { ... } while(0) trick. Please, focus on the functionality behind this.

Now, the real question comes. Sometimes I want to force and assert, and make it meaningful. So we use this:

assert(!"Assert cause carefully described.");

The problem is that we have this compiler, vrxcc, based on RVCT 2.2, that throws this warning when compiling that:

#236-D: controlling expression is constant

Of course, that resolves to a compile constant if.

How could I trick the compiler into accepting that?

Was it helpful?

Solution

Your problem ultimately boils down to "my compiler is too smart, how do I make it stop complaining about something that, yes, is true and is often a programmer mistake, but in this case is not a programmer mistake". There are only two ways to do that:

  • Outwit the compiler. This is compiler-dependent.
  • Tell the compiler "don't complain, this is not a mistake." This is compiler-dependent.

I know nothing about vrxcc. R's comment goes towards doing the first. This sort of thing is almost guaranteed to work:

extern int __truefunc(void);
#define assert(expr) ((__truefunc() && (expr)) || __assert_fail(#expr))

where truefunc is a function that always returns 1, and that you can compile separately to outwit the compiler. The cost, of course, is that darned useless run-time call.

The "tell the compiler" method is nicer, but requires some sort of compiler documentation assist.


Addendum: it occurred to me in the shower that in your particular case, you've already decided to panic, so you could just have a panic function, and call that here. The disadvantage is that you have to change all your existing common_assert(!"some string") calls, but at least you can do that mechanically.

It might be nice if the language had a two-argument assert built in or as a standard thing. The FreeBSD kernel uses KASSERT for this these days, more or less as:

#define KASSERT(expr, panic_args) \
    do { if (!(expr)) panic panic_args; } while (0)

which is a bit klunky syntactically, but is nicely flexible:

KASSERT(foo.field == FOO_MAGIC,
    ("memory overwrite of foo data structure: %d != %d",
        foo.field, FOO_MAGIC));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top