今日は興味深い知識を見つけましたので、メモしておきます。皆さんご存知の通り、C 言語にはジェネリックがありませんが、C11 ではジェネリック選択式 (_ Generic) という新しい式が追加されました。これは何のためにあるのでしょうか?その目的は、式の型に基づいて値を選択することです。具体的な構文を見てみましょう。
ジェネリック選択式の導入#
まず、以下のコードを見てみましょう:
int main()
{
int x = 1;
double y = 2.0;
char z = 'c';
printf("%d\n", _Generic(x, int:0, double : 1, default:3));
printf("%d\n", _Generic(y, int:0, double : 1, default:3));
printf("%d\n",_Generic(z, int:0, double : 1, default:3));
return 0;
}
_Generic
は C11 のキーワードであり、丸括弧の中にはコンマで区切られた複数の項目が含まれます。最初の項目は式であり、後の各項目は型、コロン、値で構成されます(例:double: 1)。最初の項目の型に一致するタグに対して、式全体の値はその後ろの値になります。
実行結果は以下の通りです:
渡された変数の型に応じて、出力結果が異なることがわかります。最初の printf の最初の項目 x は int 型なので、式全体の結果は 0 になります。2 番目の printf の最初の項目は double 型なので、式の結果は 1 になります。3 番目の printf の結果は 3 ですが、char 型に一致するものがないため、デフォルトの default が選択されたためです。
実際には、これは switch 文に似ていますが、Generic は式の型に基づいてタグをマッチさせ、switch は式の値に基づいてタグをマッチさせます。
マクロ定義との組み合わせ#
上記の使用方法は少し面倒ですが、マクロ定義と組み合わせることで非常に便利になります。
例を見てみましょう:
#define MYTYPE(X) _Generic((X),int:"int", double:"double", default:"other")
int main()
{
int d = 2;
printf("%s\n", MYTYPE(d));
printf("%s\n", MYTYPE(1.0*d));
printf("%s\n", MYTYPE("string"));
return 0;
}
実行結果は以下の通りです:
マクロ定義と組み合わせると、使いやすくなります。C++ のジェネリックプログラミングのような感じがしますが、まだ少し足りません。
上級プレイ#
_Generic タグに対応する値は、整数、文字列、関数ポインタのいずれかであることができます。
以下のコードを見てみましょう。
void PrintInt(int x)
{
printf("%d\n", x);
}
void PrintDouble(double x)
{
printf("%lf\n", x);
}
void PrintSting(char* x)
{
printf("%s\n", x);
}
void PrintOther(void x)
{
print("タイプが問題です\n");
}
#define PRINT(X) _Generic((X),\
int:PrintInt,\
double:PrintDouble,\
const char*:PrintSting,\
default:PrintOther)(X)
int main()
{
int x = 1;
int y = 2.0;
const char* str = "hello _Generic";
PRINT(x);
PRINT(y);
PRINT(str);
return 0;
}
実行結果は以下の通りです:
これで C++ のジェネリックプログラミングに似ていますね。面白いですが、やはり C++ のジェネリックの方が使いやすいです。