Ada 202x (44日目) - その他の式関連の変更 ======================================== .. post:: Jan 13, 2020 :tags: ada, ada_2022 終わらないその他シリーズ。 limited型とraise式 ------------------ Ada 202xからはlimited型を求められている箇所でもraise式が使えるようになりました。 raise式はAda 2012 with Technical Corrigendum 1で導入されました。 しかしlimited型に使える式としては認められていませんでした。 .. code-block:: ada package P1 is type T (<>) is limited private; private type T is limited null record; end P1; .. code-block:: ada declare Object : P1.T := raise Program_Error; 他にはAda 202xから追加されるdeclare式もlimited型に使えます。 関数とNo_Return --------------- Ada 202xからは関数に ``No_Return`` アスペクト(またはpragma)が指定できるようになりました。 ``No_Return`` はAda 2012から導入されました。 しかしこれまでは手続きにしか指定できませんでした。 .. code-block:: ada with P1; package P2 is function Unimplemented return Boolean with No_Return; function Unimplemented return P1.T with No_Return; end P2; ところで関数にはreturn文を最低ひとつは書かないといけないルールがあります。 そのため従来このような関数の本体ではraise文に続けてダミーのreturn文が置かれてきました。 それも簡単な型なら良いですが複雑な型や ``P1.T`` のように抽象型ですとダミーの値を用意するのは大変です。 そのため手っ取り早く型だけ満たす手段として再帰も利用されてきました。 .. code-block:: ada package body P2 is function Unimplemented return Boolean is begin raise Program_Error; return False; end Unimplemented; function Unimplemented return P1.T is begin raise Program_Error; return Unimplemented; end Unimplemented; end P2; 仕様部からbody部のスケルトンを生成するツールであるgnatstubも再帰を使ったコードを吐きます。 しかし再帰は再帰で別の問題があったりします。 処理系にスタックの使用量を予測する機能があれば誤認されたりしますし、GNATの処理系定義の ``pragma Restrictions (No_Recursion);`` に引っかかったりもします。 これはreturn文の中でraise式を使えば再帰を使わずに済みます。 .. code-block:: ada package body P2 is -- 省略 function Unimplemented return P1.T is begin return raise Program_Error; end Unimplemented; end P2; 更に言えば、わざわざ本体側を書かなくてもexpression functionにしてしまえば仕様部だけで済みます。 .. code-block:: ada with P1; package P2 is function Unimplemented return Boolean with No_Return is (raise Program_Error); function Unimplemented return P1.T with No_Return is (raise Program_Error); end P2; raise式を全ての型に使えるようになったことを覚えておきますと ``No_Return`` な関数を楽に書けます。 文字列の"&" ----------- 文字列の ``"&"`` 演算子がstaticになりました。 現状static関数で使える複合型(composite type)は文字列のみですので上手く使いたいですね。 ``Wide_Wide_String`` なら31ビット値の配列として使えますぜへへへ……。 悪巧みは置きまして、staticな文字列を要求される ``External_Name`` 等でも式を使えることになりますのできちんと活用できる場面もあると思われます。 universal_integerと範囲チェック ------------------------------- universal_integerは型が付く前の式を表す架空の型です。 基本的にはコンパイル時定数です。 .. code-block:: ada declare U : constant := 0; ところが一部の属性を使うと実行時の値に型が付かないままになることがあります。 つまりuniversal_integerにも実行時の値があります。 .. code-block:: ada declare type T is mod 2 ** 8; X : Wide_Wide_Character; Y : T; begin Ada.Wide_Wide_Text_IO.Get (X); Y := T'Mod (Wide_Wide_Character'Pos (X)); ``'Pos`` の結果の型は文脈上求められている整数型になります。 ``'Mod`` の引数の型は実際に渡された値の型になります。 このような属性を組み合わせますと型が付きませんので実行時の値としてのuniversal_integerを出現させることができます。 Ada 202xでは実行時のuniversal_integerがroot_integerの範囲を超えた場合に ``Constraint_Error`` になると規定されました。 浮動小数点数も同じで実行時のuniversal_realがroot_realの範囲を超えたら ``Constraint_Error`` になります。 root_integerやroot_realは処理系が実行時に扱える最大の整数または浮動小数点数です。 型付けされた全ての整数型や浮動小数点数型はroot_integerやroot_realの派生型(部分型)です。 注意点としてコンパイル時は依然として別扱いです。 例えば64ビットの符号なし型を定義するときは次のように書きますよね。 .. code-block:: ada type Unsigned_64 is mod 2 ** 64; ``2 ** 64`` は64ビットに収まっていませんが64ビットまでしか扱えない環境でもコンパイルできます。 この ``2 ** 64`` はコンパイル時universal_integerです。 つまりコンパイル時定数としてのuniversal_integerはroot_integerに縛られません(処理系依存の何らかの上限はあるはずです)が、実行時のuniversal_integerはroot_integerの範囲に縛られることになります。 関連AI ------ - `AI12-0172-1`_ **Raise expressions in limited contexts** - `AI12-0201-1`_ **Missing operations of static string types** - `AI12-0269-1`_ **Aspect No_Return for functions reprise** - `AI12-0227-1`_ **Evaluation of nonstatic universal expressions when no operators are involved** .. _`AI12-0172-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0172-1.txt .. _`AI12-0201-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0201-1.txt .. _`AI12-0227-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0227-1.txt .. _`AI12-0269-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0269-1.txt