Creating bindings for C libraries/ja

From Lazarus wiki
Jump to navigationJump to search

English (en) español (es) français (fr) 日本語 (ja) русский (ru) 中文(中国大陆) (zh_CN)

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報

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 typesafter ツールに組み込んでください。

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 ボタンでツールを追加することができます。その後、SearchForReplaceWithOptionsCaption といったプロパティを設定します。

例: 識別子 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 ファイルを一つのユニットに変換できるようにウィザードを拡張すること。循環参照の多くが自動的に解決されます。
  • 未定義の識別子を見つけ出し、どれをコメントにするかチェックできるようにすること。
  • 半ば翻訳されたマクロのリストを生成すること。