4

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.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • 1) C does not support _methods_. 2) `assert` is a macro. 3) Not clear what your problem is with a self-defined macro which does not depend on `NDEBUG`. – too honest for this site Feb 07 '17 at 15:34
  • This probably depends on how assert is implemented on your platform. I'd roll out my own assert, this is rather straightforward. – Jabberwocky Feb 07 '17 at 15:35
  • @MichaelWalz and @Olaf - it's easy, but it loses a lot of the magic the existing assert has and may have subtle differences in behavior. Step through a typical platform `assert` implementation: it is doing a bunch of stuff. – BeeOnRope Feb 07 '17 at 15:36
  • The point of NDEBUG is to remove asserts. So if you *don't* want to remove them then by far the simplest way is to not #define the macro. Low odds that you need to redefine how assert() works, but you'll have to try as long as you don't document the CRT you use. – Hans Passant Feb 07 '17 at 15:38
  • Seems like a big hack to me, but maybe you could #undef NDEBUG, do your own Macro using "assert", and #define NDEBUG again, if it has been defined before... – jwsc Feb 07 '17 at 15:39
  • @HansPassant, I don't want to affect the existing `assert` call at all. That will on/off as specified by NDEBUG. I want, in addition to that, an additional "assert-like" call which is always enabled. Again, I'm not really interested in whether it is a good idea. If it makes you happy I'll just say it's a terrible idea and no one should do it, but I'm still interested in an answer to the _purely technical_ aspect of the question, purely for academic curiosity. Consider it a query about what's possible with the C-preprocessor. – BeeOnRope Feb 07 '17 at 15:40
  • @BeeOnRope aah, forget what I said. while writing the answer I discovered that I am wrong. – jwsc Feb 07 '17 at 15:52
  • @jwsc you can delete your comments – Jabberwocky Feb 07 '17 at 15:57

1 Answers1

2

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.

Community
  • 1
  • 1
rici
  • 234,347
  • 28
  • 237
  • 341
  • Yes! I was coming around to something like this... it makes sense. I did make me think about exactly what `NDEBUG` is used for, since leaving it on may have _other_ affects other than changing the behavior of `assert`. While looking it, I stumbled on [this answer](http://stackoverflow.com/a/5474282/149138) which seems to indicate that `assert.h` is the one header you are allowed to include multiple times, specifically in order to mess with `assert`, so my other "bake in" question is actually more relevant than I thought. – BeeOnRope Feb 07 '17 at 23:22
  • 1
    One problem I thought of with this approach is that while I am able to evaluate in _my code_ what should be an `assert` (always on) or `assert2` (only on in debug build builds) - I have reversed this assumption for all the headers in libraries I include, so maybe they have some slow asserts in there. Having in the headers is a bit unlikely though... – BeeOnRope Feb 07 '17 at 23:24
  • 1
    Yes, that answer is correct; I just checked the standard. Good to know. I'lm have to fix my answer here or at least the comments. But I think the principle still works. – rici Feb 07 '17 at 23:52