Reputation: 2443
I have the following code declared in my header file:
//Integer Vector4D stuffs
typedef union {
size_t data[4];
struct {
size_t x;
size_t y;
size_t z;
size_t w;
};
struct {
size_t x;
size_t y;
size_t width;
size_t height;
};
} Vector4Di;
//End Integer Vector4D stuffs
I am compiling the code for Windows and Linux. I'm using Windows 10 Pro with WSL.
This is my compilation build output taken from Microsoft Visual Studio 2017:
1>------ Build started: Project: SDL, Configuration: Debug x64 ------
1>C:main_pc.cpp
1>1 File(s) copied
1>block.cpp
1>common.cpp
1>draw.cpp
1>game.cpp
1>input.cpp
1>main.cpp
1>Generating Code...
1>SDL.vcxproj -> C:\Users\tom_mai78101\Documents\VSProjects\SDL\x64\Debug\SDL.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Given the above build log, this shows that the build has succeeded when compiling.
That same source code, however, would throw errors in GCC:
$ make
clean ...
build ...
main.cpp
In file included from /mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/main.cpp:1:0:
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h: At global scope:
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:358:10: error: redeclaration of 'size_t <unnamed union>::<unnamed struct>::x'
size_t x;
^
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:352:10: note: previous declaration 'size_t <unnamed union>::<unnamed struct>::x'
size_t x;
^
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:359:10: error: redeclaration of 'size_t <unnamed union>::<unnamed struct>::y'
size_t y;
^
/mnt/c/Users/tom_mai78101/Documents/VSProjects/SDL/SDL/../SDL/game/common.h:353:10: note: previous declaration 'size_t <unnamed union>::<unnamed struct>::y'
size_t y;
^
The gist is, in GCC, it would complain about how size_t x;
and size_t y;
are both redeclared inside the union, Vector4Di
. But MSBuild doesn't throw errors in regards to this, and it successfully compiled the code.
To my knowledge, I believed unions with same types and same variable names should not be in conflict with another, especially when the variables are in a struct. Especially when compiling for C++11, where it was known that C++11 has better support for unions.
May I ask why it is throwing the error for GCC? And how should I fix this error so both MSBuild and GCC both compiled successfully?
Thanks in advance.
Upvotes: 1
Views: 419
Reputation: 126877
The C++ standard does not admit anonymous struct
not being used to define a member of its type inside a union
; using an anonymous struct
to inject its members in the enclosing union
is just a widespread extension, but it's not standard C++, so it's not strictly well-defined how this mechanism works.
In particular, here you are treading in dangerous waters, as you are having two anonymous structs with members with the same names; from the C++ standard point of view this is definitely murky, as it is ambiguous what is the "active union member" when you do e.g. v4d.x = 5;
.
VC++ documentation does not describe what happens in this case; a quick check tells me that it doesn't get mad even if the names refer to conflicting memory locations - it just seems to refer to the member of the first anonymous struct being defined, which is completely nuts if you ask me.
gcc instead does get mad, as it's like declaring two members with the same name in the same struct
; its documentation actually says:
You must never create such structures that cause ambiguous field definitions. For example, in this structure:
struct { int a; struct { int a; }; } foo;
it is ambiguous which a is being referred to with
foo.a
. The compiler gives errors for such constructs.
So, this isn't something you can work around with compiler switches or something; gcc doesn't support it, period.
This vision is confirmed by the only standard where this feature is actually described formally, which is the ISO C 11 standard; there, at §6.7.2.1 ¶13, it is said:
An unnamed member of structure type with no tag is called an anonymous structure; an unnamed member of union type with no tag is called an anonymous union. The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.
and named members of a struct are required to have unique names.
So, to recap:
if you want this to become standard C++, you have to give some names to your sub-structures; you can do:
union Vector4Di {
size_t data[4];
struct {
size_t x;
size_t y;
size_t z;
size_t w;
} q;
struct {
size_t x;
size_t y;
size_t width;
size_t height;
} r;
};
adjust the rest of your code and be happy with it.
if you are ok with using those extensions, you can still make it somehow work by naming x
and y
differently in the second struct
, as in:
union Vector4Di {
size_t data[4];
struct {
size_t x;
size_t y;
size_t z;
size_t w;
};
struct {
size_t dummy_x;
size_t dummy_y;
size_t width;
size_t height;
};
};
in this case you could freely work with x
and y
and width
and height
, and I suspect it's even pretty much ok standard-wise - if we ignore the "anonymous struct" thing; the two structs are standard-layout and are layout-compatible, so the standard guarantees that you can inspect the "common initial sequence" using whatever names you prefer.
Upvotes: 3
Reputation: 2443
Ok, this is something I learned today. And that is C++ does not play nicely with anonymous structs / unions. According to the comments, I should add tag names to the structs and unions.
So this is the new modified version:
//Integer Vector4D stuffs
typedef union {
size_t data[4];
struct quaternion {
size_t x;
size_t y;
size_t z;
size_t w;
} quaternion;
struct rect {
size_t x;
size_t y;
size_t width;
size_t height;
} rect;
} Vector4Di;
//End Integer Vector4D stuffs
Now, both Visual Studio and GCC compile the same project and source codes just fine, and no longer throw errors about the redeclaration of variables of same names and same types. I also don't need to modify the rest of the codes to accommodate the new changes, despite that this Vector4Di
struct is declared in the headers, so this is a great fix.
Thanks to MatteoItalia for the help.
Upvotes: 0