Monday, September 22, 2008

Compile-time assertion to detect signed and unsigned types in C

I was wondering how to tell whether a given typedef was a signed or unsigned integral type (e.g. unsigned int vs signed int, size_t vs ssize_t, etc.) in C. In C++ it would be easy with a little bit of meta-programing but in C there are far fewer options when it comes to static (aka compile-time) checking. And on this one, Google didn't help me much. So I thought I'd write something, hopefully it'll be helpful to someone searching what I was trying to find.

Here is a simple trick:
#define IS_UNSIGNED_TYPE(Type) \
char ERROR_ ## Type ## _MUST_BE_UNSIGNED[((Type) -1 < 0) * -1]

Now if you call IS_UNSIGNED_TYPE(ssize_t); GCC will give you a nice error: size of array 'ERROR_ssize_t_MUST_BE_UNSIGNED' is negative, whereas with size_t it compiles fine.

For those who don't understand how this works, it's pretty simple: the two hashes (##) are used to concatenate tokens in the macro. So for instance for the case of ssize_t we have:
  char ERROR_ssize_t_MUST_BE_UNSIGNED[((ssize_t) -1 < 0) * -1]
char ERROR_ssize_t_MUST_BE_UNSIGNED[(-2147483648 < 0) * -1]
char ERROR_ssize_t_MUST_BE_UNSIGNED[1 * -1]
char ERROR_ssize_t_MUST_BE_UNSIGNED[-1] // Invalid
Whereas for size_t we have:
  char ERROR_size_t_MUST_BE_UNSIGNED[((size_t) -1 < 0) * -1]
char ERROR_size_t_MUST_BE_UNSIGNED[(4294967295 < 0) * -1]
char ERROR_size_t_MUST_BE_UNSIGNED[0 * -1]
char ERROR_size_t_MUST_BE_UNSIGNED[0] // Valid
Not particularly useful but ... Kind of neat, isn't it?