Ada 202x (30日目) - 'Object_Size ================================ .. post:: Dec 30, 2019 :tags: ada, ada_2022 たまにはGNAT以外のコンパイラからの逆輸入があってもいいと思います。 経緯 ---- AdaではCPUに囚われず任意のビット数の型を定義できることはご存知かと思います。 例えば7ビットの符号なし整数型はこう定義します。 .. code-block:: ada declare type Unsinged_7 is mod 2 ** 7; for Unsinged_7'Size use 7; Ada 2012からのアスペクト構文であればこう書きます。 .. code-block:: ada declare type Unsinged_7 is mod 2 ** 7 with Size => 7; しかしこの ``Unsigned_7`` 型の変数を配置したとしまして実際丁度7ビットだけが割り当てられるということはありません。 CPUが効率良くアクセスできるメモリの境界に合わせて切り上げられます。 8ビットが最小単位のCPUであれば実際には8ビット割り当てられることでしょう。 (アラインメントは割り当ての開始位置を調整することですのでこれは厳密にはアラインメントとはちょっと違う話でしょうか。) 中途半端なビット数の指定がそのまま使われるのは配列や ``record`` の中に入れてなおかつ ``Pack`` 等の内部表現の指定をした場合のみとなります。 配列や ``record`` の中であっても特に指定しない場合はやはり切り上げられます。 それで実際に割り当てられたビット数を知りたければ型ではなくてオブジェクトの ``'Size`` 属性を参照できます。 .. code-block:: ada 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`` を付けています。 .. code-block:: ada 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になります。 .. code-block:: ada 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** .. _`AI12-0059-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0059-1.txt