(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 件のコメント:
コメントを投稿