I need to write a C macro that checks to ensure all parameters passed to it are unsigned and of the same integer type. Ex: all input params are uint8_t
If the most important aspect here is that you want it to fail to compile if a and b are different types, you can make use of C11's _Generic along with GCC's __typeof__ extension to manage this.
A generic example:
#include
#define TYPE_ASSERT(X,Y) _Generic ((Y), \
__typeof__(X): _Generic ((X), \
__typeof__(Y): (void)NULL \
) \
)
int main(void)
{
int a = 1;
int b = 2;
TYPE_ASSERT(a,b);
printf("a = %d, b = %d\n", a, b);
}
Now if we try to compile this code, it will compile fine and everybody is happy.
If we change the type of b to unsigned int, however, it will fail to compile.
This works because _Generic selection uses the type of a controlling expression ((Y) in this case) to select a rule to follow and insert code corresponding to the rule. In this case, we only provided a rule for __typeof__(X), thus if (X) is not a compatible type for (Y), there is no suitable rule to select and therefore cannot compile. To handle arrays, which have a controlling expression that will decay to a pointer, I added another _Generic that goes the other way ensuring they must both be compatible with one another rather than accepting one-way compatibility. And since--as far as I particularly cared--we only wanted to make sure it would fail to compile on a mismatch, rather than execute something particular upon a match, I gave the corresponding rule the task of doing nothing: (void)NULL
There is a corner case where this technique stumbles: _Generic does not handle Variably Modifiable types since it is handled at compile time. So if you attempt to do this with a Variable Length Array, it will fail to compile.
To handle your specific use-case for fixed-width unsigned types, we can modify the nested _Generic to handle that rather than handling the pecularities of an array:
#define TYPE_ASSERT(X,Y) _Generic ((Y), \
__typeof__(X): _Generic ((Y), \
uint8_t: (void)NULL, \
uint16_t: (void)NULL, \
uint32_t: (void)NULL, \
uint64_t: (void)NULL \
) \
)
Example GCC error when passing non-compatible types:
main.c: In function 'main':
main.c:7:34: error: '_Generic' selector of type 'signed char' is not compatible with any association
7 | __typeof__(X): _Generic ((Y), \
| ^
It is worth mentioning that __typeof__, being a GCC extension, will not be a solution that is portable to all compilers. It does seem to work with Clang, though, so that's another major compiler supporting it.