ʍoɹɐɥsのブ口グ

ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็็็็็็็็็็็็็็็ ส็็็็็็็็็็็

C11 _Generic(Generic selection) の落とし穴

C11で追加されたGenericにはちょっとした落とし穴があります。Generic(というかC言語の基本型)が名前ではなく構造で同値性を判定すること(構造同値)を確認して次のコードを見てみましょう*1*2

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    const char *s = _Generic((uint64_t)0,
                             uint64_t: "uint64_t",
                             uintmax_t: "uintmax_t");
    printf("%s\n", s);
    return 0;
}

ぱっと見て出力はuint64_tになるだろ、と思った方ビンゴー! x84_64上のgcc 5.3.0、clang 3.7.1 では以下のようになります。

 $ gcc -std=c11 test.c
test.c: In function ‘main’:
test.c:11:30: error: ‘_Generic’ specifies two compatible types
                              uintmax_t: "uintmax_t");
                              ^
test.c:10:30: note: compatible type is here
                              uint64_t: "uint64_t",
                              ^
test.c:11:30: error: ‘_Generic> selector matches multiple associations
                              uintmax_t: "uintmax_t");
                              ^
test.c:10:30: note: other match is here
                              uint64_t: "uint64_t",
                              ^
$
$ clang -std=c11 test.c
test.c:11:30: error: type 'uintmax_t' (aka 'unsigned long long') in generic association compatible with previously
      specified type 'uint64_t' (aka 'unsigned long long')
                             uintmax_t: "uintmax_t");
                             ^~~~~~~~~
test.c:10:30: note: compatible type 'uint64_t' (aka 'unsigned long long') specified here
                             uint64_t: "uint64_t",
                             ^~~~~~~~
1 error generated.
$

はい、コンパイルすら通りません。コンパイラuintmax_tuint64_tの構造(大きさ)が同じだから、「同じ型が重複しているよ」と言ってるのです。

その他にも落とし穴があるようです。というか、落とし穴だらけな気がしてきました。

The controlling expression of _Generic | Jens Gustedt's Blog

※ おそらくclangの_Genericがおかしい。


関係ありませんが、gcc 5.2.0~5.3.0の_Genericには誤った警告を出すバグがあります。

Bug 68193 - _Generic -Woverflow false alarm

*1:もちろんここで言ってるのは型の同値性

*2:構造同値ではtypedef int A; typedef int B;でAとBが同じ型と判定する. 名前同値だと名前が違うから同じ型とはないと判定する.C言語は基本型に関しては構造同値をとっている. 派生型に関しては構造同値ではない、かといって名前同値ともスッキリ言い切れない….