2021-08-01
Unix APIs are where I first saw C #define
used to rename struct
fields
In my entry on learning that you can use C unions for grouping
struct fields into namespaces, I
mentioned that the traditional version of this was done with #define
to create convenient short names for fields that were actually hidden
deeper in a struct
than they appeared. I didn't come up with this idea
on my own; instead, I'm pretty sure that where I first saw this was in
Unix APIs. More exactly, in the actual implementation of Unix APIs, as
visible in C header files. Most often, I think this was done because
of unions in the struct, which at the time had to be named.
A typical example that I can find is in the 4.2 BSD arpa/tftp.h
header:
struct tftphdr { short th_opcode; /* packet type */ union { short tu_block; /* block # */ short tu_code; /* error code */ char tu_stuff[1]; /* request packet stuff */ } th_u; char th_data[1]; /* data or error string */ }; #define th_block th_u.tu_block #define th_code th_u.tu_code #define th_stuff th_u.tu_stuff
Here various #define
s are being used to give short names to things
that are actually inside a union. Today we could do this directly
with an anonymous union, but those only officially appeared in C11
after being a GNU gcc extension.
(Another example is in the 4.2 BSD wait.h
,
which despite its position in the tree here was also in /usr/include
;
see MAKEDIRS
.
Another generally more internal use is in inode.h
.)
This approach would have serious problems with th_block
or other
#defined
d fields if they were also used in one of your own struct
s
for some other purpose. But in the very old days of C, struct
fields
had to be globally unique across your entire program, so it was common
to give them a relatively unique name and you already knew you had problems
if you reused a field name from what the manpage said was a struct
that
was part of a library API.
(I haven't looked at how the early C compilers implemented struct
fields, but I suspect that the V7 C compiler just put all struct
field names into a global table with their type and offset. This
seems a very V7 shortcut.)