Delphi 10.3以降: 列挙型で順序値を割り当てたときに起きるE2026エラーを回避する

# はじめに
Delphiの列挙型はOrd関数で順序値を返すことができます。
列挙型の定義を
```pascal
type
  TEnum = (Val0 = 5, Val1 = 10, Val = 15); 
```
と定義すると `Ord(TEnum.Val0)` は `5` を返します。
順序値は整数型で **-2147483648..2147483647** の範囲を指定できます。
順序値が指定されていない場合は0から順番に1,2,3・・・の値が設定されます。

# 検証
では，実際に試してみましょう。
新しいプロジェクトを新規作成し，以下のように**TButton**と**TMemo**を配置します。
![Snap0011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/be34f8ba-138b-5acc-31f9-e0f41dbba52d.png)
配置されたButton1にClickイベントを設定し，以下のように書きます。
```unit1.pas
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以前で動作するか確認できませんでした。)
```pascal
type
  // TEnum 10.2 以前は問題なく使えたらしい列挙型
  TEnum = (
    te0Val0 = 0,
    te0Val1 = 1,
    te0Val2 = 2,
    te0None = $FFFFFFFF
  );
```
こう書くと，10.3以降では，

:::note alert
 E2026 定数式が必要です 行 YYY (YYYY:XX)
:::

と表示されて，実行できなくなります。これが今回解決したい問題です。

# 回避方法 ・・・ 整数型でキャストする

このエラーは **「コンパイラはここで定数式を期待しましたが，見つかった式は定数式ではありませんでした。」**
というものなのですが，ちょっと違和感ありますね。定数が求められている方とは違う，あるいは，オーバーフローしているというエラーではないかと思います。

順序値は整数型なので32bitです。なので，$FFFFFFFFはサイズは収まっています。
そこで$FFFFFFFFを`integer`にキャストすれば定義できるはずです。
`integer`が32bitであることが分かりにくいと感じるなら，`INT32`か`FixedInt`でキャストすると良いと思います。
ちなみに，32bitと64bitで長さが変わる整数型は`NativeInt`です。

最初に書いたプログラムを書き換えてテストをしてみましょう。

```unit1.pas
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）](https://docwiki.embarcadero.com/RADStudio/Alexandria/ja/%e5%8d%98%e7%b4%94%e5%9e%8b%ef%bc%88Delphi%ef%bc%89#.E5.88.97.E6.8C.99.E5.9E.8B) 列挙型の説明があります。
- [Embarcadero Rad Studio docwiki - E2026 定数式が必要です (Delphi)](https://docwiki.embarcadero.com/RADStudio/Alexandria/ja/E2026_%E5%AE%9A%E6%95%B0%E5%BC%8F%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%A7%E3%81%99_(Delphi))
- [Embarcadero Rad Studio docwiki - 内部データ形式（Delphi）](https://docwiki.embarcadero.com/RADStudio/Alexandria/ja/%E5%86%85%E9%83%A8%E3%83%87%E3%83%BC%E3%82%BF%E5%BD%A2%E5%BC%8F%EF%BC%88Delphi%EF%BC%89#.E6.95.B4.E6.95.B0.E5.9E.8B) 整数型をキャストできる型を調べるために使いました。

ありがとうございます。