To summarize: Yes, this is valid in C, although it is illegal in C ++. The latter contains this note which explains the difference
Edit: In C ++, types cannot be defined in reverse order or in parameter types. In C, these type definitions are allowed.
Example:
void f( struct S { int a; } arg ) {}
- Rationale: When comparing types in different compilation units, C ++ relies on name equivalence when C relies on structural equivalence. As for the types of parameters: since the type specified in the parameter list will be in the scope of the function, the only legal calls in C ++ will be performed inside the function itself.
- Impact on the original function: Removing a semantically well-defined function.
- The complexity of the transformation: Semantic transformation. Type definitions should be moved to the file area or to header files.
- How widely used: Rarely. This type definition style is considered a bad coding style.
Structural equivalence in C is accomplished by the concept of type compatibility. This allows C to handle many types as if they were identical, although they are theoretically different - because they are declared in two different translation units. In C ++, this concept does not exist, because types have a binding and are mapped to the same object (for example, so that member functions can communicate with each other).
Note that the above explanation is based on C89, which does not take into account the name of the structure tag when determining type compatibility. In project C89, the corresponding text is read as follows:
In addition, two types of structure, association, or enumeration declared in separate translation units are compatible if they have the same number of members, the same member names, and compatible element types; for two structures, members must be in the same order;
In C99, type checking is more stringent: if one structure has a tag name, another structure declaration must have the same tag name. Therefore, in your case of type unnamed union, in order to declare a function in another TU that has a compatible type, you will need unnamed union again if you want to have a valid C99 code (without undefined behavior) - you cannot "cheat" and use the named union in one TU and an unnamed alliance in another TU. It seems to me that this "trick" is valid for C89. C99 TC3 6.2.7/1 :
In addition, two types of structure, union, or enumeration declared in separate translation units are compatible if their tags and members satisfy the following requirements: if declared by a tag, the other is declared with the same tag. If both are complete types, the following additional requirements apply: there must be a one-to-one correspondence between their members, so that each pair of the corresponding members is declared with compatible types and such that if one member of the corresponding pair is declared with a name, the other member is declared with the same by name. For two entities, the corresponding members must be declared in the same order.
The way you want to do this does not work. The function call converts the arguments to the parameter type, as if they were executed in the usual way.
So, for this you will have to have an argument compatible with the parameter type. For two unions declared in the same translation unit, this means that their type must be equal - this is the only way you can create a compatible type within the same translation unit. But this will not work, because the declaration of an unnamed union creates a unique new type - do not "refer" to it in any way using another declaration.
So, to summarize - you must specify the union name type. To avoid creating a separate variable to pass the necessary base argument, I would declare it outside the function and create functions that return a union that you can pass
union base_type { uint16_t b16; uint32_t b32; uint64_t b64; }; int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, union base_type base,int self_running); union base_type base_b16(uint16_t t) { union base_type b; b.b16 = t; return b; } union base_type base_b32(uint32_t t) { union base_type b; b.b32 = t; return b; } union base_type base_b64(uint64_t t) { union base_type b; b.b64 = t; return b; }
Now it might look like this
pcg_new_state(...., base_b32(4211), ....);