Related
I'd like to implement an "assert" that prevents compilation, rather than failing at runtime, in the error case.
I currently have one defined like this, which works great, but which increases the size of the binaries.
#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}
Sample code (which fails to compile).
#define DEFINE_A 1
#define DEFINE_B 1
MY_COMPILER_ASSERT(DEFINE_A == DEFINE_B);
How can I implement this so that it does not generate any code (in order to minimize the size of the binaries generated)?
A compile-time assert in pure standard C is possible, and a little bit of preprocessor trickery makes its usage look just as clean as the runtime usage of assert().
The key trick is to find a construct that can be evaluated at compile time and can cause an error for some values. One answer is the declaration of an array cannot have a negative size. Using a typedef prevents the allocation of space on success, and preserves the error on failure.
The error message itself will cryptically refer to declaration of a negative size (GCC says "size of array foo is negative"), so you should pick a name for the array type that hints that this error really is an assertion check.
A further issue to handle is that it is only possible to typedef a particular type name once in any compilation unit. So, the macro has to arrange for each usage to get a unique type name to declare.
My usual solution has been to require that the macro have two parameters. The first is the condition to assert is true, and the second is part of the type name declared behind the scenes. The answer by plinth hints at using token pasting and the __LINE__ predefined macro to form a unique name possibly without needing an extra argument.
Unfortunately, if the assertion check is in an included file, it can still collide with a check at the same line number in a second included file, or at that line number in the main source file. We could paper over that by using the macro __FILE__, but it is defined to be a string constant and there is no preprocessor trick that can turn a string constant back into part of an identifier name; not to mention that legal file names can contain characters that are not legal parts of an identifier.
So, I would propose the following code fragment:
/** A compile time assertion check.
*
* Validate at compile time that the predicate is true without
* generating code. This can be used at any point in a source file
* where typedef is legal.
*
* On success, compilation proceeds normally.
*
* On failure, attempts to typedef an array type of negative size. The
* offending line will look like
* typedef assertion_failed_file_h_42[-1]
* where file is the content of the second parameter which should
* typically be related in some obvious way to the containing file
* name, 42 is the line number in the file on which the assertion
* appears, and -1 is the result of a calculation based on the
* predicate failing.
*
* \param predicate The predicate to test. It must evaluate to
* something that can be coerced to a normal C boolean.
*
* \param file A sequence of legal identifier characters that should
* uniquely identify the source file in which this condition appears.
*/
#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)
#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
A typical usage might be something like:
#include "CAssert.h"
...
struct foo {
... /* 76 bytes of members */
};
CASSERT(sizeof(struct foo) == 76, demo_c);
In GCC, an assertion failure would look like:
$ gcc -c demo.c
demo.c:32: error: size of array `assertion_failed_demo_c_32' is negative
$
The following COMPILER_VERIFY(exp) macro works fairly well.
// combine arguments (after expanding arguments)
#define GLUE(a,b) __GLUE(a,b)
#define __GLUE(a,b) a ## b
#define CVERIFY(expr, msg) typedef char GLUE (compiler_verify_, msg) [(expr) ? (+1) : (-1)]
#define COMPILER_VERIFY(exp) CVERIFY (exp, __LINE__)
It works for both C and C++ and can be used anywhere a typedef would be allowed. If the expression is true, it generates a typedef for an array of 1 char (which is harmless). If the expression is false, it generates a typedef for an array of -1 chars, which will generally result in an error message. The expression given as an arugment can be anything that evaluates to a compile-time constant (so expressions involving sizeof() work fine). This makes it much more flexible than
#if (expr)
#error
#endif
where you are restricted to expressions that can be evaluated by the preprocessor.
As Leander said, static assertions are being added to C++11, and now they have.
static_assert(exp, message)
For example
#include "myfile.hpp"
static_assert(sizeof(MyClass) == 16, "MyClass is not 16 bytes!")
void doStuff(MyClass object) { }
See the cppreference page on it.
The best writeup that I could find on static assertions in C is at pixelbeat. Note that static assertions are being added to C++ 0X, and may make it in to C1X, but that's not going to be for a while. I do not know if the macros in the link I gave will increase the size of your binaries. I would suspect they would not, at least if you compile at a reasonable level of optimisation, but your mileage may vary.
If your compiler sets a preprocessor macro like DEBUG or NDEBUG you can make something like this (otherwise you could set this up in a Makefile):
#ifdef DEBUG
#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}
#else
#define MY_COMPILER_ASSERT(EXPRESSION)
#endif
Then, your compiler asserts only for debug builds.
Using '#error' is a valid preprocessor definition that causes compilation to stop on most compilers. You can just do it like this, for example, to prevent compilation in debug:
#ifdef DEBUG
#error Please don't compile now
#endif
I know you're interested in C, but take a look at boost's C++ static_assert. (Incidentally, this is likely becoming available in C++1x.)
We've done something similar, again for C++:
#define COMPILER_ASSERT(expr) enum { ARG_JOIN(CompilerAssertAtLine, __LINE__) = sizeof( char[(expr) ? +1 : -1] ) }
This works only in C++, apparently. This article discusses a way to modify it for use in C.
When you compile your final binaries, define MY_COMPILER_ASSERT to be blank, so that its output isn't included in the result. Only define it the way you have it for debugging.
But really, you aren't going to be able to catch every assertion this way. Some just don't make sense at compile time (like the assertion that a value is not null). All you can do is verify the values of other #defines. I'm not really sure why you'd want to do that.
I found this to give the least confusing error message for GCC. Everything else had some suffix about a negative size or some other confusing thing:
#define STATIC_ASSERT(expr, msg) \
typedef char ______Assertion_Failed_____##msg[1]; __unused \
typedef char ______Assertion_Failed_____##msg[(expr)?1:2] __unused
example usage:
unsigned char testvar;
STATIC_ASSERT(sizeof(testvar) >= 8, testvar_is_too_small);
And the error message in gcc (ARM/GNU C Compiler : 6.3.1):
conflicting types for '______Assertion_Failed_____testvar_is_too_small'
Well, you could use the static asserts in the boost library.
What I believe they do there, is to define an array.
#define MY_COMPILER_ASSERT(EXPRESSION) char x[(EXPRESSION)];
If EXPRESSION is true, it defines char x[1];, which is OK. If false, it defines char x[0]; which is illegal.
I have a function which returns an integer value. Now I want to write a macro which call this function, gets the return value and prepends a string to it and return the resultant string.
I have tried this:
#define TEST(x) is_enabled(x)
I call this macro in the main function as:
int ret = 0;
ret = TEST(2);
printf("PORT-%d\n", ret);
This works perfectly. However I want the macro to return the string PORT-x, where, x is the return value of the called function. How can I do this?
EDIT :
I also tried writing it into multiple lines as:
#define TEST(x)\
{\
is_enabled(x);\
}
And called it in the main function as:
printf("PORT-%d\n", TEST(2));
But this gives a compile time error:
error: expected expression before â{â token
Use a function, not a macro. There is no good reason to use a macro here.
You can solve it by using sprintf(3), in conjonction with malloc or a buffer. See Creating C formatted strings (not printing them) or man pages for details.
About your edit: You don't need to use braces {} in a macro, and they are causing your error as preprocessing would translate it to something like
printf("format%d", {
is_enabled(x);
});
To better understand macros, run gcc or clang with -E flag, or try to read this article: http://en.wikipedia.org/wiki/C_preprocessor
That's a bit of a pain since you need to ensure there's storage for the string. In all honesty, macros nowadays could be reserved for conditional compilation only.
Constants are better done with enumerated types, and macro functions are generally better as inline functions (with the knowledge that inline is a suggestion to the compiler, not a demand).
If you insist on using a macro, the storage could be done with static storage though that has problems with threads if you're using them, and delayed/multiple use of the returned string.
You could also dynamically allocate the string but then you have to free it when done, and handle out-of-memory conditions.
Perhaps the easiest way is to demand the macro user provide their own storage, along the lines of:
#include <stdio.h>
#define TEST2_STR(b,p) (sprintf(b,"PORT-%d",p),b)
int main (void) {
char buff[20];
puts (TEST2_STR(buff, 42));
return 0;
}
which outputs:
PORT-42
In case the macro seems a little confusing, it makes use of the comma operator, in which the expression (a, b) evaluates both a and b, and has a result of b.
In this case, it evaluates the sprintf (which populates the buffer) then "returns" the buffer. And, even if you think you've never seen that before, you're probably wrong:
for (i = 0, j = 9; i < 10; i++, j--)
xyzzy[i] = plugh[j];
Despite most people thinking that's a feature of for, it's very much a different construct that can be used in many different places:
int i, j, k;
i = 7, j = 4, k = 42;
while (puts("Hello, world"),sleep(1),1);
(and so on).
After reading through some of K&R's The C Programming Language I came across the #define symbolic constants. I decided to define...
#define INTEGER_EXAMPLE 2
#define CHAR_EXAMPLE 2
...so my question is how does C know if I'm defining an int or a char type?
#define-d names have no types. They just define textual replacements.
What the compiler is seeing is the preprocessed form. If using GCC, try gcc -C -E somesource.c and have a look at the (preprocessed) output.
In the 1980s the preprocessor was a separate program.
Read about the cpp preprocessor, and preprocessor and C preprocessor wikipages.
You could even define ill-defined names like
#define BAD #*?$ some crap $?
And even more scary you can define things which are syntactically incomplete like
#define BADTASTE 2 +
and later code BADTASTE 3
Actually, you want to use parenthesis when defining macros. If you have
#define BADPROD(x,y) x*y
then BADPROD(2+3,4+5) is expanded to 2+3*4+5 which the compiler understands like 2+ (3*4) +5; you really want
#define BETTERPROD(x,y) ((x)*(y))
So that BETTERPROD(2+3,4+5) is expanded to ((2+3)*(4+5))
Avoid side-effects in macro arguments, e.g. BETTERPROD(j++,j--)
In general, use macros with care and have them stay simple.
Regarding these defines, it doesn't, the expanded macros doesn't have a type. The pre-processor which processes the #define is just replacing text within the source code
When you use these defines somewhere, e.g.
int i = INTEGER_EXAMPLE;
This will expand to
int i = 2;
Here the literal 2 (which in this context is an int) is assigned to an int.
You could also do:
char c = INTEGER_EXAMPLE;
Here too, the literal 2 is an int, and it is assigned to a char. 2 is within the limits of a char though, so all is ok.
You could even do:
int INTEGER_EXAMPLE = 2;
This would expand to
int 2 = 2;
Which isn't valid C.
#define STRING VALUE
is just an instruction for the pre-processor to replace the STRING with VALUE
afterwards the compiler will take control and will check the types
It doesn't, this is the preprocessor. The type of the constant is dependent on the context in which it is used. For instance:
#define INT_EXAMPLE 257
char foo = INT_EXAMPLE;
will attempt to assign 257 in a char context which should generate a warning unless char has more than 8 bits on your computer.
#Defines are nothing but literal replacements of values. You might want to use
static const
As it respects scope and is type-safe. Try this:
#define main no_main
int main() // gets replaced as no_main by preprocessor
{
return 0;
}
Should give you linking errors. Or you could try and fool your teacher by this
#define I_Have_No_Main_Function main //--> Put this in header file 1.h
#include"1.h"
int I_Have_No_Main_Function()
{
return 0;
}
It doesn't. The #define statements are processed before the compiler starts its work. Basically the pre-processor does a search and replace for what you wrote and replaces it, for instance, all instances of INTEGER_EXAMPLE are replaced with the string 2.
It is up to the compiler to decide the type of that 2 based on where it's used:
int x = INTEGER_EXAMPLE; // 2 is an integer
char y = INTEGER_EXAMPLE; // 2 is a char
Preprocessor cannot know the type of the macro definition. Preprocessor will just replace all occurrence of 'CHAR_EXAMPLE' with '2'. I would use cast:
#define CHAR_EXAMPLE ((char)2)
I have a very big constant array that is initialized at compile time.
typedef enum {
VALUE_A, VALUE_B,...,VALUE_GGF
} VALUES;
const int arr[VALUE_GGF+1] = { VALUE_A, VALUE_B, ... ,VALUE_GGF};
I want to verify that the array is initialized properly, something like:
if (arr[VALUE_GGF] != VALUE_GGF) {
printf("Error occurred. arr[VALUE_GGF]=%d\n", arr[VALUE_GGF]);
exit(1);
}
My problem is that I want to verify this at compile time. I've read about compile-time assert in C in this thread: C Compiler asserts. However, the solution offered there suggests to define an array using a negative value as size for a compilation error:
#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)
#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
and use:
CASSERT(sizeof(struct foo) == 76, demo_c);
The solution offered dosn't work for me as I need to verify my constant array values and C doesn't allow to init an array using constant array values:
int main() {
const int i = 8;
int b[i]; //OK in C++
int b[arr[0]]; //C2057 Error in VS2005
Is there any way around it? Some other compile-time asserts?
In the code below, see the extra assignement to pointers declared with fixed length in lines 6 and 9.
This will give errors on compile time if the 2 arrays are not initialized for all values of the WORKDAYS enum. Gcc says: test.c:6:67: warning: initialization from incompatible pointer type [enabled by default]
Imagine some manager adding SATURDAY to the work week enum. Without the extra checks the program will compile, but it will crash with segmentation violation when run.
The downside of this approach is that it takes up some extra memory (I have not tested if this is optimized away by the compiler).
It is also a little hackish and probably some comments are required in the code for the next guy...
Please observe that the arrays that are tested should not declare the array size. Setting the array size will ensure that you have reserved the data, but not ensure that it contains something valid.
#include <stdio.h>
typedef enum { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, NOF_WORKDAYS_IN_WEEK } WORKDAYS;
const char * const workday_names[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };
const char * const (*p_workday_name_test)[NOF_WORKDAYS_IN_WEEK] = &workday_names;
const int workday_efforts[] = { 12, 23, 40, 20, 5 };
const int (*p_workday_effort_test)[NOF_WORKDAYS_IN_WEEK] = &workday_efforts;
main ()
{
WORKDAYS i;
int total_effort = 0;
printf("Always give 100 %% at work!\n");
for(i = MONDAY; i < NOF_WORKDAYS_IN_WEEK; i++)
{
printf(" - %d %% %s\n",workday_efforts[i], workday_names[i]);
total_effort += workday_efforts[i];
}
printf(" %d %% in total !\n", total_effort);
}
By the way, the output of the program is:
Always give 100 % at work!
- 12 % Monday
- 23 % Tuesday
- 40 % Wednesday
- 20 % Thursday
- 5 % Friday
100 % in total !
The problem is that in C++ a compile-time constant expression has the following limitations (5.19 Constant expressions):
An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, and sizeof expressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
Remember that an array indexing expression is really just pointer arithmetic in disguise (arr[0] is really arr + 0), and pointers can't be used in constant expressions, even if they're pointers to const data. So I think you're out of luck with a compile time assertion for checking array contents.
C is even more limited than C++ in where these kinds of expressions can be used at compile time.
But given C++'s complexity, maybe someone can come up with a think-outside-the-box solution.
You can express your assertion as a property to check with a static analyzer and let the analyzer do the check. This has some of the properties of what you want to do:
the property is written in the source code,
it doesn't pollute the generated binary code.
However, it is different from a compile-time assertion because it needs a separate tool to be run on the program for checking. And perhaps it's a sanity check on the compiler you were trying to do, in which case this doesn't help because the static analyzer doesn't check what the compiler does, only what it should do.
ADDED: if it's for QA, then writing "formal" assertions that can be verified statically is all the rage nowadays. The approach below is very similar to .NET contracts that you may have heard about, but it is for C.
You may not think much of static analyzers, but it is loops and function calls that cause them to become imprecise. It's easier for them to get a clear picture of what is going on at initialization time, before any of these have happened.
Some analyzers advertise themselves as "correct", that is, they do not remain silent if the property you write is outside of their capabilities. In this case they complain that they can't prove it. If this happens, after you have convinced yourself that the problem is with the analyzer and not with your array, you'll be left where you are now, looking for another way.
Taking the example of the analyzer I am familiar with:
const int t[3] = {1, 2, 3};
int x;
int main(){
//# assert t[2] == 3 ;
/* more code doing stuff */
}
Run the analyzer:
$ frama-c -val t.i
...
t.i:7: Warning: Assertion got status valid.
Values of globals at initialization
t[0] ∈ {1; }
[1] ∈ {2; }
[2] ∈ {3; }
x ∈ {0; }
...
In the logs of the analyzer, you get:
its version of what it thinks the initial values of globals are,
and its interpretation of the assertion you wrote in the //# comment. Here it goes through the assertion a single time and finds it valid.
People who use this kind of tool build scripts to extract the information they're interested in from the logs automatically.
However, as a negative note, I have to point out that if you are afraid a test could eventually be forgotten, you should also worry about the mandatory static analyzer pass being forgotten after code modifications.
No. Compile-time assertion doesn't work in your case at all, because the array "arr[ARR_SIZE]" won't exist until the linking phase.
EDIT: but sizeof() seems different so at least you could do as the below:
typedef enum {VALUE_A, VALUE_B,...,VALUE_GGF} VALUES;
const int arr[] = { VALUE_A, VALUE_B, ... ,VALUE_GGF};
#define MY_ASSERT(expr) {char uname[(expr)?1:-1];uname[0]=0;}
...
// If initialized count of elements is/are not correct,
// the compiler will complain on the below line
MY_ASSERT(sizeof(arr) == sizeof(int) * ARR_SIZE)
I had tested the code on my FC8 x86 system and it works.
EDIT: noted that #sbi figured "int arr[]" case out already. thanks
As I'm using a batch file to compile and pack my application, I think that the easiset solution would be to compile another simple program that will run through all of my array and verify the content is correct.
I can run the test program through the batch file and stop compilation of the rest of the program if the test run fails.
I can't imagine why you'd feel the need to verify this at compile time, but there is one wierd/verbose hack that could be used:
typedef enum {
VALUE_A, VALUE_B,...,VALUE_GGF
} VALUES;
struct {
static const VALUES elem0 = VALUE_A;
static const VALUES elem1 = VALUE_B;
static const VALUES elem2 = VALUE_C;
...
static const VALUES elem4920 = VALUE_GGF;
const int operator[](int offset) {return *(&elem0+offset);}
} arr;
void func() {
static_assert(arr.elem0 == VALUE_A, "arr has been corrupted!");
static_assert(arr.elem4920 == VALUE_GFF, "arr has been corrupted!");
}
All of this works at compile time. Very hackish and bad form though.
I'd like to implement an "assert" that prevents compilation, rather than failing at runtime, in the error case.
I currently have one defined like this, which works great, but which increases the size of the binaries.
#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}
Sample code (which fails to compile).
#define DEFINE_A 1
#define DEFINE_B 1
MY_COMPILER_ASSERT(DEFINE_A == DEFINE_B);
How can I implement this so that it does not generate any code (in order to minimize the size of the binaries generated)?
A compile-time assert in pure standard C is possible, and a little bit of preprocessor trickery makes its usage look just as clean as the runtime usage of assert().
The key trick is to find a construct that can be evaluated at compile time and can cause an error for some values. One answer is the declaration of an array cannot have a negative size. Using a typedef prevents the allocation of space on success, and preserves the error on failure.
The error message itself will cryptically refer to declaration of a negative size (GCC says "size of array foo is negative"), so you should pick a name for the array type that hints that this error really is an assertion check.
A further issue to handle is that it is only possible to typedef a particular type name once in any compilation unit. So, the macro has to arrange for each usage to get a unique type name to declare.
My usual solution has been to require that the macro have two parameters. The first is the condition to assert is true, and the second is part of the type name declared behind the scenes. The answer by plinth hints at using token pasting and the __LINE__ predefined macro to form a unique name possibly without needing an extra argument.
Unfortunately, if the assertion check is in an included file, it can still collide with a check at the same line number in a second included file, or at that line number in the main source file. We could paper over that by using the macro __FILE__, but it is defined to be a string constant and there is no preprocessor trick that can turn a string constant back into part of an identifier name; not to mention that legal file names can contain characters that are not legal parts of an identifier.
So, I would propose the following code fragment:
/** A compile time assertion check.
*
* Validate at compile time that the predicate is true without
* generating code. This can be used at any point in a source file
* where typedef is legal.
*
* On success, compilation proceeds normally.
*
* On failure, attempts to typedef an array type of negative size. The
* offending line will look like
* typedef assertion_failed_file_h_42[-1]
* where file is the content of the second parameter which should
* typically be related in some obvious way to the containing file
* name, 42 is the line number in the file on which the assertion
* appears, and -1 is the result of a calculation based on the
* predicate failing.
*
* \param predicate The predicate to test. It must evaluate to
* something that can be coerced to a normal C boolean.
*
* \param file A sequence of legal identifier characters that should
* uniquely identify the source file in which this condition appears.
*/
#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)
#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
A typical usage might be something like:
#include "CAssert.h"
...
struct foo {
... /* 76 bytes of members */
};
CASSERT(sizeof(struct foo) == 76, demo_c);
In GCC, an assertion failure would look like:
$ gcc -c demo.c
demo.c:32: error: size of array `assertion_failed_demo_c_32' is negative
$
The following COMPILER_VERIFY(exp) macro works fairly well.
// combine arguments (after expanding arguments)
#define GLUE(a,b) __GLUE(a,b)
#define __GLUE(a,b) a ## b
#define CVERIFY(expr, msg) typedef char GLUE (compiler_verify_, msg) [(expr) ? (+1) : (-1)]
#define COMPILER_VERIFY(exp) CVERIFY (exp, __LINE__)
It works for both C and C++ and can be used anywhere a typedef would be allowed. If the expression is true, it generates a typedef for an array of 1 char (which is harmless). If the expression is false, it generates a typedef for an array of -1 chars, which will generally result in an error message. The expression given as an arugment can be anything that evaluates to a compile-time constant (so expressions involving sizeof() work fine). This makes it much more flexible than
#if (expr)
#error
#endif
where you are restricted to expressions that can be evaluated by the preprocessor.
As Leander said, static assertions are being added to C++11, and now they have.
static_assert(exp, message)
For example
#include "myfile.hpp"
static_assert(sizeof(MyClass) == 16, "MyClass is not 16 bytes!")
void doStuff(MyClass object) { }
See the cppreference page on it.
The best writeup that I could find on static assertions in C is at pixelbeat. Note that static assertions are being added to C++ 0X, and may make it in to C1X, but that's not going to be for a while. I do not know if the macros in the link I gave will increase the size of your binaries. I would suspect they would not, at least if you compile at a reasonable level of optimisation, but your mileage may vary.
If your compiler sets a preprocessor macro like DEBUG or NDEBUG you can make something like this (otherwise you could set this up in a Makefile):
#ifdef DEBUG
#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}
#else
#define MY_COMPILER_ASSERT(EXPRESSION)
#endif
Then, your compiler asserts only for debug builds.
Using '#error' is a valid preprocessor definition that causes compilation to stop on most compilers. You can just do it like this, for example, to prevent compilation in debug:
#ifdef DEBUG
#error Please don't compile now
#endif
I know you're interested in C, but take a look at boost's C++ static_assert. (Incidentally, this is likely becoming available in C++1x.)
We've done something similar, again for C++:
#define COMPILER_ASSERT(expr) enum { ARG_JOIN(CompilerAssertAtLine, __LINE__) = sizeof( char[(expr) ? +1 : -1] ) }
This works only in C++, apparently. This article discusses a way to modify it for use in C.
When you compile your final binaries, define MY_COMPILER_ASSERT to be blank, so that its output isn't included in the result. Only define it the way you have it for debugging.
But really, you aren't going to be able to catch every assertion this way. Some just don't make sense at compile time (like the assertion that a value is not null). All you can do is verify the values of other #defines. I'm not really sure why you'd want to do that.
I found this to give the least confusing error message for GCC. Everything else had some suffix about a negative size or some other confusing thing:
#define STATIC_ASSERT(expr, msg) \
typedef char ______Assertion_Failed_____##msg[1]; __unused \
typedef char ______Assertion_Failed_____##msg[(expr)?1:2] __unused
example usage:
unsigned char testvar;
STATIC_ASSERT(sizeof(testvar) >= 8, testvar_is_too_small);
And the error message in gcc (ARM/GNU C Compiler : 6.3.1):
conflicting types for '______Assertion_Failed_____testvar_is_too_small'
Well, you could use the static asserts in the boost library.
What I believe they do there, is to define an array.
#define MY_COMPILER_ASSERT(EXPRESSION) char x[(EXPRESSION)];
If EXPRESSION is true, it defines char x[1];, which is OK. If false, it defines char x[0]; which is illegal.