Is using assert() for production not favored over if..else.. blocks?

Having read this article I will share my beliefs about assert:

  1. Yes it's fine to use assert when something absolutely should meet the condition you are asserting.

  2. Many languages allow you to raise custom errors when asserting, C not having "Exceptions" may produce errors that are a little harder to diagnose without directly looking at the source in question.


If it's a programming error (possibly by the caller), use an assert.

If it's not a programming error, then use if/else and handle the situation appropriately.


Asserts are good. Compile-time asserts are even better. Note:

  • BOOST has one, BOOST_STATIC_ASSERT().
  • C++0x has static_assert().
  • And GCC since 4.3 has had a built-in static_assert(): Does GCC have a built-in compile time assert?
  • C11 has static_assert() (<assert.h>).

If your environment doesn't already have a static assert, here is a suggestion.

The ASSERT() macro given below can be placed anywhere in the code, except:

  • In a twice-included header file, without a #ifndef...#endif wrapper.
  • In the middle of a structure definition (or enum definition).
  • In strict C89 or C90, after a statement. (But you can wrap it in braces!)

If you want stick something in the middle of a structure definition you'll need to use the lengthy, ugly, three-line construct #if...#error...#endif. And if you do do this, the pre-processor has a much more limited idea of what a "constant expression" is.

This is a refinement of ideas from the web, primarily from http://www.pixelbeat.org/programming/gcc/static_assert.html. This definition is shorter than BOOST_STATIC_ASSERT(). And, I believe, is better than Linus's suggestion for an improved BUILD_BUG_ON(). And the do{...}while(0) wrapper you commonly see is totally inapplicable here, as it limits permissible locations.

This is also simpler than Google's COMPILE_ASSERT/CompileAssert. The 'sizeof bitfield' trick also seems good, from Linux's BUILD_BUG_ON_ZERO(), but not its useless sibling BUILD_BUG_ON().

There are many suggestions for using arrays with a negative index. But with GCC, most of these do not detect a non-constant arg (which is easy enough to do in error), except for the 'extern int foo[expression]', which also gives an 'unused variable' warning. But typedef int array[expression] seems also to be good: see below.

The definition:

#define CONCAT_TOKENS(a, b)     a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum{EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}

Equally good, I believe, is the following variant, but it is longer by five characters:

#define ASSERT(e) typedef int EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__)[1-2*!(e)]

There is also the do{switch(0){case 0:case(e):;}}while(0) construct, which I haven't investigated.

Sometimes one needs a variant to handle the case where two different header files happen by chance to have two ASSERT()'s on the same line, or likewise for a source file and a header file. You could handle this via __COUNTER__, but this isn't supported by some compilers (and is uglier). And we can't use __FILE__, because it doesn't usually expand to a valid C token (e.g. it has a dot c or dot h). The Mozilla version http://mxr.mozilla.org/mozilla-central/source/mfbt/Assertions.h states that such conflicts "should be rare", but they'll greatly annoy your teammates when it happens. This can also be used to handle several ASSERTS in a multi-line macro, where __LINE__ doesn't change.

#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}

The next variant, ASSERT_zero(), is similar to BUILD_BUG_ON_ZERO(), using the 'sizeof bitfield' trick. This yields either:

  • a compile error, when e is false, or
  • the value zero.

So it can be used in places where a statement cannot, such as in the middle of an expression.

#ifndef __cplusplus
#define ASSERT_zero(e) (!sizeof(struct{int:!!(e);})) // cf. BUILD_BUG_ON_ZERO(), !C++
#else
#define ASSERT_zero(e) (!sizeof(char[(e) ? 1 : -1])) // careful: g++ has VLAs
#endif