I recently discovered that something compiles(not sure that it’s legal though). My need for such a thing comes from this: My project outputs machine code for a selected arch.(which may or may not be the same arch. as the one running the program). So, I would like to support up to 64bit architectures right now(while also supporting existing 32 and 16 bit archs.) My current solution is for new_state’s ‘base’ to just be a uint64_t, and manually casting to 16 and 32 bits as needed. Though, I discovered you can compile unions in a function parameter. So a function as this compiles:
int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
union{
uint16_t b16;
uint32_t b32;
uint64_t b64;
}base ,int self_running);
Is this kind of thing at all “legal” though or supported by any other compilers? And also, I can’t figure out how to call this function without also creating a union and then passing this union to new_state.
To summarize: Yes, that is valid in C, although being illegal in C++. The latter contains this note which explains the difference
The structural equivalence in C is done by the concept of “type compatibility”. This allows C to treat many types as if they were identical, even though they are theoretically distinct – because they are declared in two different translation units. In C++, this concept doesn’t exist, because types have linkage and are matched to the same entity (i.e to allow member functions to link against each other).
Note that the above cited explanation is based off C89, which did not consider the tag name of a struct in determining type compatibility. In a C89 draft, the relevant text reads as the following:
In C99, type checking is more stricter: If one struct has a tag name, the other struct declaration has to have that same tag name. So in your unnamed union type case, to declare a function in another TU that has a compatible type, you would need an unnamed union again if you want to have valid C99 code (without undefined behavior) – you cannot “trick” around, and use a named union in one TU, and an unnamed union in another TU. It looks to me that this “trick” is valid for C89, though.
C99 TC3 6.2.7/1:The way you want to do it doesn’t work. Calling a function will convert the arguments to the type of the parameters as if by normal assignment.
So for this to work, you will have to have an argument that’s compatible with the parameter type. For two unions declared in the same translation unit, this means that their type must equal – that’s the only way you can come up with a compatible type within the same translation unit. But this cannot work, because the declaration of the unnamed union creates an unique new type – no way to “refer back” to it using another declaration.
So, to summarize – you have to give the union type a name. To avoid creating a separate variable to pass the needed base argument, I would declare it outside the function, and create functions that give back an union you may pass over
Now, it can look like the following