• Please review our updated Terms and Rules here

C item size check at compile time

Chuck(G)

25k Member
Joined
Jan 11, 2007
Messages
44,554
Location
Pacific Northwest, USA
Thought I'd submit a C chestnut for those who might not know about it. Suppose that I wanted to check the size of a struct in C (C++ has a workaround) at compile time without generating any code. The issue is that the C preprocessor doesn't know anything about the sizeof() operator and will always return 0. You could use an "assert" statement (assert.h), but that generates code and operates only at run-time. What you really want is to be alerted to a size miscalculation at compile time.

Here's a solution and example:

Code:
#ifndef size_assert
#define size_assert( what, howmuch ) \
  typedef char what##_size_wrong_[( !!(sizeof(what) == howmuch) )*2-1 ]
#endif

size_assert( CMS_END, CMS_END_SIZE);
If the size agrees, the compiler will be silent, but if it disagrees, you'll get an error message like the following:
Code:
cms.h:56:14: error: size of array ‘CMS_END_size_wrong_’ is negative
   56 | size_assert( CMS_END, CMS_END_SIZE);
      |              ^~~~~~~

Here's another one, applicable to GCC only, as far as I know. Suppose you have a number of big-endian values and you want to convert them to little-endian ones. You could use explicit functions (AND, shift and OR) to do it, or you could use the builtin swapxx library functions. Normally, you'd have to do that with care, so that the swap function matched the width of the big-endian variable. Howeverr, you can use a single unified preprocessor macro to handle that:

Code:
//      Macro to get value of a BE value--uses GCC builtins

#define BEGET(x) \
  __builtin_choose_expr ( \
    __builtin_types_compatible_p( typeof(x), uint16_t),\
     __builtin_bswap16(x), \
  __builtin_choose_expr ( \
    __builtin_types_compatible_p( typeof(x), uint32_t),\
     __builtin_bswap32(x), \
  __builtin_choose_expr ( \
    __builtin_types_compatible_p( typeof(x), uint64_t),\
     __builtin_bswap64(x), \
  x )))
Thus, if a big-endian variable is 8, 16, 32, or 64 bits, BEGET() will pick the appropriate function automatically.
 
Last edited:
The first trick I knew about, it is a common static assert and should work across all compilers.

The second one is quite neat. I wonder if it can be extended to compile-time fail if the type is not on the list.
 
The first one can be found in the Linux kernel code, among other places. It's darned useful and a response to the "Why doesn't C have a static_assert operator?" noobs.

You've got to be careful with the "types_compatible" pseudo-op. If you reversed the order of the checks, an int16 would be counted as compatible with an int64. But I use it in code involving migration from other systems, such as IBM S/370 and it works just fine.

There's been some discussion about GCC and how execrable the code for the __builtin_swap function is on certain platforms. But it's fine on x86.
 
Back
Top