![プログラミング プログラミング](https://srad.jp/static/topics/programming_64.png)
Yoh2の日記: C11で疑似関数オーバーロード (できてません)
[2012-01-18 13:15追記: GCC拡張機能(clangでも使える)を利用して実現することができました。続・C11で疑似関数オーバーロード (GCC拡張機能使用)を参照して下さい。]
[C/C++規格: 11] C11の新機能 その2 -- 型ジェネリック式から話題を一部引っ越してきました。
C11で型ジェネリックが使えるようになったため、1引数の関数については擬似的に関数オーバーロードを実現するマクロを書けるようになった。
では引数の数が異なるものについてのオーバーロードはというと、_Genericのみで直接行うことはできない。でも、可変長引数マクロと組み合わせればできそうだと思ってコードを考えてみた。
ところが、どうにもうまくいかない。
なお、動作確認にはclang-3.0を使用した。
まず、最初に思い付いたのが以下のコード。
#include <stdio.h>
// 以下の4つの関数をfunc()マクロで呼び分けたい。
void func0(void)
{
puts("func0()");
}
void func1_i(int x)
{
printf("func1_i(%d)\n", x);
}
void func1_d(double x)
{
printf("func1_d(%f)\n", x);
}
void func2_ii(int x, int y)
{
printf("func2_ii(%d, %d)\n", x, y);
}
// 引数が少ない場合の穴埋め用ダミー。
struct dummy{int n;};
#define func(...) (func_selector(__VA_ARGS__, (struct dummy *)0, (struct dummy *)0)(__VA_ARGS__))
#define func_selector(arg1, arg2, ...) \
_Generic((arg1), \
struct dummy: func0, /* ひとつめがない → 0引数関数 */ \
int : _Generic((arg2), /* ひとつめの型がintで... */ \
struct dummy *: func1_i, /* ふたつめがない → 引数の型が(int) */\
int : func2_ii), /* ふたつめの型がint → 引数の型が(int, int) */ \
double : func1_d) /* ひとつめの型がdouble → 引数の型が(double) */
int main()
{
func(); // func_selectorを展開すると _Generic((), ……)となるのでエラー。
func(1);
func(1.0);
func(1, 2);
return 0;
}
この例では、引数の数が1個のものと2個のものではうまく動作するが、引数が0個のものは、「func()」→「func_selector(, (struct dummy *)0, (struct dummy *)0)」→「_Generic((), ……)」となり、「()」なんて式は書けないためコンパイルエラーになってしまう。
ではもう一段マクロを挟んで、0引数関数を特別扱いしたらいいんじゃないかと考えたのが次の例。
マクロ定義部以外は同じなので、マクロ定義部のみ記述。
// 0引数関数かそれ以外かの判別を行う
#define func_selector_1st(...) \
_Generic((int(*)[sizeof(#__VA_ARGS__)])0, /* #__VA_ARGS__ は引数なしなら空文字列 (sizeof(...) == 1) になる */\
int(*)[1] : func0(), /* 引数なし */\
default: func_selector_2nd(__VA_ARGS__, (struct dummy *)0)(__VA_ARGS__)) /* 引数あり */
// 1引数以上の関数の判別
#define func_selector_2nd(arg1, arg2, ...) \
_Generic((arg1), \
int : _Generic((arg2), /* ひとつめの型がintで... */ \
struct dummy *: func1_i, /* ふたつめがない → 引数の型が(int) */\
int : func2_ii), /* ふたつめの型がint → 引数の型が(int, int) */ \
double : func1_d) /* ひとつめの型がdouble → 引数の型が(double) */
func_selector_1st()で0引数とそれ以外を分けるように変更。func_selector_1st()の_Genericで選ばれなかった方は評価されないのでこれで解決、なんて思っていたが、「評価されない」と「翻訳の対象にならない」を混同していたというオチ。
結局、0引数の場合でもfunc_selector_2nd(……)が展開されてしまうため、「_Generic((), ……)」が出現するのは変わらずやっぱりコンパイルエラー。
さて、こうなるともっと発想を変えないといけなそうだけど、さてどうしたらいいものか。
C11で疑似関数オーバーロード (できてません) More ログイン