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
| Old | New | Line | |
|---|---|---|---|
| 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).* |
