Ada 202x (44日目) - その他の式関連の変更¶
終わらないその他シリーズ。
limited型とraise式¶
Ada 202xからはlimited型を求められている箇所でもraise式が使えるようになりました。
raise式はAda 2012 with Technical Corrigendum 1で導入されました。 しかしlimited型に使える式としては認められていませんでした。
package P1 is
type T (<>) is limited private;
private
type T is limited null record;
end P1;
declare
Object : P1.T := raise Program_Error;
他にはAda 202xから追加されるdeclare式もlimited型に使えます。
関数とNo_Return¶
Ada 202xからは関数に No_Return
アスペクト(またはpragma)が指定できるようになりました。
No_Return
はAda 2012から導入されました。
しかしこれまでは手続きにしか指定できませんでした。
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
のように抽象型ですとダミーの値を用意するのは大変です。
そのため手っ取り早く型だけ満たす手段として再帰も利用されてきました。
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式を使えば再帰を使わずに済みます。
package body P2 is
-- 省略
function Unimplemented return P1.T is
begin
return raise Program_Error;
end Unimplemented;
end P2;
更に言えば、わざわざ本体側を書かなくてもexpression functionにしてしまえば仕様部だけで済みます。
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は型が付く前の式を表す架空の型です。 基本的にはコンパイル時定数です。
declare
U : constant := 0;
ところが一部の属性を使うと実行時の値に型が付かないままになることがあります。 つまりuniversal_integerにも実行時の値があります。
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ビットの符号なし型を定義するときは次のように書きますよね。
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