Approves well. Compile-time statements are even better. Remarks:
If your environment does not yet have a static statement, here is a suggestion.
The ASSERT()
macro below can be placed anywhere in the code except:
- In a double-included header file without the
#ifndef...#endif
shell. - In the middle of a structure definition (or enum definition).
- In strict C89 or C90, after declaration. (But you can wrap it in braces!)
If you want to insert something in the middle of the structure definition, you need to use the long, ugly #if...#error...#endif
construct #if...#error...#endif
. And if you do this, the preprocessor will have a much more limited idea of โโwhat a "constant expression" is.
This is a refinement of ideas from the Internet, mainly from http://www.pixelbeat.org/programming/gcc/static_assert.html . This definition is shorter than BOOST_STATIC_ASSERT()
. And, I suppose, better than Linus's suggestion for improved BUILD_BUG_ON()
. And the do{...}while(0)
shell you usually see is completely inapplicable here, since it limits the permissible locations.
It is also simpler than Google COMPILE_ASSERT / CompileAssert. The "sizeof bitfield" trick also seems good, from Linux BUILD_BUG_ON_ZERO()
, but not from its useless brother BUILD_BUG_ON()
.
There are many suggestions for using negative index arrays. But with GCC, most of them do not detect an inconsistent arg argument (which is quite easy to do by mistake), with the exception of extern int foo [expression], which also issues a warning "unused variable". But typedef int array[expression]
also looks good: see below.
Definition:
Equally good, I suppose the following option, but it is five characters longer:
#define ASSERT(e) typedef int EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__)[1-2*!(e)]
There is also a do{switch(0){case 0:case(e):;}}while(0)
construct that I have not explored.
Sometimes you need an option to handle the case when two different header files randomly have two ASSERT () on the same line or the same for the source file and header file. You could handle this via __COUNTER__
, but this is not supported by some compilers (and this is uglier). And we cannot use __FILE__
because it usually does not expand to a valid C token (for example, it has point c or point h). Mozilla's version of http://mxr.mozilla.org/mozilla-central/source/mfbt/Assertions.h states that such conflicts "should be rare," but they will greatly annoy your teammates when this happens. It can also be used to handle multiple ASSERTS
in a multi-line macro, where __LINE__
does not change.
#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}
The next option, ASSERT_zero(),
is similar to BUILD_BUG_ON_ZERO(),
using the "sizeof bitfield" trick. This gives either:
- compilation error when
e
false, or - the value is zero.
Therefore, it can be used in places where the operator cannot, for example, 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