C预处理器中创建含自定义结构体的union是否可行及实现方案
Absolutely yes! The C preprocessor’s macro system (especially with C99’s variadic macros) lets you dynamically generate unions that wrap your custom structs. This is a handy trick for serialization scenarios, especially when you want to avoid repeating boilerplate code. Let me walk you through how to pull this off, plus some alternatives if the preprocessor approach doesn’t fit your needs.
The core idea is to use a variadic macro that accepts your union name and the list of struct members you want to include. Here’s a step-by-step example:
1. Define your custom structs first
Start by creating the structs you want to pack into the union:
// Example struct 1: User data typedef struct { int user_id; char username[32]; float account_balance; } User; // Example struct 2: Geospatial point typedef struct { double latitude; double longitude; int accuracy_meters; } GeoPoint;
2. Create a macro to generate the union
Use __VA_ARGS__ (a C99+ feature) to accept a variable number of struct members:
#define GENERATE_SERIALIZABLE_UNION(union_name, ...) \ typedef union { \ __VA_ARGS__ \ } union_name;
3. Use the macro to build your union
Call the macro with your union name and the struct members (each followed by a semicolon):
GENERATE_SERIALIZABLE_UNION(SerializedData, User user_record; GeoPoint location; )
This expands to valid C code automatically:
typedef union { User user_record; GeoPoint location; } SerializedData;
Bonus: Add a type tag (critical for serialization)
For serialization, you almost always need to track which member of the union is active. Extend the macro to include a tag enum:
// First define your tag enum typedef enum { TAG_USER, TAG_GEOPOINT // Add more tags as needed } SerializationTag; // Updated macro with a built-in type tag #define GENERATE_TAGGED_UNION(union_name, tag_type, ...) \ typedef union { \ tag_type type_tag; \ __VA_ARGS__ \ } union_name; // Use it to create a tagged union GENERATE_TAGGED_UNION(TaggedSerializedData, SerializationTag, User user_record; GeoPoint location; )
Now you can set type_tag to TAG_USER when storing a User, and check it later during deserialization to know which member to access safely.
- C99 requirement:
__VA_ARGS__is a C99 feature, so make sure your compiler supports it (most modern compilers do, but double-check if you’re targeting legacy systems). - Semicolons matter: Each struct member in the macro call must end with a semicolon—otherwise the preprocessor will generate invalid C code.
- Name collisions: Avoid giving union members identical names (e.g., two structs both having an
idfield is fine, but naming both union membersdatawill cause conflicts).
If the preprocessor feels too hacky or doesn’t fit your workflow, here are solid alternatives:
- Manual union definition: For small numbers of structs, just write the union by hand. It’s straightforward and easier to debug than macro-generated code.
- Code generation scripts: Write a simple Python/Perl/bash script that reads a list of structs (from a config file or header) and outputs the union code. This is great if you have dozens of structs—you can update the config instead of editing macros.
- Type-erased pointers (not recommended): You could use a
void*to store any struct, but this is type-unsafe and requires manual type tracking. It’s rarely a good choice for serialization since it bypasses C’s type safety.
内容的提问来源于stack exchange,提问作者Charlie Sale

