Thursday, August 11, 2005

Compile Time Assertions

A friend and I got into a discussion in coffee day last night about a review he was busy with. The code he was reviewing converts an integer to string. The author was taking the value of the integer, calculating log10 of the number and adding 2 to it to calculate the length of the array for storing the converted string.

This array would then be malloc'ed to store the string.

There are obvious deficiencies in this code

It is slow (due to the log10 calculation and malloc)

on the other hand

This code would scale better than any other code, for example if the integer size of the platform changed.

I suggested that the author do the following

1. Calculate the maximum array size for an int of 4 bytes (remember to include space for a possible "-"). Currently (11+1(possible "-")+1 ('\0' character)) bytes for a 4 byte integer.
2. Use compile time assertion to assert the size of the integer being converted.

If you are wondering what compile time assertions do, look at the code below

1 #define CT_ASSERT(cond, msg) \
2 typedef char msg[(cond) ? 1 : -1]
3
4 int
5 main(void)
6 {
7 int i = 10;
8
9 CT_ASSERT(sizeof(i) == 4, size_must_be_4);
10
11 }

Compile time assertions try to use the preprocessor and create situations that are illegal. The "C" compiler cannot compile the preprocessed code, if the assertion fails. See the example above.

On a platform where this condition fails, the output would be

cassert.c: In function `main':
cassert.c:9: size of array `size_must_be_4' is negative

This ensures that there is no runtime overhead of catching assertions and yet the code is fast and will break if the size of the integer changes.

Other sources of information
  1. Catching errors early with compile-time assertions
  2. Compile Time Assertions in C

No comments:

Ranking and Unranking permutations

I've been a big fan of Skiena's Algorithm Design Manual , I recently found my first edition of the book (although I own the third ed...