はじめに
Delphiの列挙型はOrd関数で順序値を返すことができます。
列挙型の定義を
type
TEnum = (Val0 = 5, Val1 = 10, Val = 15);
と定義すると Ord(TEnum.Val0)
は 5
を返します。
順序値は整数型で -2147483648..2147483647 の範囲を指定できます。
順序値が指定されていない場合は0から順番に1,2,3・・・の値が設定されます。
検証
では,実際に試してみましょう。
新しいプロジェクトを新規作成し,以下のようにTButtonとTMemoを配置します。
配置されたButton1にClickイベントを設定し,以下のように書きます。
type
// TEnum0 順序値を指定しない列挙型
TEnum0 = (
te0Val0,
te0Val1,
te0Val2,
te0None
);
// TEnum1 順序値を指定した列挙型
TEnum1 = (
te1Val0 = 0,
te1Val1 = 1,
te1Val2 = 2,
te1None = 2147483647 // 順序型で指定できる最大値 $7FFFFFFF
);
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Clear;
// TEnum0 の順序値の表示
Memo1.Lines.Add('TEnum0.te0Val0 = '+ord(TEnum0.te0Val0).ToString);
Memo1.Lines.Add('TEnum0.te0Val1 = '+ord(TEnum0.te0Val1).ToString);
Memo1.Lines.Add('TEnum0.te0Val2 = '+ord(TEnum0.te0Val2).ToString);
Memo1.Lines.Add('TEnum0.te0None = '+ord(TEnum0.te0None).ToString);
// TEnum1 の順序値の表示
Memo1.Lines.Add('TEnum1.te1Val0 = '+ord(TEnum1.te1Val0).ToString);
Memo1.Lines.Add('TEnum1.te1Val1 = '+ord(TEnum1.te1Val1).ToString);
Memo1.Lines.Add('TEnum1.te1Val2 = '+ord(TEnum1.te1Val2).ToString);
Memo1.Lines.Add('TEnum1.te1None = '+ord(TEnum1.te1None).ToString);
end;
実行して,Button1をクリックすると,Memo1には以下のように表示されます。
TEnum0.te0Val0 = 0
TEnum0.te0Val1 = 1
TEnum0.te0Val2 = 2
TEnum0.te0None = 3
TEnum1.te1Val0 = 0
TEnum1.te1Val1 = 1
TEnum1.te1Val2 = 2
TEnum1.te1None = 2147483647
期待通りの動作です。
ここで問題
Delphi 10.2 以前のライブラリには,以下のような列挙型が定義されていることがあります。
(今手元にあるのが10.3以降ですので,以下のコードが10.2以前で動作するか確認できませんでした。)
type
// TEnum 10.2 以前は問題なく使えたらしい列挙型
TEnum = (
te0Val0 = 0,
te0Val1 = 1,
te0Val2 = 2,
te0None = $FFFFFFFF
);
こう書くと,10.3以降では,
E2026 定数式が必要です 行 YYY (YYYY:XX)
と表示されて,実行できなくなります。これが今回解決したい問題です。
回避方法 ・・・ 整数型でキャストする
このエラーは 「コンパイラはここで定数式を期待しましたが,見つかった式は定数式ではありませんでした。」
というものなのですが,ちょっと違和感ありますね。定数が求められている方とは違う,あるいは,オーバーフローしているというエラーではないかと思います。
順序値は整数型なので32bitです。なので,$FFFFFFFFはサイズは収まっています。
そこで$FFFFFFFFをinteger
にキャストすれば定義できるはずです。
integer
が32bitであることが分かりにくいと感じるなら,INT32
かFixedInt
でキャストすると良いと思います。
ちなみに,32bitと64bitで長さが変わる整数型はNativeInt
です。
最初に書いたプログラムを書き換えてテストをしてみましょう。
type
// TEnum0 順序値を指定しない列挙型
TEnum0 = (
te0Val0,
te0Val1,
te0Val2,
te0None
);
// TEnum1 順序値を指定した列挙型
TEnum1 = (
te1Val0 = 0,
te1Val1 = 1,
te1Val2 = 2,
te1None = INT32($FFFFFFFF) // INT32でキャスト
);
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Clear;
// TEnum0 の順序値の表示
Memo1.Lines.Add('TEnum0.te0Val0 = '+ord(TEnum0.te0Val0).ToString);
Memo1.Lines.Add('TEnum0.te0Val1 = '+ord(TEnum0.te0Val1).ToString);
Memo1.Lines.Add('TEnum0.te0Val2 = '+ord(TEnum0.te0Val2).ToString);
Memo1.Lines.Add('TEnum0.te0None = '+ord(TEnum0.te0None).ToString);
// TEnum1 の順序値の表示
Memo1.Lines.Add('TEnum1.te1Val0 = '+ord(TEnum1.te1Val0).ToString);
Memo1.Lines.Add('TEnum1.te1Val1 = '+ord(TEnum1.te1Val1).ToString);
Memo1.Lines.Add('TEnum1.te1Val2 = '+ord(TEnum1.te1Val2).ToString);
Memo1.Lines.Add('TEnum1.te1None = '+ord(TEnum1.te1None).ToString); // そのままだと -1 が表示される
// 一旦DWORDにキャストしてHEXで表示すると期待された順序値になっていることが分かる
Memo1.Lines.Add('TEnum1.te1None= '+DWORD(ord(TEnum1.te1None)).ToHexString);
end;
実行してButton1をクリックすると,Memo1には以下のように表示されます。
TEnum0.te0Val0= 0
TEnum0.te0Val1= 1
TEnum0.te0Val2= 2
TEnum0.te0None= 3
TEnum1.te1Val0= 0
TEnum1.te1Val1= 1
TEnum1.te1Val2= 2
TEnum1.te1None= -1
TEnum1.te1None= FFFFFFFF
と,期待された値が格納されていることが分かります。Ord関数
を使って直接順序値を扱う場合にはDWORD
でキャストしたほうが良いかもしれません。
そのままで期待した動作にならない場合には検討されると良いと思います。また,DWORD
の代わりにUINT32
でキャストしても同様の効果が得られます。
まとめ
- Delphi 10.2以前のライブラリをそれ以降のDelphiでコンパイルした時に,E2026エラーが出たら,列挙型の順序値の定義を疑ってみる。
- 順序値が2,147,483,647($7FFFFFFF)より大きい値が指定されていたら,
Integer
またはINT32
で順序値をキャストする。
例:$FFFFFFFF
→integer($FFFFFFFF)
10.2以前のライブラリの更新作業などのお役に立てば幸いです。
謝辞
以下のリンクを参考にしてこの記事を書きました。
- Embarcadero Rad Studio docwiki - 単純型(Delphi) 列挙型の説明があります。
- Embarcadero Rad Studio docwiki - E2026 定数式が必要です (Delphi)
- Embarcadero Rad Studio docwiki - 内部データ形式(Delphi) 整数型をキャストできる型を調べるために使いました。
ありがとうございます。