C99 doesn't require support for "sizeof" in preprocessor conditionals and gcc for example doesn't support it. So we can't do something like #if (sizeof(my_struct)!=512) for example. This prompted me into seeking a more general solution of getting the compiler to check constant expressions, and I came up with this macro to support compile time assertions.
/* Note we need the 2 concats below because arguments to ## * are not expanded, so we need to expand __LINE__ with one indirection * before doing the actual concatenation. */ #define ASSERT_CONCAT_(a, b) a##b #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) #define ct_assert(e) enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }This macro has no runtime side affects as it just defines an enum whose name depends on the current line, and whose value will give a divide by zero error at compile time if the assertion is false. If you try to ct_assert() a non constant expression by mistake, this will also give an error and you can change to using a normal assert(). Using this macro you can create a compile time check at any scope as in the following examples:
ct_assert(sizeof(my_struct)==512); ct_assert(sizeof(int)==4); ct_assert(M_PI/2);Note compile time assertions have advantages over runtime ones.
- They don't need to be called in a function and so can be defined at a structure declaration site for example.
- You catch any errors earlier and there is no chance of missing the assertion in testing.
- You obviate the need for exercising the code path at runtime which may be awkward.
- It's handy to check/ensure a compiler is in fact computing an expression at compile time.
UPDATE 1: After writing this I noticed that this functionality is more commonly known as static assertion for some reason, and is a new proposal in the C++ 0X draft standard, which takes the form:
static_assert(constant-expression, "error message");This is also being considered for the C1X standard. The macro above will be useful for a long time to come though.
UPDATE 2: Miguel Sofer added this to TCL and noticed that the above macro doesn't work in all places for traditional C, as enums must be declared at the start of scope. So he fixed that and simplified the macro as well to give:
#define ct_assert(e) {enum { ct_assert_value = 1/(!!(e)) };}
Also it was pointed out on reddit that the linux kernel uses something similar in it's BUILD_BUG_ON macro:
#define ct_assert(e) ((void)sizeof(char[1 - 2*!(e)]))
UPDATE 3: I just realised myself, and Keith Thompson and Jim Meyering also kindly pointed out, that the above kernel and TCL variants will not work at global scope as they're statements. Therefore for traditional C you need to have separate macros to support both cases, which one can see for example in the gnulib verify macros. For C++, C99 or GCC though, my original version above works for all situations.
UPDATE 4: It was pointed out by Bill Davy that ct_assert() would fail to compile if used twice on the same line, or is used on the same line in 2 files included into the same compilation unit (which could be the same file included twice if it's not protected by #ifdef clauses). I did try to use __FILE__ to distinguish the identifier, but this is not a valid token, and wouldn't work anyway in the case where a file is included twice. So therefore I got rid of the unique identifier altogether to get the following which works for C99 and C++ for gcc at least.
#define ct_assert(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])]
UPDATE 5: Bill Davy again points out that update 4 is very noisy under microsoft compilers. So how about:
#ifdef __COUNTER__ /* microsoft */ #define ASSERT_CONCAT_(a, b) a##b #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) #define ct_assert(e) enum { ASSERT_CONCAT(assert_line_, __COUNTER__) = 1/(!!(e)) } #else /* gnu */ #define ct_assert(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])] #endif
UPDATE 6: Márcio Faustino mentioned the Microsoft C_ASSERT equivalent.
However it does not detect non constant expressions, but then I realised that my GNU
version in update 5 doesn't either!
He also mentioned that we should probably add an unused message
parameter so as to ease the transition
to the official static_assert
when it becomes available. So adding those mods to my original macro
and noting the caveats in update 2, we get:
#define ASSERT_CONCAT_(a, b) a##b #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) /* These can't be used after statements in c89. */ #ifdef __COUNTER__ /* microsoft */ #define STATIC_ASSERT(e,m) \ enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) } #else /* This can't be used twice on the same line so ensure if using in headers * that the headers are not included twice (by wrapping in #ifndef...#endif) * Note it doesn't cause an issue when used on same line of separate modules * compiled with gcc -combine -fwhole-program. */ #define STATIC_ASSERT(e,m) \ enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) } #endif
UPDATE 7: Microsoft Visual studio 2010 released 2010-04-12,
supports static_assert.
g++ -std=c++0x supports the static_assert keyword since version 4.3 released 2008-03-05.
UPDATE 8: gcc supports the _Static_assert keyword since version 4.6, released 2011-03-05, and doesn't need the -std=c1x option to do so. Note the C1X standard, defines static_assert as an alias for the _Static_assert keyword in assert.h, but that is less portable to use currently.
UPDATE 9 Nov 2012: Stephen Pitts mentioned that extra braces are required to support putting the STATIC_ASSERT() right after a case statement. When testing this I also noticed that gcc since version 4.3.0 supports __COUNTER__ too. So adjusting accordingly we get:
#define ASSERT_CONCAT_(a, b) a##b #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) /* These can't be used after statements in c89. */ #ifdef __COUNTER__ #define STATIC_ASSERT(e,m) \ { enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }; } #else /* This can't be used twice on the same line so ensure if using in headers * that the headers are not included twice (by wrapping in #ifndef...#endif) * Note it doesn't cause an issue when used on same line of separate modules * compiled with gcc -combine -fwhole-program. */ #define STATIC_ASSERT(e,m) \ { enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }; } #endif
UPDATE 10 Feb 2015: The extra braces added in update 9 don't work. What's needed is an implicit statement to cater for an assert directly after a label or a case statement, as labels can't be associated with a declaration. That can be done with a leading ';'
UPDATE 11 May 2016: Visual Studio 12.0.21005.1 gives warning C4804: 'operation' : unsafe use of type 'bool' in operation. That can be suppressed with an (int) cast, giving:
#define ASSERT_CONCAT_(a, b) a##b #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) /* These can't be used after statements in c89. */ #ifdef __COUNTER__ #define STATIC_ASSERT(e,m) \ ;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) } #else /* This can't be used twice on the same line so ensure if using in headers * that the headers are not included twice (by wrapping in #ifndef...#endif) * Note it doesn't cause an issue when used on same line of separate modules * compiled with gcc -combine -fwhole-program. */ #define STATIC_ASSERT(e,m) \ ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!!(e)) } #endifCode on the page is licensed under the GNU All-Permissive License.