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.