Let's say you're writing a function that accepts a pointer to an int.
1 void do_stuff_with_int(int *p) {
2 /* ... */
3 }
Later, you notice that the doing stuff operation doesn't depend on signedness at all, and decide that you want to use your function with unsigned int too. Just passing a pointer to an unsigned int will cause a warning. How would you get rid of it in a clean way? Easy, use a union.
1 typedef union {
2 int *si;
3 unsigned int *ui;
4 } signed_or_unsigned_int_pointer;
Now you have a type that can both contain a pointer to an int, or a pointer to an unsigned int. Wouldn't it be convenient if you could use your shiny new type to indicate that do_stuff_with_int() really can accept both types? That's where gcc's transparent_union attribute comes in. (Some other compilers also support this.)
1 typedef union {
2 int *si;
3 unsigned int *ui;
4 } signed_or_unsigned_int_pointer
5 __attribute__((transparent_union));
6
7 void do_stuff_with_int(signed_or_unsigned_int_pointer p) {
8 /* ... */
9 }
10
11 int main() {
12 int a = 0;
13 unsigned int b = 0;
14
15 do_stuff_with_int(&a);
16 do_stuff_with_int(&b);
17
18 return 0;
19 }
Now, what's the catch? You can only use transparent_union with unions that contain only types with the same representation. In other words, they need to be the same size. You can't, for example, use it with a 32-bit int and a 64-bit integer, in which case gcc will generate a warning. And lots of errors later on, because you're not passing unions to your function, but any one of their constituent types, which isn't allowed without transparent_union.
And now, the reason why this post is related to portability:
1 typedef union {
2 int an_int;
3 long a_long;
4 } int_union
5 __attribute__((transparent_union));
The transparent_union attribute will be ignored on most 64 bit platforms.
Treating int and long as the same type is a beginner's mistake. But what about this code?
1 typedef union {
2 int volatile *i;
3 unsigned int volatile *u;
4 } int_union
5 __attribute__((transparent_union));
Should work, shouldn't it?
Turns out it doesn't. That is, it works in most places. It even works on Mac OS X. Unless you build for the 64-bit PowerPC CPU. In that case, the attribute is ignored, even though the two types are still the same size. This only seems to happen on ppc64. On all three other architectures currently supported by Mac OS X, it works.
1 $ gcc -c t.c -arch ppc
2 $ gcc -c t.c -arch ppc64
3 t.c:6: warning: 'transparent_union' attribute ignored
4 $ gcc -c t.c -arch i386
5 $ gcc -c t.c -arch x86_64
Hello there. I'm Danny, and also, going to use this blog to post stories about all the minor and not so minor annoyances in building software on different platforms from the same code base. If you think that doesn't sound like a lot of fun, you're probably not the type of person that enjoys digging into the build process and stepping through each individual stage between "source file" and "binary". I'm not saying that I enjoy it myself, but it's part of what I do for a living. And maybe I do actually enjoy it a little, too