(clang では結構前から使えたみたいですね)
_Generic とは?
_Generic(制御式, 型名1: 式1, 型名2: 式2…)
これはコンパイル時に決定されます。
例えば、以下のように使用できます。
#define abs(x) _Generic((x), long: labs, double: fabs, default: abs)(x)
_Generic 自体については他のサイトも参考にしてください。
やろうとしてできなかったこと
#define printf_dec_format(x) _Generic((x), \ char: "%c", \ signed char: "%hhd", \ unsigned char: "%hhu", \ signed short: "%hd", \ unsigned short: "%hu", \ signed int: "%d", \ unsigned int: "%u", \ long int: "%ld", \ unsigned long int: "%lu", \ long long int: "%lld", \ unsigned long long int: "%llu", \ float: "%f", \ double: "%f", \ long double: "%Lf", \ char *: "%s", \ void *: "%p")
#define Message(a, b) \ printf("a = " printf_dec_format(a) "\nb = " \ printf_dec_format(b) "\n", a, b)
文字列リテラルは以下のように途切れていても1つの文字列リテラルとして扱われるので、それを期待したのですがダメでした。
printf( "aaaa" "bbbb" "\n");
メンドクサイですが、このように書く必要があるようです。
#define PRINTF_FORMAT_PARAM(prefix, x, postfix) _Generic((x) \ , char : prefix "%c" postfix \ , signed char: prefix "%hhd" postfix \ , unsigned char: prefix "%hhu" postfix \ , signed short: prefix "%hd" postfix \ , unsigned short: prefix "%hu" postfix \ , signed int: prefix "%d" postfix \ , unsigned int: prefix "%u" postfix \ , signed long: prefix "%ld" postfix \ , unsigned long: prefix "%lu" postfix \ , char* : prefix "%s" postfix \ , void* : prefix "%p" postfix \ , default: prefix "%p" postfix \ ) #define PRINTF_FORMAT_PARAM2(str0, x, str1, y, str2) _Generic((x) \ , char : PRINTF_FORMAT_PARAM(y, str0 "%c" str1, str2) \ , signed char: PRINTF_FORMAT_PARAM(y, str0 "%hhd" str1, str2) \ , unsigned char: PRINTF_FORMAT_PARAM(y, str0 "%hhu" str1, str2) \ , signed short: PRINTF_FORMAT_PARAM(y, str0 "%hd" str1, str2) \ , unsigned short: PRINTF_FORMAT_PARAM(y, str0 "%hu" str1, str2) \ , signed int: PRINTF_FORMAT_PARAM(y, str0 "%d" str1, str2) \ , unsigned int: PRINTF_FORMAT_PARAM(y, str0 "%u" str1, str2) \ , signed long: PRINTF_FORMAT_PARAM(y, str0 "%ld" str1, str2) \ , unsigned long: PRINTF_FORMAT_PARAM(y, str0 "%lu" str1, str2) \ , char* : PRINTF_FORMAT_PARAM(y, str0 "%s" str1, str2) \ , void* : PRINTF_FORMAT_PARAM(y, str0 "%p" str1, str2) \ , default: PRINTF_FORMAT_PARAM(y, str0 "%p" str1, str2) \ ) #define Message(a, b) \ printf(PRINTF_FORMAT_PARAM2("a = ", a , "\nb = ", b, "\n"), a, b)
指定した名前が型であるかどうか調べるマクロ
指定した名前が型であるかどうか調べるマクロできた http://t.co/i5nxQxRO
— でちまるさん(実際かわいい) (@decimalbloat) 2012, 9月 28
というわけで、C11 でも書いてみた。
C11 で IS_TYPE_NAME [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ http://t.co/KDGC3YpbQh
— ずみっくす (@srz_zumix) 2014, 6月 7
#include <stdio.h> #define IS_TYPE_NAME(name) _Generic((void (*)(int (name)))0, void (*)(int):0, default: 1) typedef float f; int main() { printf("%d\n", IS_TYPE_NAME(int)); printf("%d\n", IS_TYPE_NAME(f)); printf("%d\n", IS_TYPE_NAME(main)); printf("%d\n", IS_TYPE_NAME(aaa)); return 0; }
1 1 0 0
なにかに使えそうな気がする。
iutest_c での利用
はじめに iutest_c を紹介しておきます。iutest_c は「C言語のテスティングフレームワーク」です。C++テスティングフレームワークの iutest もありますが、こちらはC言語で書かれています。C++ のと比べると非常に貧弱なフレームワークですが、軽量であることは間違いありません。(小さなデバイスなどに載せることを想定していますが、あまり検証もできていませんし、そこに載せることを考えると軽量とは言い難いかも)
printer の改善
貧弱な点の一つとして、printer がビミョーでした。アサーションに失敗した場合、期待値と実値を出力します。この時の出力が値ではなくメモリダンプを出力します。
IUTEST(Test, Sample) { int zero=0; IUTEST_EXPECT_NE(zero, zero); }
sample.c(16):error: Expected of :
zero != zero
Actual: 0x00000000 vs 0x00000000
zero != zero
Actual: 0x00000000 vs 0x00000000
ビミョー過ぎて説明するのもアレなんですが、一番ダメなところとして lvalue しか扱えないところです。
IUTEST_EXPECT_NE(0, 0); と書けません。
そこで _Generic!!
前述した printf_dec_format のように、型ごとに出力をしてあげることで lvalue 制限を取り払い、わかりやすい出力を実現しました。
IUTEST(Test, Sample) { IUTEST_EXPECT_NE(0, 0); float fa = 2.0f/2; IUTEST_EXPECT_FLOAT_EQ(1.1f, fa); }
sample.c:15:error: Expected of :
zero != zero
Actual: 0 vs 0
sample.c:17:error: Value of : fa
Actual: 1.000000
Expected: 1.1f
Which is: 1.100000
zero != zero
Actual: 0 vs 0
sample.c:17:error: Value of : fa
Actual: 1.000000
Expected: 1.1f
Which is: 1.100000
ネタ切れ
このブログを書きつつ、_Generic の利用方法を考えていたのですが、ネタ切れです。_Generic 使ってもっと何かできるんじゃないかと思っていたのですが、なかなかうまくいかず。。。
例えば、decltype とか書けるんじゃないか?と思ったんですが、式しか扱えないのでダメでした。
関数オーバーロードの代用として使えるので、それで…と思ったんですが使いドコロがなかった。
IS_TYPE_NAME 使って何かできそうな気はするんですがねぇ…
まとめ
_Generic 便利だと思いますが、どの位需要があるんでしょうね?C11 使うことができるんなら、C++ 使えばいいんじゃない?と思ってしまいます。
とはいえ、_Generic は便利です。iutest_c でも今後も使っていこうと思います。
0 件のコメント:
コメントを投稿