Adaの定数の話

Cだと#defineするような、ハードコーディングしてメモリに直接乗せたくないような定数ってAdaでどうやって書くの?というお話。

結論を先に書きますとこれで万事OKです。

function Make_Constant is new Ada.Unchecked_Conversion (Integer, Integer); -- integer
function Make_Constant is new Ada.Unchecked_Conversion (Float, Float); -- real
function Make_Constant is new Ada.Unchecked_Conversion (Boolean, Boolean); -- discrete
function Make_Constant is new Ada.Unchecked_Conversion (String, String); -- array
function Make_Constant is new Ada.Unchecked_Conversion (Point, Point); -- record

function X (S : Integer := 17) return Integer renames Make_Constant;
function X (S : Float := 17.0) return Float renames Make_Constant;
function X (S : Boolean := False) return Boolean renames Make_Constant;
function X (S : String := "Hey") return String renames Make_Constant;
function X (S : Point := (X => 17, Y => 18) ) return Point renames Make_Constant;

Ada.Unchecked_Conversionはキャストを行うジェネリックな関数なのですが、同じ型にキャストすることで何もしない関数として便利に使えます。引数のデフォルト値を設定することで、デフォルト値がそのまま展開される寸法です。
もう一枚ラップして、こんな風に書けるようにすると便利です。

function X is new Generic_Constant (Integer, 17);

ただしAdaでは数値型のみ型無し定数が許されているので、整数と実数はそちらを使ったほうが良いでしょう。

X : constant := 17;
Y : constant := 17.0;

型無しの場合はUniversal_Integer*1のままにできます。

X : constant := 17;
Var_1 : Integer := X; -- OK
Var_2 : Interfaces.C.int := X; -- OK
Var_3 : Interfaces.C.int := Var_1; -- NG

ただし定数で宣言してしまうとオーバーロードできなくなります。
引数が無いのにオーバーロード?と侮る無かれ。Adaは返値型でオーバーロードが解決できますので、これがやたら便利です。
関数として宣言した場合は、返値型によるオーバーロードが使えますので、関数の方が便利なときもあります。
aliasedではないconstantはどの道'Access取れませんから、型付きの場合、aliasedにする必要が無ければ関数は定数の文法上の上位互換です。ただし巨大な配列や構造体は、何度も出現させるわけにもいきませんのでおとなしく定数にしておいた方がいいでしょう。

数値型の場合は、Ada.Unchecked_Conversionを使うまでも無く単項"+"演算子も使えます。
Adaでは演算子もin等を除いてほぼ全部関数ですので別名を定義できます。

function X (S : Integer := 17) return Integer renames "+";
function X (S : Float := 17.0) return Float renames "+";

それから、Adaでは、列挙型の要素も、関数です。
ですので列挙型の要素名が被っても、単にオーバーロードされただけの状態です。
最近の言語では列挙型に型名をプレフィクスとして付けるのが流行りですが、返値型によるアドホックオーバーロード便利ですよと切に思います。

function X return Boolean renames False;
function X return Character renames 'X';
Var_4 : Boolean := X; -- OK
Var_5 : Character := X; -- OK

本当に役に立つのはbuild-in-placeと組み合わせてコンストラクタ代わりに使うときです。

Var_6 : Window := Create ("TITLE");
Var_7 : Named_Mutex := Create ("NAME");

まとめます。

  • 数値型 → function 定数名 (S : 型 := 値) return 型 renames "+";
  • 列挙型 → function 定数名 return 型 renames 値;
  • それ以外 → 一旦Ada.Unchecked_Conversionをインスタンス化して"+"同様に使う

(はたしてこれをネタと

*1:リテラル表記された整数の型であらゆる整数型に暗黙に変換可能。あくまでコンパイル時だけの概念上の型であり変数としては宣言できません。