Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define an assert which is effective even if NDEBUG is defined

I would like to define an assert macro which is identical to the standard assert(3) call except that it isn't removed by the pre-processor when NDEBUG is defined.

Such a call, let us call it assert2 here, is useful for example if you want to have some checks happen in release versions of software as well.

How can I do this in a reasonably portable way? I could always just completely re-create the assert mechanism, like1:

#define assert2(cond) cond ? (void)0 : die_now(#cond, __FILE__, __LINE__)

static die_now(char *cond_str, const char *file, int line) {
  // print a message to stderr here
  ...
  abort();  // or maybe abort ?
}

... but I'd much rather just use the same mechanism as the existing assert call. In particular, the built-in assert call does nice stuff like handling all the various weirdnesses of your compiler and platform, annotating the assert condition with special magic that lets the compiler assume the condition holds after the call, pretty printing function names, and so on.

I could get the builtin assert by just #undef NDEBUG before including assert.h - but I can't see how to rename it to assert2. I guess I could resort to copy & pasting the definition of assert in the system header files into the #define assert2 line, but this is (a) a possible violation of some license or copyright and (b) would need to be repeated for each platform.

Please don't start a debate whether such a function is useful or not, or whether it's a reasonable way to accomplish a higher-level aim. I am asking specifically whether I can re-use the existing assert call, under another name, without the dependence on NDEBUG.


1 Of course, declaring die_now as a static function isn't ideal since it will duplicate the die_now function in every compilation unit that uses as assert (or worse, perhaps even all those that just include the header), so it should really be defined out-of-line in its own complication unit, but that's another complication to the use of this function.

like image 326
BeeOnRope Avatar asked Sep 14 '25 13:09

BeeOnRope


1 Answers

I'd do this the other way around: never define NDEBUG, so assert is always enabled, and define an assert2 which is either a no-op or an alias of assert. Then you can turn assert2 on and off at your pleasure:

file assert2.h

// Note: no include guard
//
// Copy NDEBUG to NDEBUG2
#undef NDEBUG2
#ifdef NDEBUG
#define NDEBUG2 1
#endif
#undef NDEBUG
/* include <assert.h>, so that assert and friends are defined
 * assert.h also lacks an include guard, but multiple inclusions
 * are required to be OK (section 7.1.2 paragraph 4, if you care.)
 */
#include <assert.h>
/* Now define an assert which respects the original NDEBUG */
#undef assert2
#ifdef NDEBUG2
#define assert2(x)
#else
#define assert2 assert
#endif

Now you can flip back and forth by reincluding "assert2.h" after defining or undefining NDEBUG. The above file always undefines NDEBUG as a side-effect, but you could copy it back from NDEBUG2 at the end.

like image 162
rici Avatar answered Sep 16 '25 02:09

rici