c - literal - template strings html
C11_Generic: ¿cómo lidiar con los literales de cadena? (2)
Usando la característica _Generic
en C11, ¿cómo _Generic
con los literales de cadena?
Por ejemplo:
#include <stdio.h>
#define foo(x) _Generic((x), char *: puts(x))
int main()
{
foo("Hello, world!");
return 0;
}
da este error en clang:
controlling expression type ''char [14]'' not compatible with any generic association type
Reemplazando char *
con char[]
me da
error: type ''char []'' in generic association incomplete
Las únicas formas (que yo sepa) de conseguir que esto se compile son:
- Convierte el literal de cadena a un tipo apropiado. Esto es feo y (en mi opinión) derrota el punto de
_Generic
en primer lugar. - Utilice
char[14]
como el especificador de tipo. Tienes que estar bromeando ...
Mi suposición era que las matrices _Generic
a los punteros cuando se pasaban a _Generic
, pero evidentemente no. Entonces, ¿cómo uso _Generic
con literales de cadena? ¿Son esas las únicas dos opciones?
Estoy usando Clang 3.2 en Debian. Desafortunadamente, es el único compilador al que tengo acceso que admite esta función, por lo que no puedo decir si es un error del compilador o no.
Aquí hay una solución:
#include <stdio.h>
#define foo(x) _Generic((0,x), char*: puts(x))
int main()
{
foo("Hello, world!");
return 0;
}
Esto compila y produce:
$ clang t.c && ./a.out
Hello, world!
Es un tanto cojo, pero no encontré una mejor manera de hacer que x
decaiga a un puntero a char ni a que coincida con su tipo de la manera difusa que necesita, con Apple LLVM versión 4.2 (clang-425.0.28) (basado en LLVM 3.2svn).
De acuerdo con esta publicación del blog de Jens Gusted , el comportamiento de GCC es diferente (en GCC, las cadenas decaen automáticamente al puntero en un contexto _Generic
, aparentemente).
Por cierto, en C, el tipo de un literal de cadena es una matriz de char
, no de const char
. Rechazar char []
como nombre de tipo en una asociación genérica no es un error del compilador:
Una selección genérica no tendrá más de una asociación genérica por defecto. El nombre de tipo en una asociación genérica especificará un tipo de objeto completo distinto de un tipo modificado de forma variable. (6.5.1.1:2 con mi énfasis)
He descubierto una manera de evitar el uso del truco inteligente (0,x)
.
Si usa una cadena literal, el tipo es char[s]
, donde s
es el tamaño de la cadena literal.
¿Cómo se obtiene ese tamaño ?, use el operador sizeof
:
#include <stdio.h>
#define Test( x ) _Generic( ( x ) , char*: puts , /
const char*: puts , /
const char[sizeof( x )]: puts , /
char[sizeof( x )]: puts )( x )
int main(void)
{
char str[] = "This" ;
Test( str ) ;
Test( "works" ) ;
char str2[10] = "!!!" ;
Test( str2 ) ;
return 0;
}
Intenté compilarlo con clang y pelles y funcionó.
El único problema que aún tienes que lanzar arrays de longitud variable.
Después de probar un poco más, encontré otra forma analógica de hacer lo que hizo Pascal Cuoq , usar los operadores &*
:
#include <stdio.h>
#define foo(x) _Generic( ( &*(x) ), char*: puts , const char*: puts )( x )
int main()
{
foo("Hello, world!");
return 0;
}