Creating bindings for C libraries/ja
│
English (en) │
español (es) │
français (fr) │
日本語 (ja) │
русский (ru) │
中文(中国大陆) (zh_CN) │
Cライブラリとのバインディング
概略
このページでは Pascal から C ライブラリへのバインディングについて述べます。通常、Pascal からは C ライブラリを直接用いることができません。全ての C の関数、型、変数について Pascal への翻訳を作る必要があります。よくある C のあれこれを自動変換する H2Pas というツールがあります。自動変換には Lazarus 用の GUI もあり、H2Pas などのツールを用いています。このGUI ツールは、バインディングを修正するための規則集を作る作業を援助します。規則集を作ってしまえば、次からはずっと容易に C ライブラリからの変換ができるようになります。
作業の流れ
- 変換しようとする C ヘッダを探し出します。
- 作業ディレクトリを作成し、バインディングに名前をつけます。
- h2pas ウィザードを用いて、新しい h2pas プロジェクトを作成します。
- そのプロジェクトに .h ファイルを追加します。
- h2pas のオプションを設定します。
- ウィザードを走らせます。
- text tools を追加してエラーを修正し、ウィザードを再度走らせます。
- h2pas がエラーを出さずに走るようになったら、試しにコンパイルしてみます。きれいな出力が得られるように optional tools を追加します。
- テストプログラムを書いて、バインディングをテストします。
- できあがったバインディングは Lazarus-ccr や Free Pascal のウェブサイトで公開しましょう。
ツールのインストール
h2pas は FPC を普通にインストールするとついてきます。
h2paswizard パッケージを Lazarus IDE にインストールします。"Components -> Configure installed packages ..." に行き、右側の一覧から H2PasWizard パッケージを選んで 'Install selection' をクリックし、次いで 'Save and rebuild IDE' を選びます。
IDE を再起動すれば、メニュー項目に Tools -> h2pas が追加されています。
C ヘッダファイルの取得
C ヘッダファイル .h には C ライブラリのインタフェースが記述されています。通常の場合、それらはライブラリと同時には配布されません。ソースを得るか、ライブラリの開発用パッケージを入手するかする必要があります。たとえば、gtk ライブラリの C ヘッダファイルは gtk+-devel パッケージに入っています。
例: MPICH2
ファイル mpich2-1.0.3.tar.gz を http://www-unix.mcs.anl.gov/mpi/mpich2/ からダウンロードし、展開します。C ヘッダファイルは mpich2-1.0.3/src/include にあります。
作業ディレクトリの作成とバインディングの命名
使いやすい名前でディレクトリを作成します。名前には特殊文字を含んではなりません。特殊文字は空白・ウムラウト・フルストップ・コンマなどです。そこに .h ファイルを複製します。
例: MPICH2
Pascal ファイルには h2p ディレクトリを用意します。h2p/c_sources ディレクトリを .h ファイルに用います。
mkdir -p h2p/c_sources cp mpich2-1.0.3/src/include/*.h h2p/c_sources/
(訳注: mkdir を -p オプション付きで呼んでいるので、h2p ディレクトリも同時に作成されます)
ウィザードによる新しいh2pasプロジェクトの作成
"Tools -> h2pas" として h2pas ウィザードを起動します。新しいウィンドウが開きます。このウィンドウと IDE ウィンドウの間は行ったり来たりすることができます。最後に用いた h2pas プロジェクトが自動的にロードされます。新しいプロジェクトを作成するには、"Settings -> New/Clear settings" をクリックし、一番下のボタン "Save settings" を押し、ファイル名を選んでください。
例: MPICH2
"Settings -> New/Clear settings" をクリック、次に最下部の "Save settings" をクリック、h2p/mpi2.h2p として保存します。
プロジェクトへの.hファイルの追加
"C header files" のページで .h ファイルを追加/削除できます。また、一部のファイルだけを変換できるよう、イネーブル/ディセーブル enable/disable もできます。
例: MPICH2
"C header files -> Add .h files ..." をクリック、"mpi.h" を選択。自動的にイネーブルされます。
h2pasオプションの設定
"h2pas Options" で h2pas のパラメタを設定することができます。
例: MPICH2
- イネーブルするものは -e, -D, -p, -w これら以外は全てディセーブルします。
- -l library path は "mpich"。
- 出力拡張子は ".pas"。
- 出力ディレクトリは h2pas/ (デフォルト)なので、空白のままとします。
ウィザードの実行
最下部のボタン "Run h2pas" を押します。これで、<example>.h ファイルが一時ファイル <example>.tmp.h にコピーされ、"Before h2pas" にリストされているツールが走行します。その後 h2pas が実行され、<example>.tmp.h が <example>.inc や <example>.pas あるいはなんであれ h2pas ページで設定した拡張子がつくファイルに変換されます。
h2pas が文法エラーを見つけると、IDE が example.tmp.h ファイルを開き、エラーを起こしている行に飛びます。エラー行が特定できない場合は単に 'syntax error' を表示するだけのこともあります。よくある問題とありうる解決については後述します。
例: MPICH2
h2pas ウィザードは既にこのヘッダの変換に必要な特殊ツールを全て含んでいます。よって、h2pas はエラーなしに走ります。ですが、この時作成されるユニットはまだそのままでは使えません。先をお読みください。
h2pasがエラーを吐く場合
よくあるC の構造で h2pas が認識しないものの一覧と、修正法を示します。
h2pas problem: extern "C"
ヘッダファイルによっては、C++の名前空間ブロックを持っていることがあります:
#if defined(__cplusplus) extern "C" { #endif ... #if defined(__cplusplus) } #endif
対策: before h2pas ツールに Remove C++ 'extern "C"' lines を加えます。
h2pas problem: 空のマクロ
将来の拡張のため、空のマクロを含んだヘッダファイルがあります:
#define MPI_FILE_DEFINED
対策: before h2pas ツールに Remove empty C macros を加えます。
h2pas problem: 暗黙の配列型
C は暗黙の配列 implicit array を引数として許しています。例えば:
int MPI_Group_range_incl(MPI_Group, int, int [][3], MPI_Group *);
ここで int [][3] が暗黙の配列型であり、Pascal では許されません。h2pas はポインタ型を加えるようサポートしますので、全ての [] を * に変換すればよいのです。
対策: before h2pas ツールに Replace [] with * を加えます。
h2pas problem: ヌルポインタのマクロ
型付きのヌルポインタを含むヘッダファイルがあります:
#define MPI_BOTTOM (void *)0
対策: before h2pas ツールに Replace macro values 0 pointer like (char *)0 with NULL を加えます。
コンパイルの試行と出力の整形
エラーなしに h2pas の走行が終了すると、Pascal ファイルが生成されます。ユニットファイルかインクルードファイル (.inc) かは -i スイッチで決められます。次のステップは試しにコンパイルしてみることです。今できた Pascal ソースを利用するよう、テスト用のプロジェクトを設定します。次いで、ウィザードの 'Run h2pas and compile' ボタンを押してください。
例: MPICH2
Project -> New Project -> Program で新しいプロジェクトを作り、mpihelloworld として保存します。コードを次のように変えます。
program MPIHelloWorld; {$mode objfpc}{$H+} {$linklib mpich} {$linklib rt} uses {$IFDEF UNIX}pthreads{$ENDIF}, MPI; begin MPI_Init(@argc, @argv); writeln('Hello, world.'); MPI_Finalize; end.
プロジェクトに h2p/mpi.pas を加えます。Project -> Add editor file to project を利用します。
h2pasの出力によくあるコンパイラエラー
h2pas が有効な Pascal コードを生成しないことがあります。よくある問題と修正法を挙げます。
ファイルパスを含んだユニット名
h2pas はユニット名に全パス名を含めてしまうことがあります。
対策: After h2pas ツールに Replace "unit filename;" with "unit name;" を加えます。
インクルードファイルが見当たらない
h2pas は #include 命令を Pascal の {$i } 命令に変換します。wach ヘッダファイルからユニットを生成する場合は、Remove includes ツールを用いて全ての include 命令を除去することができます。
先に宣言された型名が解決しない
例: mpi.pas(26,16) Error: Forward type not resolved "PMPI_Aint"
エラーを起こす行はしばしばレコード型へのポインタ型を含んでいます:
PMPI_Aint = ^MPI_Aint;
h2pas は無名の C ポインタ( *MPI_Aint のような)に PMPI_Aint のように名前をつけます。これらのポインタはファイルの先頭に加えられますが、Pascal はこのような宣言が同一の type 宣言部にあることを要求します(*)。また h2pas は既に存在するポインタ型を更に加えてしまうこともあります。
(訳注: 一般に Pascal では識別子は利用する前に宣言しておかなければなりません。下の例で、PARecord が宣言された時点では ARecord は宣言されていませんが、ポインタ型に限っては標準Pascal以来このような例外的な宣言が許されています。そうしないとリスト構造が構築できませんから。標準Pascalでは type 節は唯一でした。FPC では言語仕様が拡張され、複数の type 節が許容されますが、ここで言っているのは PARecord と ARecord の二つの型名が同一の type 節になければならないという点です。
type PARecord = ^ARecord; ARecord = record data : integer; next : PARecord end;
#定義の順序の問題参照)
対策: After h2pas ツールに Remove redefined pointer types を加えます。
system typeの除去
h2pas が加える system types の一部(例えば PLongint)は現在では system ユニットに含まれています。
対策: After h2pas ツールに Remove type redefinitons like PLongint を加えます。
空のtype/var/const節
上記のツールを利用すると、変数・型・定数がいくたりか削除されます。その結果空の節ができることがありますが、これは Pascal では許容されません。
対策: After h2pas ツールに Remove empty type/var/const sections を加えます。
暗黙の型
C は引数として例えば int [][3] のような暗黙の型 implicit type を許容します。Pascal はこれを許しませんので、それに名前を与えて型として宣言しなければなりません。h2pas はそこまでの処理を自動的に行うことができず、その代わりに Pascal もどきの構文に置き換えます。例えば:
int some_func(int [][3]);
は、こうなります
function some_func(_para1:array[0..2] of Plongint):longint;cdecl;external;
対策: 幸いなことに、この種の暗黙の型を除去し明示的な型を加える新たなツールができました。Replace implicit types を after ツールに組み込んでください。
Array of 無
時として、h2pas は C の引数省略 '...' を誤って 'array of )' に変換してしまいます。これは 'array of const)' でなければなりません。
対策: After h2pas ツールに Fix open arrays を加えます。
識別子が見つからない
Identifier not found というエラーが起きるのは次の三つの場合です:
定義の順序の問題
先に型を宣言する場合、それは同一の type 節の中である必要があります。例えば:
type TMyRecord = record Next: PMyRecord; // using the forward definition end; PMyRecord = ^TMyRecord;
下の例は許されません。異なる type 節に分かれているからです:
type TMyRecord = record Next: PMyRecord; // using the forward definition end; type PMyRecord = ^TMyRecord;
対策: コードの順序を入れ替えなければなりません。自動的にこれを行うツールはまだありません。
他のファイルで宣言された識別子
対策: uses 節にそのユニットを追加する。
追加するユニットが既にこのユニットを uses している場合、循環参照に陥ります。これは .h ファイルでは許されていますが、Pascal ユニットでは許されません。このような場合、コードをユニット間で移動するか、gtk2 バインディングのように IFDEF を用いる必要があります。自動的にこれらを行うツールはまだありません。
提案
次のようなツールを作れば、以上の問題はほとんど解決されます: バインディングに含まれる全ユニットの全ての定数を抽出し、一つの大きな constant 節に押し込む。型名についても同様にして、constant 節の後に置く。
不利な点: どの識別子がどの .h ファイルに含まれているのかわからなくなってしまいます。最低限コメントを加える必要があるでしょう。
未定義の識別子
不足しているヘッダファイルがあるか、ヴァージョンが違うと思われます。
対策: ヘッダファイルを探すか、その識別子をコメントにします。自動的にこれらを行うツールはまだありません。
Publish your bindings on lazarus-ccr or Free Pascal
ToDo
自分の変換ツールを書く
"Search and replace"ツールを使う
変数名の変更のような作業は Search and replace ツールで可能です。'Before h2pas' や 'After h2pas' ページにある Add new tool ボタンでツールを追加することができます。その後、SearchFor、ReplaceWith、Options、Caption といったプロパティを設定します。
例: 識別子 Tguint を guint に変更する
Property | Value |
---|---|
Caption | Rename Tguint to guint |
SearchFor | Tguint |
ReplaceWith | guint |
Options | [trtMatchCase,trtWholeWord] |
例: 複数の識別子を改名する
Tguint を guint、Tgulong を gulong、Tgint を gintに改名する:
Property | Value |
---|---|
Caption | Rename Tguint to guint |
SearchFor | gint|gulong) |
ReplaceWith | $1 |
Options | [trtMatchCase,trtWholeWord,trtRegExpr] |
今あるツールの改善
見つけたバグを修正したい、上記のツールを拡張したい、そいつは素晴らしい!
上記のツールのほとんどは h2pasconvert ユニットに定義されています。これは h2paswizard パッケージの一部です。こういったツールはクラス名、クラスの記述、Execute メソッドを必要とします。ツールを IDE の外でテスト/デバグすると、コンパイル時間を大いに短縮することができますが、その方法については components/simpleideintf/examples/testh2pastool.lpi プロジェクトをご覧ください。これをコンパイルしてコンソール/ターミナルで走らせます。最初のコマンドライン引数に .h ファイルの名前を与えます。例えば:
./testh2pastool files/h2pastest.pas
カスタムツールを書く
自分で変換ツールを書いて、容易に IDE に追加することができます。パッケージを開き新しいユニット(ここでは unit1 とします)を追加し、そのユニットに新しいクラスを書きます。既存のツールを参考にし、これまでのセクションをお読みください。新しいツールを書いて上記の simpleideintf プロジェクトでテストしたら、IDE に登録します: 追加したいユニット(ここでは unit1)に register 手続きを加えます。次に疑似コードを示します:
uses Classes, ..., IDETextConverter; type TYourToolClass = class(TCustomTextConverterTool) public class function ClassDescription: string; override; function Execute(aText: TIDETextConverter): TModalResult; override; end; procedure Register; implementation procedure Register; begin TextConverterToolClasses.RegisterClass(TYourToolClass); end;
忘れずにパッケージエディタの register チェックボックスをイネーブルしてください。さもないと Register 手続きは IDE から呼び出されません。次いで新しいパッケージを IDE にインストールします。
これからの作業/あるといいもの
- 複数の .h ファイルを一つのユニットに変換できるようにウィザードを拡張すること。循環参照の多くが自動的に解決されます。
- 未定義の識別子を見つけ出し、どれをコメントにするかチェックできるようにすること。
- 半ば翻訳されたマクロのリストを生成すること。