Sign in Join
Wiki copytemporary

Creating bindings for C libraries/ja: what changed

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

OldNewLine
11# Cライブラリとのバインディング
22
33# 概略
44
55このページでは **Pascal から C ライブラリへのバインディング**について述べます。通常、Pascal からは C ライブラリを直接用いることができません。全ての C の関数、型、変数について Pascal への翻訳を作る必要があります。よくある C のあれこれを自動変換する [[H2Pas]] というツールがあります。自動変換には Lazarus 用の GUI もあり、H2Pas などのツールを用いています。このGUI ツールは、バインディングを修正するための規則集を作る作業を援助します。規則集を作ってしまえば、次からはずっと容易に C ライブラリからの変換ができるようになります。
66
77## 作業の流れ
88
99- 変換しようとする C ヘッダを探し出します。
1010- 作業ディレクトリを作成し、バインディングに名前をつけます。
1111- h2pas ウィザードを用いて、新しい h2pas プロジェクトを作成します。
1212- そのプロジェクトに .h ファイルを追加します。
1313- h2pas のオプションを設定します。
1414- ウィザードを走らせます。
1515- text tools を追加してエラーを修正し、ウィザードを再度走らせます。
1616- h2pas がエラーを出さずに走るようになったら、試しにコンパイルしてみます。きれいな出力が得られるように optional tools を追加します。
1717- テストプログラムを書いて、バインディングをテストします。
1818- できあがったバインディングは Lazarus-ccr や Free Pascal のウェブサイトで公開しましょう。
1919
2020# ツールのインストール
2121
2222h2pas は FPC を普通にインストールするとついてきます。
2323
2424h2paswizard パッケージを Lazarus IDE にインストールします。"Components -\> Configure installed packages ..." に行き、右側の一覧から H2PasWizard パッケージを選んで 'Install selection' をクリックし、次いで 'Save and rebuild IDE' を選びます。
2525
2626IDE を再起動すれば、メニュー項目に Tools -\> h2pas が追加されています。
2727
2828# C ヘッダファイルの取得
2929
3030C ヘッダファイル .h には C ライブラリのインタフェースが記述されています。通常の場合、それらはライブラリと同時には配布されません。ソースを得るか、ライブラリの開発用パッケージを入手するかする必要があります。たとえば、gtk ライブラリの C ヘッダファイルは gtk+-devel パッケージに入っています。
3131
3232### 例: MPICH2
3333
3434ファイル mpich2-1.0.3.tar.gz を <http://www-unix.mcs.anl.gov/mpi/mpich2/> からダウンロードし、展開します。C ヘッダファイルは mpich2-1.0.3/src/include にあります。
3535
3636# 作業ディレクトリの作成とバインディングの命名
3737
3838使いやすい名前でディレクトリを作成します。名前には特殊文字を含んではなりません。特殊文字は空白・ウムラウト・フルストップ・コンマなどです。そこに .h ファイルを複製します。
3939
4040### 例: MPICH2
4141
4242Pascal ファイルには h2p ディレクトリを用意します。h2p/c_sources ディレクトリを .h ファイルに用います。
4343
44-` mkdir -p h2p/c_sources`\
45-` cp mpich2-1.0.3/src/include/*.h h2p/c_sources/`
44+```text
45+ mkdir -p h2p/c_sources
46+ cp mpich2-1.0.3/src/include/*.h h2p/c_sources/
47+```
4648
4749(訳注: mkdir を -p オプション付きで呼んでいるので、h2p ディレクトリも同時に作成されます)
4850
4951# ウィザードによる新しいh2pasプロジェクトの作成
5052
5153"Tools -\> h2pas" として h2pas ウィザードを起動します。新しいウィンドウが開きます。このウィンドウと IDE ウィンドウの間は行ったり来たりすることができます。最後に用いた h2pas プロジェクトが自動的にロードされます。新しいプロジェクトを作成するには、"Settings -\> New/Clear settings" をクリックし、一番下のボタン "Save settings" を押し、ファイル名を選んでください。
5254
5355### 例: MPICH2
5456
5557"Settings -\> New/Clear settings" をクリック、次に最下部の "Save settings" をクリック、h2p/mpi2.h2p として保存します。
5658
5759# プロジェクトへの.hファイルの追加
5860
5961"C header files" のページで .h ファイルを追加/削除できます。また、一部のファイルだけを変換できるよう、イネーブル/ディセーブル enable/disable もできます。
6062
6163### 例: MPICH2
6264
6365"C header files -\> Add .h files ..." をクリック、"mpi.h" を選択。自動的にイネーブルされます。
6466
6567# h2pasオプションの設定
6668
6769"h2pas Options" で h2pas のパラメタを設定することができます。
6870
6971### 例: MPICH2
7072
7173- イネーブルするものは -e, -D, -p, -w これら以外は全てディセーブルします。
7274- -l library path は "mpich"。
7375- 出力拡張子は ".pas"。
7476- 出力ディレクトリは h2pas/ (デフォルト)なので、空白のままとします。
7577
7678# ウィザードの実行
7779
7880最下部のボタン "Run h2pas" を押します。これで、\<example\>.h ファイルが一時ファイル \<example\>.tmp.h にコピーされ、"Before h2pas" にリストされているツールが走行します。その後 h2pas が実行され、\<example\>.tmp.h が \<example\>.inc や \<example\>.pas あるいはなんであれ h2pas ページで設定した拡張子がつくファイルに変換されます。
7981
8082h2pas が文法エラーを見つけると、IDE が example.tmp.h ファイルを開き、エラーを起こしている行に飛びます。エラー行が特定できない場合は単に 'syntax error' を表示するだけのこともあります。よくある問題とありうる解決については後述します。
8183
8284### 例: MPICH2
8385
8486h2pas ウィザードは既にこのヘッダの変換に必要な特殊ツールを全て含んでいます。よって、h2pas はエラーなしに走ります。ですが、この時作成されるユニットはまだそのままでは使えません。先をお読みください。
8587
8688# h2pasがエラーを吐く場合
8789
8890よくあるC の構造で h2pas が認識しないものの一覧と、修正法を示します。
8991
9092## h2pas problem: extern "C"
9193
9294ヘッダファイルによっては、C++の名前空間ブロックを持っていることがあります:
9395
94-` #if defined(__cplusplus)`\
95-` extern "C" {`\
96-` #endif`\
97-` ...`\
98-` #if defined(__cplusplus)`\
99-` }`\
100-` #endif`
96+```text
97+ #if defined(__cplusplus)
98+ extern "C" {
99+ #endif
100+ ...
101+ #if defined(__cplusplus)
102+ }
103+ #endif
104+```
101105
102106対策: **before h2pas** ツールに **Remove C++ 'extern "C"' lines** を加えます。
103107
104108## h2pas problem: 空のマクロ
105109
106110将来の拡張のため、空のマクロを含んだヘッダファイルがあります:
107111
108-` #define MPI_FILE_DEFINED`
112+```text
113+ #define MPI_FILE_DEFINED
114+```
109115
110116対策: **before h2pas** ツールに **Remove empty C macros** を加えます。
111117
112118## h2pas problem: 暗黙の配列型
113119
114120C は暗黙の配列 implicit array を引数として許しています。例えば:
115121
116-` int MPI_Group_range_incl(MPI_Group, int, int [][3], MPI_Group *);`
122+```text
123+ int MPI_Group_range_incl(MPI_Group, int, int [][3], MPI_Group *);
124+```
117125
118126ここで int \[\]\[3\] が暗黙の配列型であり、Pascal では許されません。h2pas はポインタ型を加えるようサポートしますので、全ての \[\] を * に変換すればよいのです。
119127
120128対策: **before h2pas** ツールに **Replace \[\] with *** を加えます。
121129
122130## h2pas problem: ヌルポインタのマクロ
123131
124132型付きのヌルポインタを含むヘッダファイルがあります:
125133
126-` #define MPI_BOTTOM      (void *)0`
134+```text
135+ #define MPI_BOTTOM (void *)0
136+```
127137
128138対策: **before h2pas** ツールに **Replace macro values 0 pointer like (char *)0 with NULL** を加えます。
129139
130140# コンパイルの試行と出力の整形
131141
132142エラーなしに h2pas の走行が終了すると、Pascal ファイルが生成されます。ユニットファイルかインクルードファイル (.inc) かは -i スイッチで決められます。次のステップは試しにコンパイルしてみることです。今できた Pascal ソースを利用するよう、テスト用のプロジェクトを設定します。次いで、ウィザードの 'Run h2pas and compile' ボタンを押してください。
133143
134144### 例: MPICH2
135145
136146Project -\> New Project -\> Program で新しいプロジェクトを作り、**mpihelloworld** として保存します。コードを次のように変えます。
137147
138148 program MPIHelloWorld;
139149
140150 {$mode objfpc}{$H+}
141151 {$linklib mpich}
142152 {$linklib rt}
143153
144154 uses
145155 {$IFDEF UNIX}pthreads{$ENDIF}, MPI;
146156
147157 begin
148158 MPI_Init(@argc, @argv);
149159 writeln('Hello, world.');
150160 MPI_Finalize;
151161 end.
152162
153163プロジェクトに h2p/mpi.pas を加えます。Project -\> Add editor file to project を利用します。
154164
155165# h2pasの出力によくあるコンパイラエラー
156166
157167h2pas が有効な Pascal コードを生成しないことがあります。よくある問題と修正法を挙げます。
158168
159169## ファイルパスを含んだユニット名
160170
161171h2pas はユニット名に全パス名を含めてしまうことがあります。
162172
163173対策: **After h2pas** ツールに **Replace "unit filename;" with "unit name;"** を加えます。
164174
165175## インクルードファイルが見当たらない
166176
167177h2pas は \#include 命令を Pascal の {\$i } 命令に変換します。wach ヘッダファイルからユニットを生成する場合は、**Remove includes** ツールを用いて全ての include 命令を除去することができます。
168178
169179## 先に宣言された型名が解決しない
170180
171181例: **mpi.pas(26,16) Error: Forward type not resolved "PMPI_Aint"**
172182
173183エラーを起こす行はしばしばレコード型へのポインタ型を含んでいます:
174184
175-` PMPI_Aint = ^MPI_Aint;`
185+```text
186+ PMPI_Aint = ^MPI_Aint;
187+```
176188
177189h2pas は無名の C ポインタ( *MPI_Aint のような)に PMPI_Aint のように名前をつけます。これらのポインタはファイルの先頭に加えられますが、Pascal はこのような宣言が同一の **type** 宣言部にあることを要求します(*)。また h2pas は既に存在するポインタ型を更に加えてしまうこともあります。
178190
179191(訳注: 一般に Pascal では識別子は利用する前に宣言しておかなければなりません。下の例で、PARecord が宣言された時点では ARecord は宣言されていませんが、ポインタ型に限っては標準Pascal以来このような例外的な宣言が許されています。そうしないとリスト構造が構築できませんから。標準Pascalでは type 節は唯一でした。FPC では言語仕様が拡張され、複数の type 節が許容されますが、ここで言っているのは PARecord と ARecord の二つの型名が同一の type 節になければならないという点です。
180192
181-`type`\
182-`  PARecord = ^ARecord;`\
183-`  ARecord = record`\
184-`    data : integer;`\
185-`    next : PARecord`\
186-`  end;`
193+``` pascal
194+type
195+ PARecord = ^ARecord;
196+ ARecord = record
197+ data : integer;
198+ next : PARecord
199+ end;
200+```
187201
188202#定義の順序の問題参照)
189203
190204対策: **After h2pas** ツールに **Remove redefined pointer types** を加えます。
191205
192206## system typeの除去
193207
194208h2pas が加える system types の一部(例えば PLongint)は現在では system ユニットに含まれています。
195209
196210対策: **After h2pas** ツールに **Remove type redefinitons like PLongint** を加えます。
197211
198212## 空のtype/var/const節
199213
200214上記のツールを利用すると、変数・型・定数がいくたりか削除されます。その結果空の節ができることがありますが、これは Pascal では許容されません。
201215
202216対策: **After h2pas** ツールに **Remove empty type/var/const sections** を加えます。
203217
204218## 暗黙の型
205219
206220C は引数として例えば *int \[\]\[3\]* のような暗黙の型 implicit type を許容します。Pascal はこれを許しませんので、それに名前を与えて型として宣言しなければなりません。h2pas はそこまでの処理を自動的に行うことができず、その代わりに Pascal もどきの構文に置き換えます。例えば:
207221
208-` int some_func(int [][3]);`
222+```text
223+ int some_func(int [][3]);
224+```
209225
210226は、こうなります
211227
212-` function some_func(_para1:array[0..2] of Plongint):longint;cdecl;external;`
228+``` pascal
229+ function some_func(_para1:array[0..2] of Plongint):longint;cdecl;external;
230+```
213231
214232対策: 幸いなことに、この種の暗黙の型を除去し明示的な型を加える新たなツールができました。**Replace implicit types** を *after* ツールに組み込んでください。
215233
216234## Array of 無
217235
218236時として、h2pas は C の引数省略 '...' を誤って 'array of )' に変換してしまいます。これは 'array of const)' でなければなりません。
219237
220238対策: **After h2pas** ツールに **Fix open arrays** を加えます。
221239
222240## 識別子が見つからない
223241
224242Identifier not found というエラーが起きるのは次の三つの場合です:
225243
226244- 識別子はユニットの中で定義されているが、利用されるより後である。
227245- 識別子が他のユニット(他の .h file)で宣言されている。
228246- 識別子がどこにも定義されていない。
229247
230248### 定義の順序の問題
231249
232250先に型を宣言する場合、それは同一の type 節の中である必要があります。例えば:
233251
234-` type`\
235-`   TMyRecord = record`\
236-`     Next: PMyRecord; // using the forward definition`\
237-`   end;`\
238-`   PMyRecord = ^TMyRecord;`
252+``` pascal
253+ type
254+ TMyRecord = record
255+ Next: PMyRecord; // using the forward definition
256+ end;
257+ PMyRecord = ^TMyRecord;
258+```
239259
240260下の例は許されません。異なる type 節に分かれているからです:
241261
242-` type`\
243-`   TMyRecord = record`\
244-`     Next: PMyRecord; // using the forward definition`\
245-`   end;`\
246-` type`\
247-`   PMyRecord = ^TMyRecord;`
262+``` pascal
263+ type
264+ TMyRecord = record
265+ Next: PMyRecord; // using the forward definition
266+ end;
267+ type
268+ PMyRecord = ^TMyRecord;
269+```
248270
249271対策: コードの順序を入れ替えなければなりません。自動的にこれを行うツールはまだありません。
250272
251273### 他のファイルで宣言された識別子
252274
253275対策: uses 節にそのユニットを追加する。
254276
255277追加するユニットが既にこのユニットを uses している場合、循環参照に陥ります。これは .h ファイルでは許されていますが、Pascal ユニットでは許されません。このような場合、コードをユニット間で移動するか、gtk2 バインディングのように IFDEF を用いる必要があります。自動的にこれらを行うツールはまだありません。
256278
257279#### 提案
258280
259281次のようなツールを作れば、以上の問題はほとんど解決されます: バインディングに含まれる全ユニットの全ての定数を抽出し、一つの大きな constant 節に押し込む。型名についても同様にして、constant 節の後に置く。
260282
261283不利な点: どの識別子がどの .h ファイルに含まれているのかわからなくなってしまいます。最低限コメントを加える必要があるでしょう。
262284
263285### 未定義の識別子
264286
265287不足しているヘッダファイルがあるか、ヴァージョンが違うと思われます。
266288
267289対策: ヘッダファイルを探すか、その識別子をコメントにします。自動的にこれらを行うツールはまだありません。
268290
269291# Publish your bindings on lazarus-ccr or Free Pascal
270292
271293ToDo
272294
273295# 自分の変換ツールを書く
274296
275297## "Search and replace"ツールを使う
276298
277299変数名の変更のような作業は **Search and replace** ツールで可能です。'Before h2pas' や 'After h2pas' ページにある **Add new tool** ボタンでツールを追加することができます。その後、**SearchFor**、**ReplaceWith**、**Options**、**Caption** といったプロパティを設定します。
278300
279301### 例: 識別子 Tguint を guint に変更する
280302
281303| **Property** | **Value** |
282304|--------------|-------------------------------|
283305| Caption | Rename Tguint to guint |
284306| SearchFor | Tguint |
285307| ReplaceWith | guint |
286308| Options | \[trtMatchCase,trtWholeWord\] |
287309
288310### 例: 複数の識別子を改名する
289311
290312Tguint を guint、Tgulong を gulong、Tgint を gintに改名する:
291313
292314| **Property** | **Value** |
293315|--------------|------------------------------------------|
294316| Caption | Rename Tguint to guint |
295317| SearchFor | T(guint\|gint\|gulong) |
296318| ReplaceWith | \$1 |
297319| Options | \[trtMatchCase,trtWholeWord,trtRegExpr\] |
298320
299321## 今あるツールの改善
300322
301323見つけたバグを修正したい、上記のツールを拡張したい、そいつは素晴らしい!
302324
303325上記のツールのほとんどは h2pasconvert ユニットに定義されています。これは h2paswizard パッケージの一部です。こういったツールはクラス名、クラスの記述、**Execute** メソッドを必要とします。ツールを IDE の外でテスト/デバグすると、コンパイル時間を大いに短縮することができますが、その方法については components/simpleideintf/examples/testh2pastool.lpi プロジェクトをご覧ください。これをコンパイルしてコンソール/ターミナルで走らせます。最初のコマンドライン引数に .h ファイルの名前を与えます。例えば:
304326
305-` ./testh2pastool files/h2pastest.pas`
327+```text
328+ ./testh2pastool files/h2pastest.pas
329+```
306330
307331## カスタムツールを書く
308332
309333自分で変換ツールを書いて、容易に IDE に追加することができます。パッケージを開き新しいユニット(ここでは unit1 とします)を追加し、そのユニットに新しいクラスを書きます。既存のツールを参考にし、これまでのセクションをお読みください。新しいツールを書いて上記の simpleideintf プロジェクトでテストしたら、IDE に登録します: 追加したいユニット(ここでは unit1)に **register** 手続きを加えます。次に疑似コードを示します:
310334
311335 uses
312336 Classes, ..., IDETextConverter;
313337
314338 type
315339 TYourToolClass = class(TCustomTextConverterTool)
316340 public
317341 class function ClassDescription: string; override;
318342 function Execute(aText: TIDETextConverter): TModalResult; override;
319343 end;
320344
321345 procedure Register;
322346
323347 implementation
324348
325349 procedure Register;
326350 begin
327351 TextConverterToolClasses.RegisterClass(TYourToolClass);
328352 end;
329353
330354忘れずにパッケージエディタの **register** チェックボックスをイネーブルしてください。さもないと Register 手続きは IDE から呼び出されません。次いで新しいパッケージを IDE にインストールします。
331355
332356# これからの作業/あるといいもの
333357
334358- 複数の .h ファイルを一つのユニットに変換できるようにウィザードを拡張すること。循環参照の多くが自動的に解決されます。
335359- 未定義の識別子を見つけ出し、どれをコメントにするかチェックできるようにすること。
336360- 半ば翻訳されたマクロのリストを生成すること。
337361
338362---
339363*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