C 11標準の汎用機構
3333 ワード
Apple LLVM4.0はすでにC 11規格における重要な特性である汎用機構をサポートしている.C 11の汎用メカニズムはC++よりも粗末に見えるが,ライブラリを作る際には非常に役に立つ.
次に、C 11規格における汎用表現を見てみましょう.
C 11における汎用メカニズムはキーワード_Genericは、次の構文形式で引き出します.
_Generic ( assignment-expression , generic-assoc-list )
generic-assoc-list:
generic-association
generic-assoc-list , generic-association
generic-association: type-name : assignment-expression
default : assignment-expression
Cコードの例を次に示します.
こちらで気をつけたいのは、Genericのassignment-expressionは、実行時の計算ではなくタイプのみを取得します.すなわち、コンパイラは、コンパイル時に式のタイプを取得するだけで、他の命令は生成されません.これはsizeof()とtypeof()とC++のtypeid()とdecltype()と同じです.
また、generic-association-listにはassignment-expressionタイプと同じgeneric-associationが必要です.そうしないと、コンパイルがエラーになります.もちろんgeneric-association-listにdefault処理が含まれていればコンパイルはスムーズに行えます.次のコードに示します.
ここで注意しなければならないのは、Generic式では、タイプマッチングを満たさない式文もコンパイラによってコンパイルされ、その有効性が確認されます.したがって,タイプマッチングを満たさない式についてはsizeof(),typeof()のようにコンパイルのみを行い,計算は行わない.たとえば、次の文はすべてエラーです.
最後の行のコードでは、char:このタイプが一致していなくても、コンパイラはエラーを報告します.
次に、C 11規格における汎用表現を見てみましょう.
C 11における汎用メカニズムはキーワード_Genericは、次の構文形式で引き出します.
_Generic ( assignment-expression , generic-assoc-list )
generic-assoc-list:
generic-association
generic-assoc-list , generic-association
generic-association: type-name : assignment-expression
default : assignment-expression
Cコードの例を次に示します.
#define GENERAL_ABS(x) _Generic((x), int:abs, float:fabsf, double:fabs)(x)
static void GenericTest(void)
{
printf("int abs: %d
", GENERAL_ABS(-12));
printf("float abs: %f
", GENERAL_ABS(-12.04f));
printf("double abs: %f
", GENERAL_ABS(-13.09876));
int a = 10;
int b = 0, c = 0;
_Generic(a + 0.1f, int:b, float:c, default:b)++;
printf("b = %d, c = %d
", b, c);
_Generic(a += 1.1f, int:b, float:c, default:b)++;
printf("a = %d, b = %d, c = %d
", a, b, c);
}
こちらで気をつけたいのは、Genericのassignment-expressionは、実行時の計算ではなくタイプのみを取得します.すなわち、コンパイラは、コンパイル時に式のタイプを取得するだけで、他の命令は生成されません.これはsizeof()とtypeof()とC++のtypeid()とdecltype()と同じです.
また、generic-association-listにはassignment-expressionタイプと同じgeneric-associationが必要です.そうしないと、コンパイルがエラーになります.もちろんgeneric-association-listにdefault処理が含まれていればコンパイルはスムーズに行えます.次のコードに示します.
struct MyStruct { int a, b; } s;
_Generic("Hello", const char*:puts("OK!")); // ERROR! "Hello" char[6]
_Generic("Hello", char[6]:puts("OK!")); // OK
_Generic((const char*)"Hello", const char*:puts("OK!")); // OK
_Generic(s, int:puts("OK!")); // ERROR
_Generic(s, struct MyStruct:puts("OK!")); // OK
_Generic(s, int:puts("Yep!"), default:puts("Others")); // OK
ここで注意しなければならないのは、Generic式では、タイプマッチングを満たさない式文もコンパイラによってコンパイルされ、その有効性が確認されます.したがって,タイプマッチングを満たさない式についてはsizeof(),typeof()のようにコンパイルのみを行い,計算は行わない.たとえば、次の文はすべてエラーです.
const int g = 50;
typeof(g = 10) t = 200; // ERROR
int f = sizeof(g = 100); // ERROR
f = _Generic(g, int:sizeof(g), char:(g = 100, g), default:g + 10); // ERROR
最後の行のコードでは、char:このタイプが一致していなくても、コンパイラはエラーを報告します.