7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

image.png

項目 内容
Directory with C Header files C のヘッダファイルがある フォルダ
Target Pascal file 出力される Object Pascal のファイル名

フォルダにあるヘッダファイルを全て1つの Object Pascal のファイルとして出力します。

Platforms

image.png

項目 内容
Library constant DLL や Lib をインポートする時に使う 定数の名前
Platform 必要なプラットフォームにチェックを入れます
Platform.Library name リンクする Lib や DLL を指定します

Translate

あとはデフォルトのままで大丈夫です。
[Run Header Translator] を押すと…

image.png

メッチャエラーが出ます。
C のソースを変換するツールなので C23 未満では定義されていない bool 型に対するエラーが出ています。

Conversion Options の項にある Custom types map に Key Value で定義しておくと置き換えてくれるみたいですが上手く動きませんでした。

なので、GameInput.h 側の bool を全て char に置き換えました。

これで、改めて実行すると

image.png

上手くいきました。
弊害として、bool になるところが UTF8Char になります…

これで、GameInput.pas が出力されました。

手直しする

Chet が高性能とはいえそのままビルドできるソースができるのはマレです。
出力された GameInput.pas そのままだと、↓これだけエラーが出ます。

image.png

このエラーを潰していきます。

1. Winapi.Windows を追加

interface

uses
  Winapi.Windows;

これを追加するだけでエラーが激減します。

image.png

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 の威力を感じました。

COM Interface の変換結果抜粋
  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 のライブラリがあるけどヘッダの移植が面倒くさいな…っていう時、試して見てください。

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?