C11 Generic: как работать со строковыми литералами?


Используя функцию _Generic в C11, как вы справляетесь со строковыми литералами?

Например:

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

int main()
{
    foo("Hello, world!");
    return 0;
}

Выдает эту ошибку на clang:

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

Замена char * на char[] дает мне

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

Единственные способы (насколько мне известно) получить это для компиляции:

  1. приведите строковый литерал к соответствующему типу. Это некрасиво и (на мой взгляд) поражает точку зрения _Generic в первую очередь.
  2. используйте char[14] в качестве спецификатора типа. У тебя есть получил , чтобы шутить надо мной...
Мое предположение состояло в том, что массивы будут распадаться на указатели при передаче в _Generic, но, очевидно, нет. Итак, как я использую _Generic со строковыми литералами? Это единственные два варианта?

Я использую clang 3.2 в Debian. К сожалению, это единственный компилятор, к которому у меня есть доступ, который поддерживает эту функцию, поэтому я не могу сказать, является ли это ошибкой компилятора или нет.

2 24

2 ответа:

Вот решение:

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

int main()
{
    foo("Hello, world!");
    return 0;
}

Это компилирует и производит:

$ clang t.c && ./a.out 
Hello, world!

Это несколько хромает, но я не нашел лучшего способа заставить x распадаться на указатель на char или соответствовать его типу в нечеткой форме, которую вы требуете, с Apple LLVM версии 4.2 (clang-425.0.28) (на основе LLVM 3.2 svn).

Согласно этой записи в блоге Йенса Густеда , поведение GCC отличается (в GCC строки автоматически распадаются на указатель в контексте _Generic , по-видимому).

Кстати, в языке Си тип строкового литерала-массив char, а не const char. Отклонение char [] как type-name в generic-association не является ошибкой компилятора:

Универсальная выборка должна иметь не более одной стандартной универсальной ассоциации. Имя типа в универсальной ассоциации должно указывать полный тип объекта , отличный от изменяемого типа. (6.5.1.1:2 с моим акцентом)

Я нашел способ избежать использования хитрого трюка (0,x).

Если вы используете строковый литерал , то тип char[s], где s - размер строкового литерала.

Как вы получаете такой размер?, использовать оператор 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;
}
Я попытался скомпилировать его с помощью clang и Pelles, и это сработало.

Единственная проблема, которую вы все еще должны разыгрывать массивы переменной длины.

Попробовав еще немного, я нашел другой аналоговый способ делать то, что Паскаль Куок сделал, использовал &* операторы:

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

int main()
{
    foo("Hello, world!");
    return 0;
}