El siguiente código genera 2 advertencias que se describen en el título de la pregunta.
#include <stdio.h>
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;
}
PRODUCCIÓN:
int : 10
float : 10.000000
Lo sé, esta macro podría escribirse de la siguiente manera:
#define print(num) _Generic((num), \
int* : _print_i, \
float* : _print_f)(num)
y en ese caso, no habrá ninguna advertencia, 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 específicos del tipo deben pasarse a la función seleccionada.
Entonces la pregunta es: Incluso si la macro funciona como debería y el resultado es exactamente lo que espero, ¿por qué se generan las advertencias?
Banderas y Medio Ambiente:
/* 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
solucione este error, aquí hay una solución fea para silenciar las advertencias, que funcionará si todas las claves en la lista de asociación son tipos, O todas son punteros a tipos; y fallará si los tipos Y los punteros a los tipos también están en las claves:
/* HACK: re-casting pointers to mute warnings */
#define print(num) _Generic((num), \
int* : _print_i((int*)num), \
float* : _print_f((float*)num))
_Generic
merecería completamente su propia etiqueta, pero aparentemente StackOverflow no me deja crearlo.– Pascal Cuoq
14 de julio de 2014 a las 18:48
@PascalCuoq lo hice, mira => Flags & Envs
– Pedro Varo
14/07/2014 a las 18:50
Parece un falso positivo. Hay un comentario en alguna parte del sitio web de Clang (en la parte inferior de clang-analyzer.llvm.org ) en el sentido de que tratan la emisión de falsos positivos como errores, por lo que vale la pena informar.
– Pascal Cuoq
14/07/2014 a las 18:58
Su solución alternativa funciona en este caso, pero no en los casos en que el lanzamiento en sí activa una advertencia; mira el ejemplo que publiqué.
–Keith Thompson
14/07/2014 a las 20:13
@KeithThompson interesante. Mi solución funciona si todas las claves en la lista de asociaciones son punteros a tipos o si todas son tipos básicos. Cambié tu ejemplo a
int
yfloat
yprint_i((int)i)
yprint_f((float)f)
y también está funcionando para eso… Mi truco solo fallará si las claves de la lista asociada son una combinación de tipos simples y punteros a tipos.– Pedro Varo
14/07/2014 a las 20:23