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

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

  • Передайте строковый литерал соответствующему типу. Это уродливо и (на мой взгляд) в первую очередь поражает точку _Generic.
  • Используйте char[14] как спецификатор типа. Ты должен шутить...

Мое предположение заключалось в том, что массивы будут распадаться на указатели при передаче на _Generic, но, очевидно, нет. Итак, как мне использовать _Generic со строковыми литералами? Это только два варианта?

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

Ответ 1

Вот решение:

#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.2svn).

Согласно этой публикации в блоге Дженса Гастеда, поведение GCC отличается (в GCC строки автоматически _Generic чтобы _Generic контексте _Generic, по-видимому).

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

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

Ответ 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, и это сработало.

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

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

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

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

Ответ 3

Поведение Clang было неправильным (отчет о дефекте C11 481) до 3.7.1. Это было исправлено в Clang 3.8.0, выпущенном 8 марта 2016 года.

В ответе Комитета на DR 481 говорится следующее:

Эта статья вызвала долгую и продуктивную дискуссию. Комитет согласен с автором предложения _Generic о том, что намерение состояло в том, что отбор на квалифицированных типах явно следует избегать, так же как выбор на массивах по размеру. Намерение _Generic состояло в том, чтобы дать C механизм для некоторого выражения понятия "перегруженной функции", найденного в C++, и, в частности, возможный механизм, который разработчики могут использовать для реализации универсальных функций атомарного типа из раздела 7.17.7.