CinchBlue
CinchBlue

Reputation: 6190

Is it possible to have a zero-cost assert() such that code should not have to be modified between debug and release builds?

I've noticed that some code often looks like this:

#ifdef DEBUG
assert(i == 1);
#endif //DEBUG

and that you may have several blocks of these sitting around in your raw code. Having to write out each block is tedious and messy.

Would it be plausible to have a function like this:

auto debug_assert = [](auto expr) { 
 #ifdef DEBUG
 assert(expr);
 #endif //DEBUG
};

or something like this:

#ifdef DEBUG
auto debug_assert = [](bool expr) {
  assert(expr);     
};
#else //DEBUG
void debug_assert(bool expr) {}
#endif //DEBUG

to get a zero-cost assert when the DEBUG flag is not specified? (i.e. it should have the same effect as if it was not put into the code without the lambda running, etc. and be optimized out by the g++/clang compilers).

Upvotes: 0

Views: 278

Answers (1)

milleniumbug
milleniumbug

Reputation: 15814

As mentioned by @KerrekSB, you can already disable asserts by defining NDEBUG before including <cassert>. The best way to ensure that it's defined before including the header file is to list it in as the argument to the compiler (with gcc it's -DNDEBUG)

Note: the assert is removed by replacing it with a no-op expression, and there, the argument isn't evaluated at all (which is different from your suggested solution)! This is why it's of utmost importance to not call any functions that have side effects in assert.

For completeness: here is how assert can be implemented:

#include <cstdio>
#include <cstdlib>

#ifndef NDEBUG
#define assert(EXPRESSION) ((EXPRESSION) ? (void)0 : (printf("assertion failed at line %d, file %s: %s\n", __LINE__, __FILE__, #EXPRESSION), exit(-1)))
#else
#define assert(EXPRESSION) (void)0
#endif

Introducing your own assert-style macro is very commonly done. There are quite a lot of reasons you may want to do this:

  • you want to include more information about the evaluated expression (see Catch's REQUIRE and how they use expression templates to decompose the expression into individual elements and stringify them)
  • you want to do action other than exit()ing the program, like throwing an exception, mailing the developer, logging to a file, breaking into the debugger
  • you want to evaluate the expression even on release builds which is less error prone than not evaluating it at all (after all, if it doesn't have side effects, it can be eliminated by a compiler optimizations, and if it does, you just avoided a heisenbug)
  • and so on, and so on (if you have an idea, you can post a comment, I'll add it to the answer)

Upvotes: 6

Related Questions