Ada 202x (41日目) - その他のrecordの変更¶
あまりにも細かいやつは紹介しなくていいとは思うのですが、実際書き始めると自分でも案外発見があったりしますので一応見ていきます。
バリアントレコード¶
今までaggregate式でバリアントレコードの可変部分を書くときはdiscriminantはstaticである必要がありました。
type D_Type is (A, B, C);
type VR (D : D_Type) is
record
case D is
when A | B =>
Element_1: Float;
when C =>
Element_2 : Character;
end case;
end record;
function Create (D : D_Type) return VR is
(case D is
when A => VR'(D => A, Element_1 => 1.0),
when B => VR'(D => B, Element_1 => 1.0),
when C => VR'(D => C, Element_2 => 'C'));
discriminantを実行時の値で指定するならこんな風にする必要がありました。
function Create (D : D_Type) return VR is
begin
return Result : VR (D => D) do
case D is
when A | B =>
Result.Element_1 := 1.0;
when C =>
Result.Element_2 := 'C';
end case;
end return;
end Create;
つまり A
の場合と B
の場合をまとめようとすればひとつの式で書けませんでした。
ひとつのaggregate式で済まないということは要素の指定漏れがあってもチェックされなくなるということでもあります。
Ada 202xでは可変部分を書くときもdiscriminantを実行時の値で指定できるようになりました。
discriminantが合わない場合は Constraint_Error
になります。
function Create (D : D_Type) return VR is
(case D is
when A | B => VR'(D => D, Element_1 => 1.0),
when C => VR'(D => C, Element_2 => 'C'));
Unchecked_Unionとaggregate式¶
今まで Unchecked_Union
のaggregate式にnamed associationを使えませんでした。
type U32 (D : Integer := 0) is
record
case D is
when 0 =>
As_Float : Float;
when others =>
As_Unsigned : Interfaces.Unsigned_32;
end case;
end record
with Unchecked_Union;
X : U32 := (0, 1.0);
Y : U32 := (D => 1, As_Unsigned => 16#3f80_0000#); -- error
Ada 202xからは使えるようになりました。
pygmentsさん16進リテラルに _
挟んだだけでエラーになるの情けないでしょう……。
Unchecked_Unionとin式¶
Unchecked_Union
の比較では両辺がconstrained、つまり型でdiscriminantが固定されていなければなりません。
いずれかがunconstrainedの場合は Program_Error
になります。
declare
subtype Float_U32 is U32 (0);
begin
if Float_U32 (X) = Float_U32 (Y) then
null;
end if;
でないと Float
と Interfaces.Unsigned_32
のどちらで比較していいかわからなくなるからですね。
in式では値と型の関係のため基本的に全部 True
になりますが、unconstrained in
constrainedの場合だけ Program_Error
になります。
begin
if Float_U32 (X) in U32 then
null;
end if;
if Float_U32 (X) in Float_U32 then
null;
end if;
if X in U32 then
null;
end if;
if X in Float_U32 then -- Program_Error
null;
end if;
X in Float_U32
を求めるには X.D
を確定させる必要があるからでしょうか。
Ada 2005のときのAIを見てみましたが理由は簡単には読み取れそうにないです。
AI95-00216-01 Unchecked unions -- variant records with no run-time discriminant
で、まあ、ここまで前置きです。 Ada 2012でin式はcase文のように複数の選択肢を続けて書けるようになりました。
begin
if X in U32 | Float_U32 then
null;
end if;
この場合左から X in U32
が先に判定されて True
になるのか、それとも Program_Error
になるのか曖昧でした。
Ada 202xで Program_Error
になると確定しました。
抽象型とin式¶
ユーザー定義の "="
演算子を持つ抽象型で実体が基本型の場合、実体が可視の箇所からはin式が禁止されます。
package Pkg1 is
type T is private;
function "=" (Left, Right : T) return Boolean;
function Is_Zero (X : in T) return Boolean;
private
type T is range -100 .. 100;
end Pkg1;
package body Pkg1 is
function "=" (Left, Right : T) return Boolean is
begin
return Integer (Left) = Integer (Right);
end "=";
function Is_Zero (X : in T) return Boolean;
begin
return X in 0; -- error
end Is_Zero;
end Pkg1;
抽象型のin式は "="
が使われます。
対して基本型のin式はユーザー定義の演算子を介さずに直接比較されます。
両方の性質が可視の箇所ではエラーになるようになったというわけです。
Pkg1
の外で Pkg1.T
にin式を使うことはでき "="
が使用されます。
ドット記法のrenames¶
renames
でドット記法に別名を与えるときはオブジェクト名部分も renames
可能でなければならないルールが追加されました。
type T is tagged null record;
procedure Method (Object : in T) is null;
Object : T;
procedure Bound renames Object.Method;
この Object
の部分が renames
できないと駄目、と言いましてもドット記法が書けるのはtagged型のみですから駄目なケースも限られます。
具体的にはバリアントレコードの可変部分では駄目ということです。
type V_Type (D : Boolean := False) is
record
case D is
when False =>
Element : T;
when True =>
null;
end case;
end record;
V : V_Type := (D => False, others => <>);
procedure Bound renames V.Element.Method; -- error
ユーザー定義indexingもこれに倣います。
上書き不可能なアスペクト¶
tagged型に付くアスペクトには一度指定すると派生先で上書き不可能(nonoverridable)なものがあります。
Aggregate
Constant_Indexing
Default_Iterator
Implicit_Dereference
Integer_Literal
Iterator_Element
No_Controlled_Parts
Real_Literal
String_Literal
Variable_Indexing
Integer_Literal
、 Real_Literal
、 String_Literal
については AI12-0342-1 で一度上書き可能になって AI12-0419-1 で再び上書き不可能になりました。
(ざっと見ただけですのでまだあるかも知れません。)
No_Controlled_Parts
以外は全部コンテナ用の構文糖ですね。
つまりはアップキャストやダウンキャストで構文糖の展開を変えることはできません。
この上書き不可能なアスペクトにいくつかルールが追加されました。
Constant_Indexing/Variable_Indexing¶
Constant_Indexing
と Variable_Indexing
は一度に指定しないといけなくなりました。
基底型で Variable_Indexing
だけ指定しておいて派生先で Constant_Indexing
を追加するようなことは禁止されました。
Objective-Cのように NSMutableArray
を NSArray
から派生させるようなことはやめた方がいいですね。
<読み飛ばし推奨> ちなみに変更可能不可能を継承階層で表現するなら、D言語のconstとimmutableに習って読み取り専用の抽象型から変更不可能と変更可能の両方を派生させるのが賢いやり方と思います。 他の参照を通じて変更されるかもしれないのと、本当に変更不可能なのとを区別できますので。 </読み飛ばし推奨>
interface¶
interface
から継承した上書き不可能なアスペクトが衝突した場合エラーになると定められました。
type I1 is interface with Constant_Indexing => Constant_Reference;
type Reference_Type (Element : not null access Float) is null record
with Implicit_Dereference => Element;
function Constant_Reference (Object : I1; Index : Positive)
return Reference_Type is abstract;
type I2 is interface with Constant_Indexing => Element;
function Element (Object : I1; Index : Positive) return Float is
abstract;
type T is new I1 and I2 with null record; -- error
この例では Constant_Indexing
が衝突しています。
Adaの interface
はEiffelやDelphiやC#のようにサブプログラム名の衝突を解決することもできませんし、こうして組み合わせが確実にエラーになる例もできてしまいましたしで使い辛いです。
やはり継承よりも generic
以下略。
関連AI¶
AI12-0086-1 Aggregates and variant parts
AI12-0160-1 Adding an indexing aspect to an indexable container type
AI12-0162-1 Memberships and Unchecked_Unions
AI12-0174-1 Aggregates of Unchecked_Unions using named notation
AI12-0204-1 Renaming of a prefixed view
AI12-0211-1 Interface types and inherited nonoverridable aspects
AI12-0328-1 Meaning of limited type and record type in 4.5.2(28.1/4)