Twig Technology

[...] incredibly interested in all the things you could do with twigs.

Type-safe Struct Wrappers in C

| Comments

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
typedef void *HBITMAP;
typedef void *HWINDOW;

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
typedef void *HBITMAP;
typedef void *HWINDOW;
HWINDOW CreateWindow(void);
HBITMAP CreateBitmap(void);
void DestroyBitmap(HBITMAP);
void DestroyWindow(HWINDOW);

int main(void) {
    HWINDOW wnd = CreateWindow();
    HBITMAP bmp = CreateBitmap();
    DestroyBitmap(wnd);
    DestroyWindow(bmp);
    return 0;
}

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
typedef struct { void *ptr; } HBITMAP;
typedef struct { void *ptr; } HWINDOW;

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.

Comments