Sign in Join
Wiki copytemporary

Creating bindings for C libraries/ja: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Creating bindings for C libraries/ja

OldNewLine
1+# Cライブラリとのバインディング
2+
3+# 概略
4+
5+このページでは **Pascal から C ライブラリへのバインディング**について述べます。通常、Pascal からは C ライブラリを直接用いることができません。全ての C の関数、型、変数について Pascal への翻訳を作る必要があります。よくある C のあれこれを自動変換する [[H2Pas]] というツールがあります。自動変換には Lazarus 用の GUI もあり、H2Pas などのツールを用いています。このGUI ツールは、バインディングを修正するための規則集を作る作業を援助します。規則集を作ってしまえば、次からはずっと容易に C ライブラリからの変換ができるようになります。
6+
7+## 作業の流れ
8+
9+- 変換しようとする C ヘッダを探し出します。
10+- 作業ディレクトリを作成し、バインディングに名前をつけます。
11+- h2pas ウィザードを用いて、新しい h2pas プロジェクトを作成します。
12+- そのプロジェクトに .h ファイルを追加します。
13+- h2pas のオプションを設定します。
14+- ウィザードを走らせます。
15+- text tools を追加してエラーを修正し、ウィザードを再度走らせます。
16+- h2pas がエラーを出さずに走るようになったら、試しにコンパイルしてみます。きれいな出力が得られるように optional tools を追加します。
17+- テストプログラムを書いて、バインディングをテストします。
18+- できあがったバインディングは Lazarus-ccr や Free Pascal のウェブサイトで公開しましょう。
19+
20+# ツールのインストール
21+
22+h2pas は FPC を普通にインストールするとついてきます。
23+
24+h2paswizard パッケージを Lazarus IDE にインストールします。"Components -\> Configure installed packages ..." に行き、右側の一覧から H2PasWizard パッケージを選んで 'Install selection' をクリックし、次いで 'Save and rebuild IDE' を選びます。
25+
26+IDE を再起動すれば、メニュー項目に Tools -\> h2pas が追加されています。
27+
28+# C ヘッダファイルの取得
29+
30+C ヘッダファイル .h には C ライブラリのインタフェースが記述されています。通常の場合、それらはライブラリと同時には配布されません。ソースを得るか、ライブラリの開発用パッケージを入手するかする必要があります。たとえば、gtk ライブラリの C ヘッダファイルは gtk+-devel パッケージに入っています。
31+
32+### 例: MPICH2
33+
34+ファイル mpich2-1.0.3.tar.gz を <http://www-unix.mcs.anl.gov/mpi/mpich2/> からダウンロードし、展開します。C ヘッダファイルは mpich2-1.0.3/src/include にあります。
35+
36+# 作業ディレクトリの作成とバインディングの命名
37+
38+使いやすい名前でディレクトリを作成します。名前には特殊文字を含んではなりません。特殊文字は空白・ウムラウト・フルストップ・コンマなどです。そこに .h ファイルを複製します。
39+
40+### 例: MPICH2
41+
42+Pascal ファイルには h2p ディレクトリを用意します。h2p/c_sources ディレクトリを .h ファイルに用います。
43+
44+` mkdir -p h2p/c_sources`\
45+` cp mpich2-1.0.3/src/include/*.h h2p/c_sources/`
46+
47+(訳注: mkdir を -p オプション付きで呼んでいるので、h2p ディレクトリも同時に作成されます)
48+
49+# ウィザードによる新しいh2pasプロジェクトの作成
50+
51+"Tools -\> h2pas" として h2pas ウィザードを起動します。新しいウィンドウが開きます。このウィンドウと IDE ウィンドウの間は行ったり来たりすることができます。最後に用いた h2pas プロジェクトが自動的にロードされます。新しいプロジェクトを作成するには、"Settings -\> New/Clear settings" をクリックし、一番下のボタン "Save settings" を押し、ファイル名を選んでください。
52+
53+### 例: MPICH2
54+
55+"Settings -\> New/Clear settings" をクリック、次に最下部の "Save settings" をクリック、h2p/mpi2.h2p として保存します。
56+
57+# プロジェクトへの.hファイルの追加
58+
59+"C header files" のページで .h ファイルを追加/削除できます。また、一部のファイルだけを変換できるよう、イネーブル/ディセーブル enable/disable もできます。
60+
61+### 例: MPICH2
62+
63+"C header files -\> Add .h files ..." をクリック、"mpi.h" を選択。自動的にイネーブルされます。
64+
65+# h2pasオプションの設定
66+
67+"h2pas Options" で h2pas のパラメタを設定することができます。
68+
69+### 例: MPICH2
70+
71+- イネーブルするものは -e, -D, -p, -w これら以外は全てディセーブルします。
72+- -l library path は "mpich"。
73+- 出力拡張子は ".pas"。
74+- 出力ディレクトリは h2pas/ (デフォルト)なので、空白のままとします。
75+
76+# ウィザードの実行
77+
78+最下部のボタン "Run h2pas" を押します。これで、\<example\>.h ファイルが一時ファイル \<example\>.tmp.h にコピーされ、"Before h2pas" にリストされているツールが走行します。その後 h2pas が実行され、\<example\>.tmp.h が \<example\>.inc や \<example\>.pas あるいはなんであれ h2pas ページで設定した拡張子がつくファイルに変換されます。
79+
80+h2pas が文法エラーを見つけると、IDE が example.tmp.h ファイルを開き、エラーを起こしている行に飛びます。エラー行が特定できない場合は単に 'syntax error' を表示するだけのこともあります。よくある問題とありうる解決については後述します。
81+
82+### 例: MPICH2
83+
84+h2pas ウィザードは既にこのヘッダの変換に必要な特殊ツールを全て含んでいます。よって、h2pas はエラーなしに走ります。ですが、この時作成されるユニットはまだそのままでは使えません。先をお読みください。
85+
86+# h2pasがエラーを吐く場合
87+
88+よくあるC の構造で h2pas が認識しないものの一覧と、修正法を示します。
89+
90+## h2pas problem: extern "C"
91+
92+ヘッダファイルによっては、C++の名前空間ブロックを持っていることがあります:
93+
94+` #if defined(__cplusplus)`\
95+` extern "C" {`\
96+` #endif`\
97+` ...`\
98+` #if defined(__cplusplus)`\
99+` }`\
100+` #endif`
101+
102+対策: **before h2pas** ツールに **Remove C++ 'extern "C"' lines** を加えます。
103+
104+## h2pas problem: 空のマクロ
105+
106+将来の拡張のため、空のマクロを含んだヘッダファイルがあります:
107+
108+` #define MPI_FILE_DEFINED`
109+
110+対策: **before h2pas** ツールに **Remove empty C macros** を加えます。
111+
112+## h2pas problem: 暗黙の配列型
113+
114+C は暗黙の配列 implicit array を引数として許しています。例えば:
115+
116+` int MPI_Group_range_incl(MPI_Group, int, int [][3], MPI_Group *);`
117+
118+ここで int \[\]\[3\] が暗黙の配列型であり、Pascal では許されません。h2pas はポインタ型を加えるようサポートしますので、全ての \[\] を * に変換すればよいのです。
119+
120+対策: **before h2pas** ツールに **Replace \[\] with *** を加えます。
121+
122+## h2pas problem: ヌルポインタのマクロ
123+
124+型付きのヌルポインタを含むヘッダファイルがあります:
125+
126+` #define MPI_BOTTOM      (void *)0`
127+
128+対策: **before h2pas** ツールに **Replace macro values 0 pointer like (char *)0 with NULL** を加えます。
129+
130+# コンパイルの試行と出力の整形
131+
132+エラーなしに h2pas の走行が終了すると、Pascal ファイルが生成されます。ユニットファイルかインクルードファイル (.inc) かは -i スイッチで決められます。次のステップは試しにコンパイルしてみることです。今できた Pascal ソースを利用するよう、テスト用のプロジェクトを設定します。次いで、ウィザードの 'Run h2pas and compile' ボタンを押してください。
133+
134+### 例: MPICH2
135+
136+Project -\> New Project -\> Program で新しいプロジェクトを作り、**mpihelloworld** として保存します。コードを次のように変えます。
137+
138+ program MPIHelloWorld;
139+
140+ {$mode objfpc}{$H+}
141+ {$linklib mpich}
142+ {$linklib rt}
143+
144+ uses
145+ {$IFDEF UNIX}pthreads{$ENDIF}, MPI;
146+
147+ begin
148+ MPI_Init(@argc, @argv);
149+ writeln('Hello, world.');
150+ MPI_Finalize;
151+ end.
152+
153+プロジェクトに h2p/mpi.pas を加えます。Project -\> Add editor file to project を利用します。
154+
155+# h2pasの出力によくあるコンパイラエラー
156+
157+h2pas が有効な Pascal コードを生成しないことがあります。よくある問題と修正法を挙げます。
158+
159+## ファイルパスを含んだユニット名
160+
161+h2pas はユニット名に全パス名を含めてしまうことがあります。
162+
163+対策: **After h2pas** ツールに **Replace "unit filename;" with "unit name;"** を加えます。
164+
165+## インクルードファイルが見当たらない
166+
167+h2pas は \#include 命令を Pascal の {\$i } 命令に変換します。wach ヘッダファイルからユニットを生成する場合は、**Remove includes** ツールを用いて全ての include 命令を除去することができます。
168+
169+## 先に宣言された型名が解決しない
170+
171+例: **mpi.pas(26,16) Error: Forward type not resolved "PMPI_Aint"**
172+
173+エラーを起こす行はしばしばレコード型へのポインタ型を含んでいます:
174+
175+` PMPI_Aint = ^MPI_Aint;`
176+
177+h2pas は無名の C ポインタ( *MPI_Aint のような)に PMPI_Aint のように名前をつけます。これらのポインタはファイルの先頭に加えられますが、Pascal はこのような宣言が同一の **type** 宣言部にあることを要求します(*)。また h2pas は既に存在するポインタ型を更に加えてしまうこともあります。
178+
179+(訳注: 一般に Pascal では識別子は利用する前に宣言しておかなければなりません。下の例で、PARecord が宣言された時点では ARecord は宣言されていませんが、ポインタ型に限っては標準Pascal以来このような例外的な宣言が許されています。そうしないとリスト構造が構築できませんから。標準Pascalでは type 節は唯一でした。FPC では言語仕様が拡張され、複数の type 節が許容されますが、ここで言っているのは PARecord と ARecord の二つの型名が同一の type 節になければならないという点です。
180+
181+`type`\
182+`  PARecord = ^ARecord;`\
183+`  ARecord = record`\
184+`    data : integer;`\
185+`    next : PARecord`\
186+`  end;`
187+
188+#定義の順序の問題参照)
189+
190+対策: **After h2pas** ツールに **Remove redefined pointer types** を加えます。
191+
192+## system typeの除去
193+
194+h2pas が加える system types の一部(例えば PLongint)は現在では system ユニットに含まれています。
195+
196+対策: **After h2pas** ツールに **Remove type redefinitons like PLongint** を加えます。
197+
198+## 空のtype/var/const節
199+
200+上記のツールを利用すると、変数・型・定数がいくたりか削除されます。その結果空の節ができることがありますが、これは Pascal では許容されません。
201+
202+対策: **After h2pas** ツールに **Remove empty type/var/const sections** を加えます。
203+
204+## 暗黙の型
205+
206+C は引数として例えば *int \[\]\[3\]* のような暗黙の型 implicit type を許容します。Pascal はこれを許しませんので、それに名前を与えて型として宣言しなければなりません。h2pas はそこまでの処理を自動的に行うことができず、その代わりに Pascal もどきの構文に置き換えます。例えば:
207+
208+` int some_func(int [][3]);`
209+
210+は、こうなります
211+
212+` function some_func(_para1:array[0..2] of Plongint):longint;cdecl;external;`
213+
214+対策: 幸いなことに、この種の暗黙の型を除去し明示的な型を加える新たなツールができました。**Replace implicit types** を *after* ツールに組み込んでください。
215+
216+## Array of 無
217+
218+時として、h2pas は C の引数省略 '...' を誤って 'array of )' に変換してしまいます。これは 'array of const)' でなければなりません。
219+
220+対策: **After h2pas** ツールに **Fix open arrays** を加えます。
221+
222+## 識別子が見つからない
223+
224+Identifier not found というエラーが起きるのは次の三つの場合です:
225+
226+- 識別子はユニットの中で定義されているが、利用されるより後である。
227+- 識別子が他のユニット(他の .h file)で宣言されている。
228+- 識別子がどこにも定義されていない。
229+
230+### 定義の順序の問題
231+
232+先に型を宣言する場合、それは同一の type 節の中である必要があります。例えば:
233+
234+` type`\
235+`   TMyRecord = record`\
236+`     Next: PMyRecord; // using the forward definition`\
237+`   end;`\
238+`   PMyRecord = ^TMyRecord;`
239+
240+下の例は許されません。異なる type 節に分かれているからです:
241+
242+` type`\
243+`   TMyRecord = record`\
244+`     Next: PMyRecord; // using the forward definition`\
245+`   end;`\
246+` type`\
247+`   PMyRecord = ^TMyRecord;`
248+
249+対策: コードの順序を入れ替えなければなりません。自動的にこれを行うツールはまだありません。
250+
251+### 他のファイルで宣言された識別子
252+
253+対策: uses 節にそのユニットを追加する。
254+
255+追加するユニットが既にこのユニットを uses している場合、循環参照に陥ります。これは .h ファイルでは許されていますが、Pascal ユニットでは許されません。このような場合、コードをユニット間で移動するか、gtk2 バインディングのように IFDEF を用いる必要があります。自動的にこれらを行うツールはまだありません。
256+
257+#### 提案
258+
259+次のようなツールを作れば、以上の問題はほとんど解決されます: バインディングに含まれる全ユニットの全ての定数を抽出し、一つの大きな constant 節に押し込む。型名についても同様にして、constant 節の後に置く。
260+
261+不利な点: どの識別子がどの .h ファイルに含まれているのかわからなくなってしまいます。最低限コメントを加える必要があるでしょう。
262+
263+### 未定義の識別子
264+
265+不足しているヘッダファイルがあるか、ヴァージョンが違うと思われます。
266+
267+対策: ヘッダファイルを探すか、その識別子をコメントにします。自動的にこれらを行うツールはまだありません。
268+
269+# Publish your bindings on lazarus-ccr or Free Pascal
270+
271+ToDo
272+
273+# 自分の変換ツールを書く
274+
275+## "Search and replace"ツールを使う
276+
277+変数名の変更のような作業は **Search and replace** ツールで可能です。'Before h2pas' や 'After h2pas' ページにある **Add new tool** ボタンでツールを追加することができます。その後、**SearchFor**、**ReplaceWith**、**Options**、**Caption** といったプロパティを設定します。
278+
279+### 例: 識別子 Tguint を guint に変更する
280+
281+| **Property** | **Value** |
282+|--------------|-------------------------------|
283+| Caption | Rename Tguint to guint |
284+| SearchFor | Tguint |
285+| ReplaceWith | guint |
286+| Options | \[trtMatchCase,trtWholeWord\] |
287+
288+### 例: 複数の識別子を改名する
289+
290+Tguint を guint、Tgulong を gulong、Tgint を gintに改名する:
291+
292+| **Property** | **Value** |
293+|--------------|------------------------------------------|
294+| Caption | Rename Tguint to guint |
295+| SearchFor | T(guint\|gint\|gulong) |
296+| ReplaceWith | \$1 |
297+| Options | \[trtMatchCase,trtWholeWord,trtRegExpr\] |
298+
299+## 今あるツールの改善
300+
301+見つけたバグを修正したい、上記のツールを拡張したい、そいつは素晴らしい!
302+
303+上記のツールのほとんどは h2pasconvert ユニットに定義されています。これは h2paswizard パッケージの一部です。こういったツールはクラス名、クラスの記述、**Execute** メソッドを必要とします。ツールを IDE の外でテスト/デバグすると、コンパイル時間を大いに短縮することができますが、その方法については components/simpleideintf/examples/testh2pastool.lpi プロジェクトをご覧ください。これをコンパイルしてコンソール/ターミナルで走らせます。最初のコマンドライン引数に .h ファイルの名前を与えます。例えば:
304+
305+` ./testh2pastool files/h2pastest.pas`
306+
307+## カスタムツールを書く
308+
309+自分で変換ツールを書いて、容易に IDE に追加することができます。パッケージを開き新しいユニット(ここでは unit1 とします)を追加し、そのユニットに新しいクラスを書きます。既存のツールを参考にし、これまでのセクションをお読みください。新しいツールを書いて上記の simpleideintf プロジェクトでテストしたら、IDE に登録します: 追加したいユニット(ここでは unit1)に **register** 手続きを加えます。次に疑似コードを示します:
310+
311+ uses
312+ Classes, ..., IDETextConverter;
313+
314+ type
315+ TYourToolClass = class(TCustomTextConverterTool)
316+ public
317+ class function ClassDescription: string; override;
318+ function Execute(aText: TIDETextConverter): TModalResult; override;
319+ end;
320+
321+ procedure Register;
322+
323+ implementation
324+
325+ procedure Register;
326+ begin
327+ TextConverterToolClasses.RegisterClass(TYourToolClass);
328+ end;
329+
330+忘れずにパッケージエディタの **register** チェックボックスをイネーブルしてください。さもないと Register 手続きは IDE から呼び出されません。次いで新しいパッケージを IDE にインストールします。
331+
332+# これからの作業/あるといいもの
333+
334+- 複数の .h ファイルを一つのユニットに変換できるようにウィザードを拡張すること。循環参照の多くが自動的に解決されます。
335+- 未定義の識別子を見つけ出し、どれをコメントにするかチェックできるようにすること。
336+- 半ば翻訳されたマクロのリストを生成すること。
337+
338+---
339+*From the Free Pascal wiki, page "Creating bindings for C libraries/ja", as it stood on 2007-07-02 after a change by TheCreativeCAT, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history