C11 Genérico: ¿cómo tratar con literales de cadena?


Usando la función _Generic en C11, ¿cómo se manejan 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 al clang:

controlling expression type 'char [14]' not compatible with any generic association type

Reemplazar char * por char[] me da

error: type 'char []' in generic association incomplete

Las únicas formas (que yo sepa) de obtener esto para compilar son:

  1. Envía el literal de cadena a un tipo apropiado. Esto es feo y (en mi opinión) derrota el punto de _Generic en primer lugar.
  2. Use char[14] como especificador de tipo. Tienes tiene que estar bromeando...

Mi suposición era que los arrays decaerían en punteros cuando se pasaran a _Generic, pero evidentemente no. Entonces, ¿cómo uso _Generic con literales de cadena? ¿Son esas las dos únicas opciones?

Estoy usando clang 3.2 en Debian. Desafortunadamente, es el único compilador al que tengo acceso que soporta esta característica, así que no puedo decir si es un error del compilador o no.

 24
Author: Michael Rawson, 2013-09-17

2 answers

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 poco cojo, pero no encontré ninguna mejor manera de hacer que x decaiga a un puntero a char ni para que coincida con su tipo en la forma difusa que se requiere, con Apple LLVM versión 4.2 (clang-425.0.28) (basado en LLVM 3.2 svn).

De acuerdo con esta entrada de blog de Jens Gusted , el comportamiento de GCC es diferente (en GCC, las cadenas se decaen automáticamente a puntero en un contexto _Generic, aparentemente).

Por cierto, en C, el tipo de un literal de cadena es array 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 predeterminada. El nombre del tipo en una asociación genérica especificará un tipo de objeto complete distinto de un tipo modificado variablemente. (6.5.1.1: 2 con mi énfasis)

 19
Author: Pascal Cuoq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-12 08:45:10

He descubierto una manera de evitar usar el truco inteligente (0,x).

Si usa un literal de cadena el tipo es char[s] , donde s es el tamaño del literal de cadena.

¿Cómo se obtiene ese tamaño?, use sizeof operador:

#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 todavía tiene que lanzar matrices de longitud variable.

Después de probar un poco más encontré otra forma analógica de hacer lo que Pascal Cuoq hizo, uso &* operadores:

#include <stdio.h>
#define foo(x) _Generic( ( &*(x) ), char*: puts , const char*: puts )( x )

int main()
{
    foo("Hello, world!");
    return 0;
}
 12
Author: 2501,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:29