My code makes extensive use of compiler asserts like this to flag errors at build time in preference to run time, and to improve performance by not executing asserts at run time.
#define COMPILER_ASSERT(EXPR) switch (0) {case 0: case (EXPR):;}
All good. I'd like to extend this to use compiler asserts for the following case. Say I have a macro which is called from 100 places, 99 of which pass a fixed value, 1 of which passes a variable. How can I code the macro to make this a compiler assert in 99 places and a runtime assert in the last one.
If I could guarantee that MY_FUNCTION() was always called with a fixed value I could code it like this.
void my_function(int x)
{
//do something
}
#define MY_FUNCTION(X) \
COMPILER_ASSERT(X != 0); \
my_function(X)
//These can all take advantage of a compiler assert.
MY_FUNCTION(1);
MY_FUNCTION(SOME_HASH_DEFINE);
MY_FUNCTION(sizeof(SOME_STRUCTURE));
//This can't (this is a contrived example - actual code is complex).
int some_variable = 1;
MY_FUNCTION(some_variable);
So, if I can't guarantee that X is fixed, but want to take advantage for each call to MY_FUNCTION() where it is, how do I code it? Something like:
#define MY_FUNCTION(X) \
if (X is a fixed value) COMPILER_ASSERT(X != 0); \
else assert(X != 0); \
my_function(X)
Recoding the calls to MY_FUNCTION() to only pass fixed values is not an option for me. Yes I could define MY_FUNCTION_FIXED_X and MY_FUNCTION_VARIABLE_X but that exposes all this to the calling code.
Thanks for your help. NickB
If your C compiler supports variable-length arrays, you can write something like:
#define GENERIC_ASSERT(EXPR) \
((EXPR) ? (void) 0 : assert((EXPR)), (void) sizeof(char[(EXPR) ? 1 : -1]))
If EXPR
is a false-valued compile time constant, this reduces to:
(assert((EXPR)), (void) sizeof(char[-1]))
which is a compile error (it involves a negative-length array).
If EXPR
is a true-valued compile time constant, we get:
((void) 0), (void) 1)
Both Clang and gcc are capable of reducing the assert to nothing if invoked with a true-valued compile time constant.
If EXPR
has a runtime value, the sizeof
expression if invoked would result in a runtime error (e.g. an abort), so the assert
is sequenced first through the use of the comma operator.
Unfortunately in the compile-time constant case, the error message output by gcc is not particularly illuminating:
prog.c:5: error: size of array ‘type name’ is negative
In Clang it's a bit better:
error: array size is negative
GENERIC_ASSERT(2 + 2 == 5);
^~~~~~~~~~~~~~~~~~~~~~~~~~
note: expanded from:
((EXPR) ? (void) 0 : assert((EXPR)), (void) sizeof(char[(EXPR) ? 1 : -1]))
^~~~~~~~~~~~~~~
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With