Ada 202x (30日目) - 'Object_Size¶
たまにはGNAT以外のコンパイラからの逆輸入があってもいいと思います。
経緯¶
AdaではCPUに囚われず任意のビット数の型を定義できることはご存知かと思います。
例えば7ビットの符号なし整数型はこう定義します。
declare
type Unsinged_7 is mod 2 ** 7;
for Unsinged_7'Size use 7;
Ada 2012からのアスペクト構文であればこう書きます。
declare
type Unsinged_7 is mod 2 ** 7
with Size => 7;
しかしこの Unsigned_7
型の変数を配置したとしまして実際丁度7ビットだけが割り当てられるということはありません。
CPUが効率良くアクセスできるメモリの境界に合わせて切り上げられます。
8ビットが最小単位のCPUであれば実際には8ビット割り当てられることでしょう。
(アラインメントは割り当ての開始位置を調整することですのでこれは厳密にはアラインメントとはちょっと違う話でしょうか。)
中途半端なビット数の指定がそのまま使われるのは配列や record
の中に入れてなおかつ Pack
等の内部表現の指定をした場合のみとなります。
配列や record
の中であっても特に指定しない場合はやはり切り上げられます。
それで実際に割り当てられたビット数を知りたければ型ではなくてオブジェクトの 'Size
属性を参照できます。
declare
type Unsigned_7_Array is array (Positive range <>) of Unsigned_7;
type Packed_Unsigned_7_Array is array (Positive range <>) of Unsigned_7
with Pack;
Standalone : Unsigned_7;
In_Array : Unsigned_7_Array (1 .. 1);
In_Packed_Array : Packed_Unsigned_7_Array (1 .. 1);
begin
Ada.Integer_IO.Put (Standalone'Size); -- 8
Ada.Integer_IO.Put (In_Array (1)'Size); -- 8
Ada.Integer_IO.Put (In_Packed_Array (1)'Size); -- 7
しかし場合によってはオブジェクトを介さずに切り上げ後のサイズを知りたいときもあります。
例えば内部表現を 'Size
を参照して書きたい場合等です。
次の例は A
をCPUにとって最適にアクセスできるよう配置して残る X
、Y
、Z
のみを詰め込むことを意図しています。
A
が配置されるバイト(storage unit)を他の要素と共有しないことを明示するため aliased
を付けています。
declare
type Rec
record
A : aliased Unsinged_7;
X, Y, Z : Boolean;
end record;
for Rec use
record
A at 0 range 0 .. Unsinged_7'Size - 1; -- error
X at 0 range Unsinged_7'Size .. Unsinged_7'Size; -- error
Y at 0 range Unsinged_7'Size + 1 .. Unsinged_7'Size + 1;
Z at 0 range Unsinged_7'Size + 2 .. Unsinged_7'Size + 2;
end record;
for Rec'Alignment use Unsigned_7'Alignment;
これは上手く行きません。
Unsinged_7'Size
は7ですので X
と Y
が1バイト目に詰め込まれてしまいます。
aliased
を付けましたのでエラーとして検出できています。
このため従来は (Unsinged_7'Size + 7) / 8 * 8
等といった冗長なコードも書かれていました。
Ada 202xでの改善¶
'Object_Size¶
GNATの拡張だった 'Object_Size
属性が追認されています。
型の 'Object_Size
属性はその型で普通に宣言した変数の 'Size
属性と同じになります。
Unsinged_7'Object_Size
であれば Standalone'Size
と同じ8になります。
declare
type Rec
record
A : aliased Unsinged_7;
X, Y, Z : Boolean;
end record;
for Rec use
record
A at 0 range 0 .. Unsinged_7'Object_Size - 1;
X at 0 range Unsinged_7'Object_Size .. Unsinged_7'Object_Size;
Y at 0 range
Unsinged_7'Object_Size + 1 .. Unsinged_7'Object_Size + 1;
Z at 0 range
Unsinged_7'Object_Size + 2 .. Unsinged_7'Object_Size + 2;
end record;
for Rec'Alignment use Unsigned_7'Alignment;
これで Rec
の内部表現は A
が0 .. 7ビット目(計8ビット)、X
が8ビット目、Y
が9ビット目、Z
が10ビット目に配置されることになります。
Rec
自体は普通に変数を宣言すれば2バイトを占めるでしょう。
もし内部表現を書かなければ X
、Y
、Z
もそれぞれ1バイト使いますから4バイトになりますので圧縮成功です。
関連AI¶
AI12-0059-1 Object_Size attribute