Lately, I find myself writing more and more code that uses void* pointers as handles to implementation-specific data. This can get confusing when there is more than one type of handle. Imagine we have these two types:
1 2 |
|
It is then possible to assign a value of type HBITMAP to a variable of type HWINDOW, with no complaints from the compiler.
Let me show you some example code to illustrate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
The C compiler will compile this without a warning, even though I obviously mixed up the arguments to the Destroy calls.
The goal of today’s excercise is to get a warning from the compiler. While C does not distinguish different typedef’s that map to the same internal type as multiple types, it does recognize different structs. So we’ll define our handle-types like this:
1 2 |
|
With that change, these two are different types, and the compiler will give us an error message:
$ gcc -Wall -c types.c
types.c: In function ‘main’:
types.c:11: error: incompatible type for argument 1 of ‘DestroyBitmap’
types.c:12: error: incompatible type for argument 1 of ‘DestroyWindow’
Note that these types are just as big as the void* in the struct (a struct causes no additional memory overhead), so copying them is no less efficient. Assignment works just like before, because C assigns structs by copying.
This elegant little epiphany was brought to me by Kevin and Imran, who is fanatic about type-safety, and was doing this to basic types like int, float and char, to avoid accidental implicit conversion between them.