Tipos de punteros incompatibles que pasan en _Generic macro

El siguiente código genera 2 advertencias que se describen en el título de la pregunta.

#include  static void _print_f(float *f){printf("float : %f\n", *f);} static void _print_i(int *i) {printf("int : %d\n", *i);} #define print(num) _Generic((num), \ int* : _print_i(num), \ float* : _print_f(num)) int main(void) { print((&(int){10})); print((&(float){10.f})); return 0; } 

SALIDA:

 int : 10 float : 10.000000 

Lo sé, esta macro podría escribirse como la siguiente:

 #define print(num) _Generic((num), \ int* : _print_i, \ float* : _print_f)(num) 

y en ese caso, no habrá advertencias, sin embargo, mi ejemplo es un fragmento ficticio que escribí para demostrar el problema. En mi base de código real, elegí la solución anterior, porque algunos otros argumentos “predeterminados” pero de tipo específico deben pasarse a la función seleccionada.

Entonces, la pregunta es: incluso si la macro funciona como debería y la salida es exactamente lo que espero, ¿por qué se generan las advertencias?


Banderas y entorno:

 /* Mac OS X 10.9.4 Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn) */ cc -Wall -v -g -std=c11 -fmacro-backtrace-limit=0 -I/usr/local/include -c -o build/tmp/main.o main.c 

Actualización1:

¡Olvidé pegar el rastreo completo! Aquí está el primero:

 main.c:39:11: warning: incompatible pointer types passing 'int *' to parameter of type 'float *' [-Wincompatible-pointer-types] print((&(int){10})); ^~~~~~~~~~~~ main.c:31:23: note: expanded from macro 'print' float* : _print_f(num)) ^ main.c:26:29: note: passing argument to parameter 'f' here static void _print_f(float *f){printf("float : %f\n", *f);} ^ 

Y aquí está el segundo:

 main.c:40:11: warning: incompatible pointer types passing 'float *' to parameter of type 'int *' [-Wincompatible-pointer-types] print((&(float){10.f})); ^~~~~~~~~~~~~~~~ main.c:30:23: note: expanded from macro 'print' int* : _print_i(num), \ ^ main.c:27:27: note: passing argument to parameter 'i' here static void _print_i(int *i) {printf("int : %d\n", *i);} ^ 

Actualización2:

Hasta que los desarrolladores de clang solucionen este error, aquí hay una fea solución para silenciar las advertencias, que funcionará si todas las claves en la lista de asociados son tipos, O todas son punteros a tipos; y fallará si los tipos Y punteros a los tipos están en las teclas también:

 /* HACK: re-casting pointers to mute warnings */ #define print(num) _Generic((num), \ int* : _print_i((int*)num), \ float* : _print_f((float*)num)) 

Esto no es un error en clang, pero desafortunadamente lo que requiere el estándar C11. Todas las twigs de una expresión primaria _Generic deben ser expresiones válidas y, por lo tanto, válidas en cualquier circunstancia. El hecho de que solo una de las twigs alguna vez será evaluada, no está relacionada con esto.

Su versión alternativa es lo que C11 prevé para situaciones como esta: elegir la función (y no la llamada evaluada) como resultado de la expresión genérica de tipo, y aplicar esa función a los argumentos.

CORRECCIÓN: Esto no es (por lo que puedo decir) un error en clang, pero una interpretación correcta de cómo se supone que _Generic se comporta. Solo se evalúa una de las asociaciones genéricas en una expresión de selección genérica , pero todas deben ser expresiones válidas. ( _Generic no actúa como una macro.)

Ver la respuesta de Jens Gustedt .


Esto definitivamente parece un error en clang. Estoy razonablemente seguro de que tu código es válido C11. Veo lo mismo con la versión 3.4 en Linux Mint.

He creado una demostración ligeramente simplificada :

 #include  static void print_i(int i) { puts("int"); } static void print_ip(int *i) { puts("int*"); } #define print(num) _Generic((num), \ int : print_i(num), \ int* : print_ip(num)) int main(void) { int i = 10; print(i); print(&i); } 

La salida es correcta, pero recibo las siguientes advertencias:

 cc:12:11: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'int *'; take the address with & [-Wint-conversion] print(i); ^ & cc:8:23: note: expanded from macro 'print' int* : print_ip(num)) ^ cc:4:27: note: passing argument to parameter 'i' here static void print_ip(int *i) { puts("int*"); } ^ cc:13:11: warning: incompatible pointer to integer conversion passing 'int *' to parameter of type 'int'; remove & [-Wint-conversion] print(&i); ^~ cc:7:22: note: expanded from macro 'print' int : print_i(num), \ ^ cc:3:25: note: passing argument to parameter 'i' here static void print_i(int i) { puts("int"); } ^ 2 warnings generated.