0

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?

Spidey
  • 2,508
  • 2
  • 28
  • 38
  • 2
    I don't know if this would help the warning, but I would use the `?:` operator rather than an `if` statement so you don't have to worry about the expansion of the macro being a compound statement. It can simply be an expression. You might could use `&&` or `||` in place of `?:` too, and these might silence the warning. – R.. GitHub STOP HELPING ICE Jul 04 '13 at 21:55
  • I like that. Something like `if (expr && handle_failed_assert())` might work. I'll try and report that. – Spidey Jul 04 '13 at 22:32

1 Answers1

2

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));
torek
  • 448,244
  • 59
  • 642
  • 775
  • So, is it actually necessary to make `__truefunc()` an extern function? I was about to suggest something similar using a volatile integer. – This isn't my real name Jul 04 '13 at 22:10
  • @Spidey Also, I must strongly suggest that you name your macro something other than `assert()`, if for no other reason than to avoid severely confusing anyone reading your code. The macro named `assert()` is, if you `#include `, defined by the C standard to call `abort()` if the condition is false. If you're writing an assert-type macro (or function) that actually handles the error, `assert()` is a bad choice of name. – This isn't my real name Jul 04 '13 at 22:13
  • It's actually `common_assert`, don't worry about that. – Spidey Jul 04 '13 at 22:33
  • @ElchononEdelson: not necessarily; an extern volatile is likely to work too. – torek Jul 04 '13 at 22:42