Chet とは
OSS で公開されている C ヘッダを Object Pascal のコードに変換するツールです。
実行ファイルはこちらからダウンロードできます。
https://github.com/neslib/Chet/tree/master/Bin
Chet が他の変換ツールと違うのは Clang を使って実際にコンパイルし解析する所です。
そのため、他の変換ツールだと間違う場面でも、正しく変換できる事が多いようです。
2020 年のエンバカデロ・テクノロジーズのブログにも紹介されています。
https://blogs.embarcadero.com/c-header-translator-tool-for-delphi-lets-you-parse-c-header-files-using-the-clang-compiler/
Chet で GameInput.h を変換する
今回「クソアプリ Advent Calendar 2024」用に必要になった(最終的には使いませんでしたが…)GameInput.h を変換してみます。
変換に必要な最低限の設定を紹介します。
まずは、以下に紹介している項目を埋めます。
Project
項目 | 内容 |
---|---|
Directory with C Header files | C のヘッダファイルがある フォルダ |
Target Pascal file | 出力される Object Pascal のファイル名 |
フォルダにあるヘッダファイルを全て1つの Object Pascal のファイルとして出力します。
Platforms
項目 | 内容 |
---|---|
Library constant | DLL や Lib をインポートする時に使う 定数の名前 |
Platform | 必要なプラットフォームにチェックを入れます |
Platform.Library name | リンクする Lib や DLL を指定します |
Translate
あとはデフォルトのままで大丈夫です。
[Run Header Translator] を押すと…
メッチャエラーが出ます。
C のソースを変換するツールなので C23 未満では定義されていない bool
型に対するエラーが出ています。
Conversion Options
の項にある Custom types map
に Key Value で定義しておくと置き換えてくれるみたいですが上手く動きませんでした。
なので、GameInput.h 側の bool を全て char に置き換えました。
これで、改めて実行すると
上手くいきました。
弊害として、bool になるところが UTF8Char になります…
これで、GameInput.pas が出力されました。
手直しする
Chet が高性能とはいえそのままビルドできるソースができるのはマレです。
出力された GameInput.pas そのままだと、↓これだけエラーが出ます。
このエラーを潰していきます。
1. Winapi.Windows を追加
interface
uses
Winapi.Windows;
これを追加するだけでエラーが激減します。
2. エラーの中で使われていない物を排除
INTERFACE = IGameInput;
と書いてあるところが予約語の Interface と被っていてエラーが出ています。
他で使われていないので削除します。
もう一つ _HRESULT_TYPEDEF_
が見つからないというエラーがでているので、コレも削除します。
_HRESULT_TYPEDEF_
は引数の定数を HRESULT の型として定義するマクロなので、そのまま定数として宣言して問題ありません。
GAMEINPUT_E_DEVICE_DISCONNECTED = _HRESULT_TYPEDEF_($838A0001);
GAMEINPUT_E_DEVICE_DISCONNECTED = $838A0001;
3. 未定義の構造体の定義
APP_LOCAL_DEVICE_ID 構造体定義が無いので追加します。
const
APP_LOCAL_DEVICE_ID_SIZE = 32;
type
PAPP_LOCAL_DEVICE_ID = ^APP_LOCAL_DEVICE_ID;
APP_LOCAL_DEVICE_ID = record
value: array [0.. APP_LOCAL_DEVICE_ID_SIZE - 1] of Byte;
end;
同様に PIID, PIUnknown, HANDLE が未定義なのでこれも定義します。
PIID = PGUID;
PIUnknown = ^IUnknown;
HANDLE = THandle;
ここまで作業が終わるとエラーが全て消えます。
4. bool の後始末
最後に、bool を char に変換したことで UTF8Char になった部分を ByteBool に修正します。
修正結果
全文は長い(1000行超え)ため、修正した部分が含まれるコードを貼っておきます。
unit GameInput;
{ This unit is automatically generated by Chet:
https://github.com/neslib/Chet }
{$MINENUMSIZE 4}
interface
// Winapi.Windows を追加
uses
Winapi.Windows;
const
{$IF Defined(WIN64)}
// Library constant で指定した定数に dll が定義されています
LIB_GAMEINPUT = 'GameInput.dll';
_PU = '';
{$ELSE}
{$MESSAGE Error 'Unsupported platform'}
{$ENDIF}
const
GAMEINPUT_CURRENT_CALLBACK_TOKEN_VALUE = $FFFFFFFFFFFFFFFF;
GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE = $0000000000000000;
FACILITY_GAMEINPUT = 906;
// _HRESULT_TYPEDEF_ を削除してコメントを外したもの
GAMEINPUT_E_DEVICE_DISCONNECTED = $838A0001;
GAMEINPUT_E_DEVICE_NOT_FOUND = $838A0002;
GAMEINPUT_E_READING_NOT_FOUND = $838A0003;
GAMEINPUT_E_REFERENCE_READING_TOO_OLD = $838A0004;
GAMEINPUT_E_TIMESTAMP_OUT_OF_RANGE = $838A0005;
GAMEINPUT_E_INSUFFICIENT_FORCE_FEEDBACK_RESOURCES = $838A0006;
// APP_LOCAL_DEVICE_ID のサイズ
APP_LOCAL_DEVICE_ID_SIZE = 32;
type
// APP_LOCAL_DEVICE_ID の定義
PAPP_LOCAL_DEVICE_ID = ^APP_LOCAL_DEVICE_ID;
APP_LOCAL_DEVICE_ID = record
value: array [0.. APP_LOCAL_DEVICE_ID_SIZE - 1] of Byte;
end;
// 未定義の型を定義
PIID = PGUID;
PIUnknown = ^IUnknown;
HANDLE = THandle;
COM Interface の変換
今回変換した GameInput.h は、COM ライトと呼ばれる COM Interface を公開するヘッダです。
ですが、これもきちんと変換できたので Clang の威力を感じました。
IGameInputDispatcher = record
lpVtbl: PIGameInputDispatcherVtbl;
end;
IGameInputDispatcherVtbl = record
QueryInterface: function(This: PIGameInputDispatcher; const riid: PIID; ppvObj: PPointer): HRESULT; stdcall;
AddRef: function(This: PIGameInputDispatcher): ULONG; stdcall;
Release: function(This: PIGameInputDispatcher): ULONG; stdcall;
Dispatch: function(This: PIGameInputDispatcher; quotaInMicroseconds: UInt64): UTF8Char; stdcall;
OpenWaitHandle: function(This: PIGameInputDispatcher; waitHandle: PHANDLE): HRESULT; stdcall;
end;
ちなみに、このタイプに変換されたものは下記の様に VTable に直接アクセすることで呼び出せます。
var Intf: IGameInputDispatcher;
Intf.lpVtbl^.AddRef(Intf);
まとめ
C のライブラリを使う場合などヘッダファイルの移行を大分手助けしてくれます。
修正が必要とはいえ、自分で書く事に比べて大分時間が節約できます。
Post Process
の項では、スクリプトも組めるようです。
今回、手作業で修正しましたがもしかするとスクリプトで修正できるかもしれません。
使って見たい C のライブラリがあるけどヘッダの移植が面倒くさいな…っていう時、試して見てください。