Reputation: 64682
When calling a C-function its OK to call with a wider type by passing a narrower type that can be converted/promoted into the wider type.
For example:
void FooBar(uint32_t alpha); // Function takes a 32-bit unsigned value
int main(void)
{
uint16_t foo = 232; // Variable foo is 16-bit unsigned
FooBar(foo); // 16-bit value gets auto-promoted to 32-bit value for the function call.
}
However, when I try to do this with _Generic
in C11, auto-promotion seems to be lost:
#define FooBar(x) _Generic((x), uint32_t: FooBar_U32,\
double: FooBar_DBL)(x)
int main(void)
{
uint16_t foo = 232; // Variable foo is 16-bit unsigned
FooBar(foo); // Try to promote a 16-bit to 32-bit value for FooBar_U32
float nib = 3.14;
FooBar(nib); // Try to promote a float to a double for FooBar_DBL
}
The error is:
prog.c:7:28: error: ‘_Generic’ selector of type ‘short unsigned int’ is not compatible with any association
I would certainly expect a short unsigned int
to be compatible with a unsigned int
(32-bit). So I do not understand the error message.
Is it impossible to use _Generic
and get the benefits of type-promotion?
Am I doing something wrong here?
Upvotes: 0
Views: 114
Reputation: 180306
This is "compatible" in the sense defined by the language specification, not in the much broader sense of pairs of types between which automatic conversions are defined, or where the integer promotions apply. Relevant bits are spread across several sections of the specification, including at least sections 6.2.7 ("Compatible type and composite type"), 6.7.2 ("Type specifiers"), 6.7.3 ("Type qualifiers"), and 6.7.6 ("Declarators"). For arithmetic types other than enumerated types, however, "compatible type" simply boils down to the same type, including type qualifiers.
That is indeed very strict. More so, for example, than is required by the strict aliasing rule for accessing an object of one type via an lvalue of a different type. Much more so than is required for type matching across assignments or for matching function arguments to corresponding parameters. And no, short unsigned int
is not compatible with unsigned int
in this sense, not even in implementations where the two types are the same size.
Is it impossible to use
_Generic
and get the benefits of type-promotion?
You get lvalue conversion, which drops type qualifiers, but generic associations are not among the places where the "usual arithmetic conversions" apply, nor do the automatic conversions performed in the context of assignments and function calls apply in this context (these are explicitly called out in the language spec where they apply).
This fine-grained approach is intentional. The point is for generic associations to be able to distinguish between cases that are, among other things, assignment-compatible. For example,
#define hton(x) _Generic((x), uint32_t: htonl, uint16_t: htons)(x)
Am I doing something wrong here?
Your code does not conform to the language spec. You can fix it by defining a generic association for each relevant type, and / or by converting the type of the operand of the generic selection to one of the types for which an association is provided.
One of the ways to do that would be to alter the expression in your generic selection to one that engages the usual arithmetic conversions (as opposed to the generic selection itself doing so), as was described in comments on the question. But do note that the usual arithmetic conversions are narrower than the assignment conversions. Also, that might not be useful in your particular case, because the the integer promotions of uint32_t
and uint16_t
(as engaged as part of the usual arithmetic conversions) are not necessarily compatible. It is reasonably likely the the former is unsigned int
whereas the latter is int
.
Upvotes: 2