C11 提供了 _Generic 選擇,用來模擬泛型程式,其本質是類似 switch 的選擇陳述,不過是編譯時期根據型態來選擇展開的對象。例如:
#define V_TYPE 0
#define WAT _Generic(V_TYPE, float: 2.0, \
char *: "XD", \
int: 10, \
default: 'a')
根據 V_TYPE 的型態,WAT 會展開為 2.0、"XD"、10 等,如果沒有符合的型態,就使用 default 的'a'。
其應用之一,就是用來模擬 C 語言本身不支援的函式重載(function overloading),例如,根據參數型態的不同,選擇真正對應的函式,
像是 math.h 中定義有 cbrt、cbrtl、cbrtf 等函式,可用來求得 double、long double、float 等引數的立方根,基本上可以如下使用:
#include <stdio.h>
#include <math.h>
int main(void) {
double x = 8.0;
const float y = 3.375;
printf("cbrt(8.0) = %f\n", cbrt(x));
printf("cbrtf(3.375) = %f\n", cbrtf(y));
return 0;
}
然而,如果想以同一個名稱來呼叫,可以定義 _Generic 選擇:
#include <stdio.h>
#include <math.h>
#define cbrt(X) _Generic((X), long double: cbrtl, \
float: cbrtf, \
default: cbrt \
)(X)
int main(void){
double x = 8.0;
const float y = 3.375;
printf("cbrt(8.0) = %f\n", cbrt(x));
printf("cbrtf(3.375) = %f\n", cbrt(y));
return 0;
}
以 cbrtf(3.375) 為例,x 是 float 型態,_Generic 透過第一個 (X) 展開後的 (3.375) 比對後選擇預設的 cbrtf,之後結合第二個 (X) 展開後的 (3.375) 成為 cbrtf(3.375)。
當然,只要選擇有依據,也可以是多個參數,例如:
#include <stdio.h>
#define foo(a, b) _Generic((a), int: foo1, \
default: foo2 \
)(a, b)
void foo1(int a, int b) {
printf("%d %d\n", a, b);
}
void foo2(double a, int b) {
printf("%f %d\n", a, b);
}
int main(void){
foo(1, 10);
foo(1.0, 10);
return 0;
}
在上面的範例中,選擇的依據是第一個參數的型態,在更複雜的範例中,可能要根據第二個參數的型態來選擇,這時可以如下:
#include <stdio.h>
#include <math.h>
#define foo(a, b) \
_Generic((a), \
int: foo1, \
double: _Generic((b), \
int : foo2, \
double: foo3 \
) \
)(a, b)
void foo1(int a, int b) {
printf("%d %d\n", a, b);
}
void foo2(double a, int b) {
printf("%f %d\n", a, b);
}
void foo3(double a, double b) {
printf("%f %f\n", a, b);
}
int main(void){
foo(1, 5);
foo(1.0, 10);
foo(1.0, 3.14);
return 0;
}
當然,還可以結合 ...、__VA_ARGS__ 等,撰寫更複雜的巨集,只不過很難撰寫與維護,該用在哪些場合,還是得在可讀性等方面評估一下。

