Convenient C macros: enum(X), struct(X), union(X)
Sunday, June 30, 2024
Of the many C macros I set up when starting a new project, three of the most clever (i.e. hacky) are these:
#define enum(X) typedef enum X X; enum X #define struct(X) typedef struct X X; struct X #define union(X) typedef union X X; union X
(Yes, I have a low tolerance for hackiness when it comes to C macros.)
If their function is not immediately obvious, let me show you how they are used:
struct (Rect) { int32_t x; int32_t y; int32_t width; int32_t height; }; enum (Http_Request) { HTTP_REQUEST_GET = 1, HTTP_REQUEST_POST, HTTP_REQUEST_HEAD, HTTP_REQUEST_PUT, }; union (Value) { int integer; char *string; };
Note that, unlike ordinary type declarations, in the example above the type
name is enclosed in parentheses. This causes the preprocessor to read them as
function macro invocations, translating them into this (output from
gcc -E
):
typedef struct Rect Rect; struct Rect { int32_t x; int32_t y; int32_t width; int32_t height; }; typedef enum Http_Request Http_Request; enum Http_Request { HTTP_REQUEST_GET = 1, HTTP_REQUEST_POST, HTTP_REQUEST_HEAD, HTTP_REQUEST_PUT, }; typedef union Value Value; union Value { int integer; char *string; };
These macros allow me to declare a type in the “tagged” namespace (where you
have to include the struct
, union
, or
enum
keyword before the type name), and then automatically add
them to the untagged type namespace. This is like how type names work in
C++.
These macros are not portable because enums can’t be forward-declared without GNU extensions (and I’m not sure about giving a macro the same name as a keyword). However, they work in Clang and GCC, and I find them quite convenient.
One caveat is that these macros turn what looks like one declaration into two. I don’t think there is a realistic scenario where this could cause an issue, and if that did happen, it would be caught by the compiler because the types need to be known statically.
Using these macros doesn’t break code that uses the ordinary type
declarations because the preprocessor won’t read declarations like this as an
invocation of the struct
macro:
struct foo { int index; };