Here is a macro that should work across clang, gcc and msvc (haven't tested the msvc version):
#define YDUMMY(suffix, size) char dummy##suffix[size]
#define XDUMMY(suffix, size) YDUMMY(suffix, size)
#define DUMMY(size) XDUMMY(__COUNTER__, size)
#ifdef __GNUC__
#define EXPLICIT_UNION_START(name) union Test {
#define EXPLICIT_UNION_END() };
#define EXPLICIT_OFFSET_FIELD(foff, ftype, fname) struct __attribute__((packed)) { DUMMY(foff); ftype fname; };
#elif defined(_MSC_VER)
#define EXPLICIT_UNION_START(name) #pragma pack(push, 1) \
union Test {
#define EXPLICIT_UNION_END() }; \
#pragma pack(pop)
#define EXPLICIT_OFFSET_FIELD(foff, ftype, fname) struct { DUMMY(foff); ftype fname; };
#else
#error "What compiler is this?"
#endif
EXPLICIT_UNION_START(Test)
EXPLICIT_OFFSET_FIELD(0, int, a)
EXPLICIT_OFFSET_FIELD(1, int, b)
EXPLICIT_OFFSET_FIELD(3, int, c)
EXPLICIT_UNION_END()
Thanks to unnamed fields, the syntax to access your defined fields is not polluted by dummy names:
int main() {
union Test t;
t.b = 13;
printf("offset a = %zx\n", offsetof(union Test, a));
printf("offset b = %zx\n", offsetof(union Test, b));
printf("offset c = %zx\n", offsetof(union Test, c));
printf("t.b = %d\n", t.b);
return 0;
}