Ada 2020 (15日目) - ‘Image

折り返し? お、折り返しでしょうか? まだまだ終わりそうにないのです?

経緯

今回は7日目の宿題回収です。

今まで 'Image 属性は数値型や列挙型でしか使えませんでした。 また返す文字列の書式も固定でした。

Ada 2020での改善

全ての型で 'Image 属性が使えるようになりました。 わー拍手。

record や配列にはaggregate式風の文字列が返りますし、class-wide型にはqualified式風の文字列が返ります。 access 型にはアドレスを16進表記した文字列が返ります。

printデバッグが捗ります。

Ada 2012のTechnical Corrigendum 1の時点で関数形式の T'Image (V) だけでなく直接 O'Image と書けるようになっていたことも覚えておくと更に捗ります。

また 'Image 属性の返す文字列は型ごとにカスタマイズできるようになりました。

‘Put_Image

'Image 属性をカスタマイズするには 'Put_Image 属性を自分で用意した手続きで上書きすればいいです。 'Read'Write をカスタマイズするのと同じ感じです。

'Put_Image は渡されたバッファに値を文字列化したものを入れる属性です。 'Image 属性は 'Put_Image 属性を使って処理系が用意したバッファを埋め、それからバッファの中身を文字列にして返す、という挙動になったわけです。

バッファの型として最初は Ada.Streams.Storage が使われる予定だったのですが、バイナリではなく文字列をメモリ上で作る出力先として Ada.Strings.Text_Buffers が用意されました。 (JavaやC#の StringBuffer みたいなものです。)

'Image ではなく 'Put_Image を直接使うこともできます。 例えば入れ子の実装には 'Image を使って無駄に文字列を経由させるよりも効率的です。

type My_Rec is record X : Float; Y : Character; end record;

procedure My_Put_Image
  (Buffer : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
   Arg : in My_Rec);
for T'Put_Image use My_Put_Image;

procedure My_Put_Image
  (Buffer : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
   Arg : in My_Rec) is
begin
   Ada.Strings.Text_Buffers.Put (Buffer, "(X => ");
   Float'Put_Image (Buffer, Arg.X);
   Ada.Strings.Text_Buffers.Put (Buffer, ", Y => ");
   Character'Put_Image (Buffer, Arg.Y);
   Ada.Strings.Text_Buffers.Put (Buffer, ")");
end My_Put_Image;

自分で用意したバッファに出力させることもできます。

declare
   Buffer : Ada.Strings.Text_Buffers.Unbounded.Buffer_Type;
   Obj : My_Rec := (X => 5.0, Y => 'A');
begin
   Ada.Strings.Text_Buffers.Put (Buffer, "Obj = ");
   My_Rec'Put_Image (Buffer, Obj);

この例では Buffer には "Obj = (X => 5.0, Y => 'A')" が入ります。

Ada.Strings.Text_Buffers.Root_Buffer_Type が抽象型ですので派生型を作れば出力先を変えることもできます。 例えば文字列を介さず直接 Text_IO に出力するようにすること等が考えられます。 (これは StringBuffer と異なる点です。)

注意点としまして 'Image 属性はカスタマイズできるようになりましたが 'Value 属性は従来のまま数値型や列挙型にしか使えませんしカスタマイズもできないという点です。 'Image 属性の結果は 'Value 属性で元の値にできることが期待されているため、数値型や列挙型の 'Image 属性をカスタマイズするときは 'Value 属性が処理できる形に留める必要があります。 例えば16進数にする場合は単に "FF" としたりC系風に "0xFF" としてしまいますと 'Value 属性が処理できませんので "16#FF#" とする必要があります。

'Image 属性はあくまで簡易的な文字列化です。 主な用途はデバッグやシリアライズに留めて、ちゃんとした出力にはちゃんとした書式化ができるライブラリを使うようにしましょう。

関連AI