I have this code:
#include <stdio.h>
#include <wchar.h>
/* If either of the if-true or if-false expressions of ?: is void *, then the
* of the result shall also be void * (plus const if either is const). */
#define TO_VOID_PTR(P) (1 ? (P) : (void *)(P))
#define PTR_FMT(P) \
_Generic(TO_VOID_PTR(P), \
void const *: "%p", \
void * : "%p" \
)
/* Get the corresponding format specifier of a type. */
#define PRINTF_FMT(T) \
_Generic((T), \
_Bool : "%d", \
char : "%c", \
signed char : "%hhd", \
unsigned char : "%hhu", \
short int : "%hd", \
int : "%d", \
long int : "%ld", \
long long int : "%lld", \
unsigned short int : "%hu", \
unsigned int : "%u", \
unsigned long int : "%lu", \
unsigned long long int : "%llu", \
float : "%f", \
double : "%f", \
long double : "%Lf", \
char * : "%s", \
wchar_t * : "%ls", \
default : PTR_FMT(T) \
)
#define print(x) printf(PRINTF_FMT(x), x)
/* A separate call to putchar() is required because _Generic is not a macro, but
* a primary expression. As such it is evaluated at a later translation phase (7)
* than string concatenation (phase 6). */
#define println(x) printf(PRINTF_FMT(x), x), putchar('\n')
int main(void)
{
println("hello");
println(10);
println(5u);
println('A');
}
Compiling with GCC 14.1 with the following flags -std=c23 -O3 -Werror -Wall -Wextra
resulted in these error messages:
<source>:48:5: error: pointer/integer type mismatch in conditional expression ('int' and 'void *') [-Werror,-Wconditional-type-mismatch]
48 | println(10);
| ^~~~~~~~~~~
<source>:43:28: note: expanded from macro 'println'
43 | #define println(x) printf(PRINTF_FMT(x), x), putchar('\n')
| ^~~~~~~~~~~~~
<source>:34:43: note: expanded from macro 'PRINTF_FMT'
34 | default : PTR_FMT(T) \
| ^~~~~~~~~~
<source>:9:14: note: expanded from macro 'PTR_FMT'
9 | _Generic(TO_VOID_PTR(P), \
| ^~~~~~~~~~~~~~
<source>:6:28: note: expanded from macro 'TO_VOID_PTR'
6 | #define TO_VOID_PTR(P) (1 ? (P) : (void *)(P))
| ^ ~~~ ~~~~~~~~~~~
<source>:49:5: error: pointer/integer type mismatch in conditional expression ('unsigned int' and 'void *') [-Werror,-Wconditional-type-mismatch]
49 | println(5u);
| ^~~~~~~~~~~
<source>:43:28: note: expanded from macro 'println'
43 | #define println(x) printf(PRINTF_FMT(x), x), putchar('\n')
| ^~~~~~~~~~~~~
<source>:34:43: note: expanded from macro 'PRINTF_FMT'
34 | default : PTR_FMT(T) \
| ^~~~~~~~~~
<source>:9:14: note: expanded from macro 'PTR_FMT'
9 | _Generic(TO_VOID_PTR(P), \
| ^~~~~~~~~~~~~~
<source>:6:28: note: expanded from macro 'TO_VOID_PTR'
6 | #define TO_VOID_PTR(P) (1 ? (P) : (void *)(P))
| ^ ~~~ ~~~~~~~~~~~
<source>:50:5: error: pointer/integer type mismatch in conditional expression ('int' and 'void *') [-Werror,-Wconditional-type-mismatch]
50 | println('A');
| ^~~~~~~~~~~~
<source>:43:28: note: expanded from macro 'println'
43 | #define println(x) printf(PRINTF_FMT(x), x), putchar('\n')
| ^~~~~~~~~~~~~
<source>:34:43: note: expanded from macro 'PRINTF_FMT'
34 | default : PTR_FMT(T) \
| ^~~~~~~~~~
<source>:9:14: note: expanded from macro 'PTR_FMT'
9 | _Generic(TO_VOID_PTR(P), \
| ^~~~~~~~~~~~~~
<source>:6:28: note: expanded from macro 'TO_VOID_PTR'
6 | #define TO_VOID_PTR(P) (1 ? (P) : (void *)(P))
| ^ ~~~ ~~~~~~~~~~~
Why is _Generic
choosing the default
case for 10
, 5u
, and 'A'
when separate cases for int
and unsigned int
are present?
The _Generic
expression isn't choosing the default
case. The issue is that all cases must be a valid expression.
This means that the conditional:
(1 ? (P) : (void *)(P))
Which for the second case is being evaluated as:
(1 ? (10) : (void *)(10))
Results in a conversion between an integer type and a pointer type between the second and third parts of the conditional, which is a constraint violation as per section 6.5.15p3 of the C standard regarding the conditional operator.
You can accomplish what you want by getting rid of the conditional and just casting to const void *
, since you don't intent to change what the result points to.
#define TO_VOID_PTR(P) ((const void *)(P))