Check following contrived code:
#include <stdio.h>
void print_int(int *p) {
printf("%d\n", *p);
}
void print_double(double *p) {
printf("%f\n", *p);
}
#define PRINT(x) _Generic((x), \
int *: print_int, \
double *: print_double \
)(_Generic((x), int *: &(int){*x}, double *: &(double){*x}))
int main(void) {
int a = 3;
PRINT(&a);
return 0;
}
The intention is straightforward:
(1) The first generic selection returns the function pointer:
_Generic((x), \
int *: print_int, \
double *: print_double \
)
(2) The second generic selection returns the function parameter which is compound literal:
_Generic((x), int *: &(int){*x}, double *: &(double){*x})
I have two questions:
(1) The expanded PRINT(&a)
is:
print_int(&(int){*&a})
or
print_int(The address of returned compound literal from generic selection)
or others?
(2) If the expanded PRINT(&a)
is:
print_int(The address of returned compound literal from generic selection)
is returning compound literal from generic selection guaranteed safe or not?
(1) The expanded
PRINT(&a)
is:print_int(&(int){*&a})
or
print_int(The address of returned compound literal from generic selection)
or others?
Other. Your PRINT()
is a macro, but generic selection is not a macro. The macro-expanded PRINT(&a)
is therefore
_Generic((&a), int *: print_int, double *: print_double )(_Generic((&a), int *: &(int){*&a}, double *: &(double){*&a}))
(Note that macro expansion happens after line splicing.)
In context, this has the same observable behavior as simply print_int(&(int){a})
, but that should not be taken to mean that the two are equivalent.
is returning compound literal from generic selection guaranteed safe or not?
Compound literals are categorized as postfix expressions. They can appear anywhere that an expression of their type can appear. In particular, there is no inherent problem with compound literals appearing in a generic selection expression, including with being the selected expression or a sub-expression of it.
However, _Generic
being a C operator, not a preprocessor feature, all the subexpressions in a generic selection need to be valid according to C syntax and semantics, including even in the unselected generic associations. Both (double){*&a}
and (int){*&a}
in your example are ok for any controlling operand that is matched to one of the provided associations, but one or the other might elicit a warning from the compiler under some circumstances. And it wouldn't take much of a change to produce a variation where the resulting generic selection had an invalid subexpression in some cases, or even in every case.
When such a generic selection appears at block scope, the compound literal sub-expressions are lvalues. The objects they designate have automatic storage duration, and their lifetimes end at the termination of the execution of the innermost enclosing block. They are valid operands of the unary &
operator, and that lifetime is sufficient for the use to your example puts them.
Overall, then, your particular generic selection uses compound literals in a safe manner, but of course there can be no guarantee that an arbitrary generic selection involving compound literals is correct or safe.
For example, consider this:
_Generic(a,
int: print_int,
double: print_double
void *: print_pointer
)(
_Generic(a,
int: &(int){a},
double: &(double){a})
void *: &(void *){a})
)
It is syntactically correct, but semantically wrong, no matter what the type of a
is. If that type is not one compatible with int
, double
, or void *
then for that reason, or otherwise because at least one of (int){a}
, (double){a}
, and (void *){a}
is invalid.
There is an unbounded collection of examples that look much less plausible than this one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With