_Generic
简单介绍
C语言的_Generic
表达式是C11标准引入的一个特性,它提供了一种在编译时基于表达式类型选择函数或值的方法。这种机制与C++中的模板元编程相似,但更为简单和受限。
_Generic
表达式的基本形式如下
_Generic(expr, type1: value1, type2: value2, ..., default: default_value)
其中expr
是我们要判断的表达式,type1, type2, ...
是可能的类型,value1, value2, ...
是对应的类型为真时返回的值,default_value
是默认返回的值(当没有任何类型匹配时)。
_Generic
表达式的基本思想是,根据expr
的类型,从多个候选值中选择一个。这种选择是在编译时进行的,因此_Generic
表达式是一种编译时多态。
_Generic
表达式可以用于多种场景,比如根据不同的类型选择不同的处理函数,实现类型转换等。它的一个重要特点是,它仅适用于类型,而不是特定的值。因此,我们不能用它来做数值的比较或者复杂的类型检查。此外,__Generic
表达式在编译时求值,因此它不能用于运行时类型检查。
在使用_Generic
表达式时,我们需要注意的是,它的类型匹配是基于expr
的静态类型,而不是它的实际值。这意味着,如果expr
是一个指针,那么_Generic
表达式将根据指针的类型进行匹配,而不是指针指向的对象的类型。
此外,_Generic
表达式的类型匹配是按照它在表达式中的顺序进行的。如果多个类型匹配expr
,那么将选择第一个匹配的类型。因此,我们在编写_Generic
表达式时,应该将最具体的类型放在前面,将最一般的类型放在后面。
_Generic
表达式的一个常见用途是实现泛型宏。在C语言中,宏是类型无关的,这意味着我们不能编写一个宏,它可以接受任何类型的参数并正确地进行类型转换。但是,通过使用_Generic
表达式,我们可以编写一个宏,它在编译时根据参数的类型选择正确的表达式。
总的来说,_Generic
表达式为C语言提供了一种简单的、编译时的类型选择机制。尽管它的功能有限,但在某些情况下,它仍然能够提供便利和代码的清晰度。
代码实例
简单的类型选择(类型判断)
#include <stdio.h>
#include <stdbool.h>
void print_schar(signed char n);
void print_short(short n);
void print_int(int n);
void print_long(long n);
void print_longlong(long long n);
void print_Bool(_Bool n);
void print_uschar(unsigned char n);
void print_ushort(unsigned short n);
void print_uint(unsigned int n);
void print_ulong(unsigned long n);
void print_ulonglong(unsigned long long n);
void print_float(float n);
void print_double(double n);
void print_ldouble(long double n);
void print_char(char n);
void print_charX(char *n);
#define print(n) _Generic(n, \
signed char: print_schar, \
short: print_short, \
int: print_int, \
long: print_long, \
long long: print_longlong, \
_Bool: print_Bool, \
unsigned char: print_uschar, \
unsigned short: print_ushort, \
unsigned int: print_uint, \
unsigned long: print_ulong, \
unsigned long long: print_ulonglong, \
float: print_float, \
double: print_double, \
long double: print_ldouble, \
char: print_char, \
char *: print_charX)(n)
int main(void)
{
signed char a = 2;
short b = 20;
int c = 5;
long d = 10;
long long e = 12;
_Bool f = true;
unsigned char g = 4;
unsigned short h = 6;
unsigned int i = 8;
unsigned long j = 10;
unsigned long long k = 16;
float l = 2.5;
double m = 3.5;
long double n = 4.5;
char o = 'a';
char *p = "hello";
print(a);
print(b);
print(c);
print(d);
print(e);
print(f);
print(g);
print(h);
print(i);
print(j);
print(k);
print(l);
print(m);
print(n);
print(o);
print(p);
return 0;
}
void print_schar(signed char n) { printf("%d is signed char\n", n); }
void print_short(short n) { printf("%d is short\n", n); }
void print_int(int n) { printf("%d is int\n", n); }
void print_long(long n) { printf("%ld is long\n", n); }
void print_longlong(long long n) { printf("%lld is long long\n", n); }
void print_Bool(_Bool n) { printf("%d is _Bool\n", n); }
void print_uschar(unsigned char n) { printf("%d is unsigned char\n", n); }
void print_ushort(unsigned short n) { printf("%d is unsigned short\n", n); }
void print_uint(unsigned int n) { printf("%u is unsigned int\n", n); }
void print_ulong(unsigned long n) { printf("%lu is unsigned long\n", n); }
void print_ulonglong(unsigned long long n) { printf("%llu is unsigned long long\n", n); }
void print_float(float n) { printf("%f is float\n", n); }
void print_double(double n) { printf("%lf is double\n", n); }
void print_ldouble(long double n) { printf("%Lf is long double\n", n); }
void print_char(char n) { printf("%c is char\n", n); }
void print_charX(char *n) { printf("%s is string\n", n); }
程序输出
$ gcc generic01.c -o main
$ ./main
2 is signed char
20 is short
5 is int
10 is long
12 is long long
1 is _Bool
4 is unsigned char
6 is unsigned short
8 is unsigned int
10 is unsigned long
16 is unsigned long long
2.500000 is float
3.500000 is double
4.500000 is long double
a is char
hello is string
$
类型转换宏
#include <stdio.h>
#define TO_INT(x) _Generic((x), \
int: (x), \
float: (int)(x), \
default: (int)(x))
int main(void) {
int i = 42;
float f = 3.14f;
char c = 'A';
printf("ToInt of i: %d\n", TO_INT(i));
printf("ToInt of f: %d\n", TO_INT(f));
printf("ToInt of c: %d\n", TO_INT(c));
return 0;
}
程序输出
$ gcc generic02.c -o main
$ ./main
ToInt of i: 42
ToInt of f: 3
ToInt of c: 65
$
泛型函数
#include <stdio.h>
#define ADD(x, y) _Generic((x), \
int: _Generic((y), \
int: (x) + (y), \
default: (x) + (int)(y)), \
float: _Generic((y), \
float: (x) + (y), \
default: (x) + (float)(y)), \
default: _Generic((y), \
default: (int)(x) + (int)(y)) \
)
int main(void) {
int i = 42;
float f = 3.14f;
char c = 'A';
printf("ADD(i, i): %d\n", ADD(i, i));
printf("ADD(f, f): %f\n", ADD(f, f));
printf("ADD(i, f): %d\n", ADD(i, f));
printf("ADD(c, i): %d\n", ADD(c, i));
return 0;
}
程序输出
$ gcc generic03.c -o main
$ ./main
ADD(i, i): 84
ADD(f, f): 6.280000
ADD(i, f): 45
ADD(c, i): 107
$