C Language
Общий выбор
Поиск…
Синтаксис
- _Generic (присваивание-выражение, общий-ассоциированный список)
параметры
параметр | подробности |
---|---|
родовое-ассоциативный список | generic-association ИЛИ generic-assoc-list, generic-association |
родовая-ассоциация | type-name: присваивание-выражение ИЛИ default: присваивание-выражение |
замечания
- Все квалификаторы типов будут
_Generic
во время оценки первичного выражения_Generic
. -
_Generic
первичное выражение оценивается на этапе 7 перевода . Таким образом, фазы, такие как конкатенация строк, были закончены до его оценки.
Проверьте, имеет ли переменная определенный квалифицированный тип
#include <stdio.h>
#define is_const_int(x) _Generic((&x), \
const int *: "a const int", \
int *: "a non-const int", \
default: "of other type")
int main(void)
{
const int i = 1;
int j = 1;
double k = 1.0;
printf("i is %s\n", is_const_int(i));
printf("j is %s\n", is_const_int(j));
printf("k is %s\n", is_const_int(k));
}
Выход:
i is a const int
j is a non-const int
k is of other type
Однако, если общий макрос типа реализуется следующим образом:
#define is_const_int(x) _Generic((x), \
const int: "a const int", \
int: "a non-const int", \
default: "of other type")
Выход:
i is a non-const int
j is a non-const int
k is of other type
Это связано с тем, что все квалификаторы типов отбрасываются для оценки управляющего выражения первичного выражения _Generic
.
Макрос типовой печати
#include <stdio.h>
void print_int(int x) { printf("int: %d\n", x); }
void print_dbl(double x) { printf("double: %g\n", x); }
void print_default() { puts("unknown argument"); }
#define print(X) _Generic((X), \
int: print_int, \
double: print_dbl, \
default: print_default)(X)
int main(void) {
print(42);
print(3.14);
print("hello, world");
}
Выход:
int: 42
double: 3.14
unknown argument
Обратите внимание, что если тип не является ни int
ни double
, будет создано предупреждение. Чтобы исключить предупреждение, вы можете добавить этот тип в макрос print(X)
.
Общий выбор, основанный на нескольких аргументах
Если требуется выбор по нескольким аргументам для типового выражения типа, и все типы, о которых идет речь, являются арифметическими типами, простой способ избежать вложенных выражений _Generic
заключается в использовании добавления параметров в управляющем выражении:
int max_int(int, int);
unsigned max_unsigned(unsigned, unsigned);
double max_double(double, double);
#define MAX(X, Y) _Generic((X)+(Y), \
int: max_int, \
unsigned: max_unsigned, \
default: max_double) \
((X), (Y))
Здесь управляющее выражение (X)+(Y)
проверяется только по типу и не оценивается. Для определения выбранного типа выполняются обычные преобразования для арифметических операндов.
Для более сложной ситуации выбор может быть сделан на основе нескольких аргументов оператору, путем их объединения.
В этом примере выбираются четыре функции, выполняемые извне, которые принимают комбинации из двух аргументов int и / или string и возвращают их сумму.
int AddIntInt(int a, int b);
int AddIntStr(int a, const char* b);
int AddStrInt(const char* a, int b );
int AddStrStr(const char* a, const char* b);
#define AddStr(y) \
_Generic((y), int: AddStrInt, \
char*: AddStrStr, \
const char*: AddStrStr )
#define AddInt(y) \
_Generic((y), int: AddIntInt, \
char*: AddIntStr, \
const char*: AddIntStr )
#define Add(x, y) \
_Generic((x) , int: AddInt(y) , \
char*: AddStr(y) , \
const char*: AddStr(y)) \
((x), (y))
int main( void )
{
int result = 0;
result = Add( 100 , 999 );
result = Add( 100 , "999" );
result = Add( "100" , 999 );
result = Add( "100" , "999" );
const int a = -123;
char b[] = "4321";
result = Add( a , b );
int c = 1;
const char d[] = "0";
result = Add( d , ++c );
}
Несмотря на то, что он выглядит так, как если бы аргумент y
оценивался более одного раза, это не 1 . Оба аргумента оцениваются только один раз, в конце макроса Add: ( x , y )
, как и в обычном вызове функции.
1 (Цитируется по: ISO: IEC 9899: 201X 6.5.1.1 Общий выбор 3)
Контролирующее выражение общего выбора не оценивается.