I have been thinking about ways to validate types in C macros and so far the best way that I have come up with is this:
#define ASSERT_PTYPE(TYPE, VALUE) (0 && (*(int (*)(TYPE*))0)(VALUE))
This obviously expects a type name and a pointer to that type. A similar ASSERT_TYPE macro can be made as well. This seems to work quite well with GCC. It even gives a very helpful error message in the case that the types do not match. The problems are that I am not completely certain that this is valid C or the best way for that matter.
As I understand it the standard says that you can cast a function pointer, but the result of calling the cast function pointer is undefined. In this case it is impossible for the function to be called at runtime. Is that good enough or does the standard mean that you cannot even write code that cannot be called that calls the cast function?
In macros, no type checking(incompatible operand, etc.) is done and thus use of macros can lead to errors/side-effects in some cases. However, this is not the case with functions. Also, macros do not check for compilation error (if any).
Inside of a C source file, you can use the #ifdef macro to check if a macro is defined.
The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.
There are two kinds of macros. They differ mostly in what they look like when they are used. Object-like macros resemble data objects when used, function-like macros resemble function calls. You may define any valid identifier as a macro, even if it is a C keyword.
With C99 and compound literals you can do something like
#define ASSERT_TYPE(TYPE, VALUE) ((TYPE){ 0 } = (VALUE))
This ensures that VALUE
is assignment compatible to TYPE
. The expression returns an rvalue because of the assignment.
Compound literals work in function scope as well as in file scope and any decent compiler should optimize the extra object that is created out of the way.
Addition: TYPE
in that macro can be any valid type name, e.g pointer double*
, struct or union struct toto
, besides arrays. Array type such as double[4]
wouldn't work because of the assignment. Use pointer to
array double(*)[4]
instead, e.g as in
double A[4];
(*ASSERT_TYPE(double(*)[4], &A))
where the second line again is a lvalue of type double[4]
that is compile time checked for that property.
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