2010-03-28

attributeは難しい

post-Pittsburgh mailingの公開まで、いよいよ日が迫ってきた。FCDが公開されたならば、直ちに変更点を把握して、参考書の執筆を始めなければならない。

ところで、実は、いまもって、よく理解出来ないことがひとつある。attributeだ。

attributeがよく分からない。というのも、あまりにも、記述出来る場所が多すぎるので、一体、どこに書いていいのか、迷うのだ。

たとえば、ある変数に対して、アラインメントを指定したいとしよう。

// 16バイトにアラインメントされてほしい。
char buffer[64] ;

目的は様々だが、とにかく、この変数が、16バイトのアラインメントされていてほしい。では、既存のコンパイラは、どのような拡張機能を提供しているのだろうか。

MSVCの場合、__declspecというキーワードの、一種の、specifierが存在する。以下のように使う。

// どちらも同じ意味。
__declspec( align(16) ) char buffer1[64] ;
char __declspec( align(16) ) buffer2[64] ;

GCCの場合、__alignof__や、__attribute__という、キーワードがある。

// すべて同じ意味。
__attribute__ ((aligned (16))) char buffer1[60] ;
char __attribute__ ((aligned (16)))  buffer2[60] ;
char buffer3 [60] __attribute__ ((aligned (16))) ;

両者とも、何とも、ファジーな文法ではある。

まず、C++0xの、attributeの文法を見てみる。attributeは、実に様々な場所に、記述できる。

[[/* #1 */]] char [[/* #2 */]] buffer [[/* #3 */]] [64] [[/* #4 */]] ;

さて、アラインメントは、どこに記述すればいいのだろうか。

7.6.2 Alignment attribute [dcl.align] p1を読むと、アラインメントのattributeは、「関数パラメーターか、register」以外の(つまり、関数パラメーターや、registerな変数には指定できないという意味)、変数(Variable)か、クラスのデーターメンバー(ただし、ビットフィールドを除く)に指定できる、と書いてある。

この文、非常にわかりにくかったので、わかりやすく改行して引用する。

The attribute can be applied
  to a variable that is neither(i.e except followings)
    a function parameter
  nor
    declared with the register storage class specifier

and(can be applied)
  to a class data member that is not a bit-field.

ということは、一箇所にしか記述できない。すなわち、#3である。

唯一の正しい例は、以下の通りである。

// well-formed.
char buffer [[ align(16)]] [64] ;

以下はすべて、間違った例である。

// ill-formed.
[[ align(16)]] char buffer[64] ;
char [[ align(16)]] buffer[64] ;
char buffer[64] [[ align(16)]] ;

何故か。そもそも、variableというのは、3 Basic concepts [basic] p6に書いてあるように、宣言文によってもたらされる、名前(name)である。ここで、変数の名前とは、bufferである。では、この名前にattributeを指定したい場合は、どこに書けばいいのか。

8 Declarators [dcl.decl] p4に書いてあるように、

noptr-declarator:
    declarator-id attribute-specifieropt

である。つまり、名前の直後に、attributeを指定しなければならない。

この、variable(変数)に対して、指定しなければならないというのは、統一的で、分かりやすい文法である。というのも、C++は、ひとつの宣言文で、複数の変数を宣言できる言語である。

int (*a)(int), b ;

一目瞭然であるが、aは、関数ポインタ型であり、bは、int型である。もし、以下のように、アラインメントattributeを指定できるとしたら、どうにも、言語の文法的に、分かりにくい。

// これはエラー
[[align(16)]] int (*a)(int), b ;

int (*a [[align(16)]] )(int), b [[align(16)]] ;

変数(つまり、変数の名前)に指定するというルールは、文法的には、分かりやすい。ただ、どうも、人間には、わかりやすくないと思う。

int (*a [[align(16)]] [10])(int) ;

まあ、これは、極端な例で、関数ポインタの配列をアラインメントしたいという事は、あまりないだろうが。

基本的に、標準のattributeはすべて、名前に付くとしておけば、とりあえず、参考書の解説としては、分かりやすいだろうか。どうもこの詳細は、参考書で書いても、無駄にページを浪費するだけだと思う。

No comments: