PERLAPI(1) USER COMMANDS PERLAPI(1) NAME perlapi - Perl 5 の C 拡張のアプリケーションプログラミングイ ンタフェース DESCRIPTION 導入 XS は、Perl と Perl といっしょに使おうとする C ライブラリの 間の拡張インタフェースを作るための言語です。 XS インタフェ ースは、Perl とリンクすることのできる、新しいライブラリを作 るためのライブラリと組み合わせて使われます。 XSUB は、XS 言 語内の関数で、Perl アプリケーションインタフェースの中核を成 すものです。 XS コンパイラは、xsubpp と呼ばれています。 このコンパイラは、 実際には C の関数を装って XSUB に Perl の値を操作させるため に必要な構造を埋め込み、Perl に XSUB をアクセスさせるために 必要なグルー関数を作ります。 コンパイラは、C 関数の引数や変 数を Perl の値にマッピングする方法を決めるために、typemap を 使用します。 デフォルトの typemap では、多くの一般的な C の 型を扱っています。 新たにリンクされるライブラリのための、特 殊な構造体や型を扱うときには、補助的な typemap を作らなけれ ばなりません。 以下の多くの例では、Perl と ONC+RPC 結合ライブラリ関数のイン タフェースの作成に焦点をおいています。 特に、rpcb_gettime() 関数が、XS 言語の多くの機能を示すために用いられています。 この関数は、2 つの引数をとります。 一つめの引数が入力用で、 ふたつめの引数が出力用です。 この関数はまた、ステータス値を 返します。 bool_t rpcb_gettime(const char *host, time_t *timep); C からは、以下のような形でこの関数が呼ばれます。 #include <rpc/rpc.h> bool_t status; time_t timep; status = rpcb_gettime( "localhost", &timep ); この関数と Perl の間で直接変換を行なうための XSUB を作れば、 次のようなコードで、Perl からその XSUB が使われます。 変数 $status と変数 $timep に、この関数の出力が得られます。 use RPC; $status = rpcb_gettime( "localhost", $timep ); 以下に示す XS ファイルは、rpcb_gettime() 関数へのインタフェ ースの一例となる XS サブルーティン (XSUB のこと) を示してい ます。 この XSUB が C と Perl の間の直接変換を行ない、Perl からのインタフェースも確保します。 XSUB は上に示したような 使い方で、Perl から起動されます。 XS ファイルではいつも、最 Perl manpages Last change: Release 5.0 Patchlevel 00 1 PERLAPI(1) USER COMMANDS PERLAPI(1) 初に EXTERN.h、perl.h、XSUB.h のための 3 つの #include 文を 指定しないといけません。 この方法や他の方法の拡張は、後述し ます。 #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <rpc/rpc.h> MODULE = RPC PACKAGE = RPC bool_t rpcb_gettime(host,timep) char * host time_t &timep OUTPUT: timep XSUB を使うものを含めて、どんな Perl への拡張も、ブートスト ラップのように起動時に、その拡張を Perl に組み込む Perl モジ ュールを用意するようになっています。 このモジュールが、拡張 機能や変数を Perl プログラムにエクスポートし、拡張のための XSUB を Perl に組み込みます。 以下のモジュールは、このドキ ュメントのほとんどの例で使われ、前に示したように Perl から use コマンドで使われるはずです。 Perl モジュールについては、 このドキュメント内でも、もう少し詳しく触れます。 package RPC; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw( rpcb_gettime ); bootstrap RPC; 1; このドキュメントを通じて、さまざまな rpcb_gettime() XSUB へ のインタフェースを研究しています。 XSUB は、場合によって、 順番や数の異なる引数をとるようになっています。 どの場合にも、 XSUB は、Perl と実際の C の rpcb_gettime() 関数の間を取り持 つもので、XSUB が実際の rpcb_gettime() 関数を常に正しい引数 で呼ぶことを保証しなければなりません。 この抽象化された仲介 によって、プログラマは、C 関数にいっそう Perl 風なインタフェ ースを用意することができるようになっているのです。 XSUB 解剖学 以下の XSUB では、Perl プログラムから sin() という C のライ ブラリ関数をアクセスできるようにしています。 この XSUB は、 一つの引数をとり、一つの値を返す C の関数を模倣します。 Perl manpages Last change: Release 5.0 Patchlevel 00 2 PERLAPI(1) USER COMMANDS PERLAPI(1) double sin(x) double<tab>x コンパイラは、引数の名前と型の間に 1 つのタブがあることを期 待しており、型の前には空白は、あっても無くてもかまいません。 C のポインタを使うときには、上記の rpcb_gettime() 関数で示し たように、間接指定演算子 * は、型の一部と考えるべきであり、 アドレス演算子 & は、変数の一部と考えるべきです。 C の型に 関する修飾子と単項演算子の扱いについては、typemap の節も参照 してください。 関数の引数リストでは、開き括弧の後や、閉じ括弧の前に空白を入 れてはなりません。 誤った表記 正しい表記 double double sin( x ) sin(x) double x double x 関数名と関数型は、行を改めて書かなくてはなりません。 誤った表記 正しい表記 double sin(x) double double x sin(x) double x 引数スタック 引数スタックは、XSUB への引数として渡される値や XSUB からの 返却値を蓄えるために使用します。 実際には、すべての Perl の 関数が、スタック上の自分の範囲を決めて、同時にこのスタックに 値を蓄えています。 このドキュメントでは、アクティブな関数に 属するスタック上の最初の範囲を、その関数に対して位置 0 とい うようにして参照するようにします。 x をスタック上のその XSUB の部分の位置を示すものとするとき、 XSUB は、自分のスタック引数を ST(x) というマクロで参照できま す。 その関数の位置 0 は、XSUB には、ST(0) という形で伝えら れることになります。 XSUB への引数と XSUB からの返却値は、 いつも ST(0) の位置から置かれることになっています。 多くの 簡単なケースでは、xsubpp コンパイラは、typemap に入れる埋め 込みプログラムで、引数スタックを扱うために必要なコードを生成 します。 より複雑なケースでは、プログラマがそのコードを用意 しなくてはなりません。 Perl manpages Last change: Release 5.0 Patchlevel 00 3 PERLAPI(1) USER COMMANDS PERLAPI(1) RETVAL 変数 RETVAL 変数は、常に C のライブラリ関数の関数型にマッチするマ ジック変数です。 xsubpp コンパイラは、XSUB ごとに、この変数 を用意し、デフォルトでは、呼び出される C のライブラリ関数の 返却値を保持するためにこの変数を使用します。 簡単なケースで は、RETVAL の値は、Perl が XSUB の返却値として受け取れるよう に、引数スタックの ST(0) に置かれます。 XSUB の関数型を void とした場合には、この関数に対して、コン パイラは RETVAL 変数を用意しません。 PPCODE: ディレクティブ を使うときには、RETVAL 変数は必要ありません。 MODULE キーワード MODULE キーワードは、XS コードを開始し、定義される関数のパッ ケージを示すために使われます。 最初の MODULE キーワードの前 のテキストはすべて、C のコードとみなされ、透過的に出力に渡さ れます。 すべての XS モジュールには、XSUB をフックして、 Perl に渡すために使われる bootstrap 関数を用意します。 この bootstrap 関数のパッケージ名は、XS ソースファイル中で最後の MODULE 文の値にマッチします。 MODULE の値は、一つの XS ファ イルの中では一定にしておくべきですが、そうしておかなければな らないと決められているものではありません。 以下の例は、XS コードを開始し、すべての関数を RPC というパッ ケージに置くようにするものです。 MODULE = RPC PACKAGE キーワード XS ソースファイル内で関数を、複数のパッケージに分けなければ ならないとき、PACKAGE キーワードが使われます。 このキーワー ドが使われるときには、MODULE キーワードとともに用い、その直 後に置かなければなりません。 MODULE = RPC PACKAGE = RPC [ パッケージ RPC の XS コード ] MODULE = RPC PACKAGE = RPCB [ パッケージ RPCB の XS コード ] MODULE = RPC PACKAGE = RPC [ パッケージ RPC の XS コード ] Perl manpages Last change: Release 5.0 Patchlevel 00 4 PERLAPI(1) USER COMMANDS PERLAPI(1) このキーワードは省略可能であり、冗長な情報となることもありま すが、いつも使用するようにした方が良いでしょう。 このキーワ ードを使うことによって、XSUB が目的のパッケージに入れられる ことを保証するのです。 PREFIX キーワード PREFIX キーワードは、Perl の関数名から取り除くプリフィクスを 指定するものです。 C の関数がrcpb_gettime() で、PRIFIX の値 が rcpb_ であれば、この関数は、Perl からは gettime() という ように見えます。 このキーワードが使われるときには、PACKAGE キーワードのあとに 置きます。 PACKAGE が使われなければ、MODULE キーワードのあ とに置きます。 MODULE = RPC PREFIX = rpc_ MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_ OUTPUT: キーワード OUTPUT: キーワードは、XSUB の終了時に、(新しい値が Perl から 見えるように) 特定の関数引数を更新しなければならないか、特定 の値を呼び出しもとの Perl 関数に返さなければならないことを示 します。 上記の sin() 関数のような簡単な関数では、RETVAL 変 数が、自動的に出力値として使われます。 より複雑な関数では、 xsubpp コンパイラがどの変数が出力変数であるかを判断するため に助けが必要となります。 このキーワードは、通常 CODE: キーワードを補足するために使い ます。 CODE: キーワードが存在するとき、RETVAL 変数が、出力 変数として認識されることはなくなります。 OUTPUT: キーワード を使用すると、コンパイラに RETVAL を実際に出力変数として使う ことを指示することになります。 OUTPUT: キーワードは、関数の引数が出力変数であることを示すた めにも使われます。 これは、その引数が関数内で変更されている ときに、更新結果を Perl から見えるようにするために必要となる ことがあります。 関数引数を RETVAL 変数といっしょに OUTPUT: の下に並べる場合には、RETVAL 変数は最後に置かないといけませ ん。 bool_t rpcb_gettime(host,timep) char * host time_t &timep OUTPUT: timep Perl manpages Last change: Release 5.0 Patchlevel 00 5 PERLAPI(1) USER COMMANDS PERLAPI(1) OUTPUT: キーワードはまた、出力引数を typemap ではなく、マッ チするコードに対応させるためにも使います。 bool_t rpcb_gettime(host,timep) char * host time_t &timep OUTPUT: timep<tab>sv_setnv(ST(1), (double)timep); CODE: キーワード このキーワードは、C の関数に対して特別な扱いを要求する、比較 的複雑な XSUB で使われます。 RETVAL 変数も使えますが、 OUTPUT: キーワードの下に指定して、実際に値を返すことを示さな ければなりません。 次の XSUB は、引数に特別な扱いが要求される C 関数のためのも のです。 Perl 側の用法をまず示します。 $status = rpcb_gettime( "localhost", $timep ); XSUB は以下の通りです。 bool_t rpcb_gettime(host,timep) char * host time_t timep CODE: RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL ここに示す多くの例で、CODE: ブロック (と他のブロック) が、中 括弧 ({ と }) で囲まれます。 これによって、CODE: ブロックを 複雑な INPUT typemap から保護し、結果の C のコードが正しいも のとなることを保証してくれます。 NO_INIT キーワード NO_INIT キーワードは、ある関数引数が、出力値としてだけ使われ ることを示すために用います。 xsubpp コンパイラは、通常、引 数スタックからすべての関数引数の値を読み取り、その関数のエン トリ上の C 変数へ、代入を行なうコードを生成します。 NO_INIT が指定されるとコンパイラは、ある引数が入力ではなく、出力に使 われ、関数が終了する前に処理されることを認識します。 次の例では、rpcb_gettime() 関数のバリエーションを示します。 この関数では、変数 timep を出力のみの変数として使用していて、 Perl manpages Last change: Release 5.0 Patchlevel 00 6 PERLAPI(1) USER COMMANDS PERLAPI(1) 初期状態の値には関与しません。 bool_t rpcb_gettime(host,timep) char * host time_t &timep = NO_INIT OUTPUT: timep 関数引数の初期化 関数の引数は、通常、引数スタックからの値で初期化されます。 typemap には、Perl の値を C の引数に変換するために使われる、 コードを入れています。 しかし、プログラマの方で、typemap を オーバライドして、別の初期化コードを与えることができます。 次のコードでは、関数引数の初期化コードの与え方を示します。 初期化コードは、出力に加えられる前に、コンパイラによって評価 されるので、ダブルクォートのようなものを文字どおりに解釈させ たいときには、バックスラッシュで保護しなければなりません。 bool_t rpcb_gettime(host,timep) char * host = (char *)SvPV(ST(0),na); time_t &timep = 0; OUTPUT: timep これは、引数のデフォルト値を指定するために、使うべきものでは ありません。 これは通常、関数引数を使用する前に、別のライブ ラリ関数で処理しなければならないときに使います。 デフォルト 引数については、次の節で扱います。 デフォルト引数値 関数の引数に対するデフォルト値は、引数リスト内に代入文を置く ことで、指定することができます。 デフォルト値としては、数値 か文字列を指定することができます。 デフォルトは、引数リスト の右側の引数から順にしか指定できません。 rpcb_gettime() に対する XSUB にデフォルトの host 値を持てる ようにするには、XSUB への引数を並べ替えるとよいでしょう。 その XSUB が、本当の rpcb_gettime() 関数を正しい引数順序で呼 ぶことになります。 Perl から、この XSUB を呼ぶときには、次 のどちらの形式でも使えることになります。 $status = rpcb_gettime( $timep, $host ); $status = rpcb_gettime( $timep ); Perl manpages Last change: Release 5.0 Patchlevel 00 7 PERLAPI(1) USER COMMANDS PERLAPI(1) XSUB は次のようなものになるでしょう。 CODE: ブロックが、本 当の rpcb_gettime() 関数を、正しい順序の引数で呼び出すために 使われます。 bool_t rpcb_gettime(timep,host="localhost") char * host time_t timep = NO_INIT CODE: RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL 可変長引数リスト 引数リストに省略記号 (...) を指定することで、XSUB は可変長の 引数リストを持てるようになります。 省略記号のこの使い方は、 ANSI C のものと同様です。 xsubpp コンパイラが、すべての XSUB に用意している変数 items を調べることで、XSUB に渡され た引数の数を知ることができます。 この仕組みを使って、大きさ のわからない引数のリストを受け取る XSUB を作ることができます。 rpcb_gettime() XSUB の host 引数は省略できますから、XSUB が 可変個数の引数をとれることを示すために、省略記号を使うことが できます。 Perl は、この XSUB を次のどちらかの形式で呼び出 すことができます。 $status = rpcb_gettime( $timep, $host ); $status = rpcb_gettime( $timep ); 省略記号を持つ XS コードです。 bool_t rpcb_gettime(timep, ...) time_t timep = NO_INIT CODE: { char *host = "localhost"; if( items > 1 ) host = (char *)SvPV(ST(1), na); RETVAL = rpcb_gettime( host, &timep ); } OUTPUT: timep RETVAL Perl manpages Last change: Release 5.0 Patchlevel 00 8 PERLAPI(1) USER COMMANDS PERLAPI(1) PPCODE: キーワード PPCODE: キーワードは、CODE: キーワードの別形式で、XSUB の返 却値を扱うために、引数スタックを制御するコードを用意している ことを xsubpp コンパイラに伝えるものです。 単独の値よりも、 値のリストを XSUB から返したいこともあるでしょう。 こういっ た場合には、PPCODE: を使って、明示的に値のリストをスタックに プッシュしなければなりません。 PPCODE: キーワードと CODE: キーワードは、同一の XSUB 内で同時に用いることはありません。 次の XSUB では、C の rpcb_gettime() 関数を呼び、この関数から の二つの出力値 timep と status を、一つのリストにして Perl に返しています。 void rpcb_gettime(host) char * host PPCODE: { time_t timep; bool_t status; status = rpcb_gettime( host, &timep ); EXTEND(sp, 2); PUSHs(sv_2mortal(newSVnv(status))); PUSHs(sv_2mortal(newSVnv(timep))); } 実際に rpcb_gettime() を呼ぶためのコードと、返却値を正しく引 数スタック上に置くためのコードを、用意しておかねばなりません。 この関数の型を void とすることで、RETVAL 変数が必要ないか、 使われないものであり、作る必要がないものであることを、xsubpp コンパイラに伝えることになります。 関数型 void は、ほとんど の場合、PPCODE: ディレクティブといっしょに使われるはずです。 引数スタック上に二つの返却値の場所を確保するために、EXTEND() マクロを使っています。 PPCODE: ディレクティブの指定によって、 xsubpp コンパイラが、sp というスタックポインタを作り、それが EXTEND() マクロ内で this ポインタとして使われます。 その後、 PUSHs() マクロで値を順にスタックに積みます。 これで、以下のようにして、Perl から rpcb_gettime() 関数を呼 ぶことができるようになりました。 ($status, $timep) = rpcb_gettime("localhost"); Undef 値や空リストを返す 関数の異常を示すために、状態変数を別に用意するよりも、単に undef 値や空リストを返したい場合があります。 rpcb_gettime() 関数を、そのような場合にあてはめてみましょう。 この関数がう Perl manpages Last change: Release 5.0 Patchlevel 00 9 PERLAPI(1) USER COMMANDS PERLAPI(1) まくいったときには、時刻を返し、問題があったときには、undef 値を返すようにしてみましょう。 次の Perl コードでは、$timep の値が undef か有効な時刻のどちらかになることになります。 $timep = rpcb_gettime( "localhost" ); 次の XSUB では、関数型に void を用いて、RETVAL 変数を作らな いようにし、CODE: ブロックを使って、必要なコードはすべて用意 していることをコンパイラに示しています。 sv_newmortal() の 呼び出しで返却値を undef で初期化し、これをデフォルトの返却 値としています。 void rpcb_gettime(host) char * host CODE: { time_t timep; bool_t x; ST(0) = sv_newmortal(); if( rpcb_gettime( host, &timep ) ) sv_setnv( ST(0), (double)timep); } 次の例では必要となったときに、明示的に undef 値を返却値とし て設定する方法を示します。 void rpcb_gettime(host) char * host CODE: { time_t timep; bool_t x; ST(0) = sv_newmortal(); if( rpcb_gettime( host, &timep ) ){ sv_setnv( ST(0), (double)timep); } else{ ST(0) = &sv_undef; } } 空リストを返すには、PPCODE: ブロックを使ったうえで、スタック 上に返却値を積まないでおきます。 Perl manpages Last change: Release 5.0 Patchlevel 00 10 PERLAPI(1) USER COMMANDS PERLAPI(1) void rpcb_gettime(host) char * host PPCODE: { time_t timep; if( rpcb_gettime( host, &timep ) ) PUSHs(sv_2mortal(newSVnv(timep))); else{ /* スタックに何も積まないので、 */ /* 暗黙のうちに空リストが返される。 */ } } CLEANUP: キーワード このキーワードは、XSUB が終了する前に、特別な後処理を必要と する場合に使われます。 CLEANUP: キーワードを使用する場合に は、XSUB 内の、どの CODE: ブロック、PPCODE: ブロック、OUTPUT: セクションよりも後になければなりません。 CLEANUP: ブロック として記述されるコードは、XSUB 内の最後の実行文として、追加 されます。 BOOT: キーワード BOOT: キーワードは、拡張モジュールの bootstrap 関数にコード を追加するために使用します。 bootstrap 関数は、xsubpp コン パイラによって作られ、普通は Perl に任意の XSUB を登録するた めに必要な実行文が納められています。 BOOT: キーワードを使う と、bootstrap 関数にさらにの実行文を付け加えるように xsubpp コンパイラに伝えることができます。 このキーワードは、最初の MODULE キーワード以降であれば、どこ でも使うことができ、行内に単独で使用します。 このキーワード 以降、最初の空行までが、コードを記述したブロックとなります。 BOOT: # bootstrap 関数の実行時に、 # 以下のメッセージが表示されます。 printf("Hello from the bootstrap!\n"); コメントや C プリプロセッサディレクティブを使用する CODE: ブロック、PPCODE: ブロック、BOOT: セクション、CLEANUP: ブロック内では、コメントや C プリプロセッサディレクティブを 記述することができます。 コンパイラは、プリプロセッサディレ クティブには触れずに渡し、コメント行を削除します。 行の最初 Perl manpages Last change: Release 5.0 Patchlevel 00 11 PERLAPI(1) USER COMMANDS PERLAPI(1) に # を置くことで、XSUB にコメントを入れることができます。 コメントが、C のプリプロセッサディレクティブに見えることの無 いように注意し、そのように解釈されないようにすべきです。 XS を C++ で使用する 関数が、C++ のメソッドとして定義されているときには、その最初 の引数はオブジェクトポインタとみなされます。 オブジェクトポ インタは、THIS という変数に入れられることになります。 対象 となるオブジェクトは、C++ の new() 関数で生成され、Perl の sv_setptrobj() マクロで bless されたものです。 Perl による オブジェクトの bless は、T_PTROBJ typemap で扱うことができま す。 メソッドが static で定義されているときには、その C++ の関数 を class::method() という構文で呼び出すことになります。 メ ソッドが static でないならば、その関数を THIS->method() とい う構文で呼び出します。 Perl 変数 これから、XSUB でどのように Perl の変数 $host をアクセスする かを示します。 perl_get_sv() という関数が、内部的に SV (ス カラ変数) といわれている変数へのポインタを得るために使用され ます。 変数の名前には、パッケージ名 RPC が付け加えられます ので、perl_get_sv() で $host がどのパッケージにあるかがわか ります。 パッケージ名がないときには、perl_get_sv() では、パ ッケージ main で変数を探すことになります。 それから、SV の 内容への char* ポインタを取得するため、マクロ SvPVX() を使っ て SV の被参照を行ないます。 void rpcb_gettime() PPCODE: { char *host; SV *hostsv; time_t timep; hostsv = perl_get_sv( "RPC::host", FALSE ); if( hostsv != NULL ){ host = SvPVX( hostsv ); if( rpcb_gettime( host, &timep ) ) PUSHs(sv_2mortal(newSVnv(timep))); } } Perl manpages Last change: Release 5.0 Patchlevel 00 12 PERLAPI(1) USER COMMANDS PERLAPI(1) この XSUB を呼ぶためには、次のような Perl コードを使います。 $RPC::host = "localhost"; $timep = rpcb_gettime(); 上記の例では、SV に C の char* が入っていましたが、Perl のス カラ変数には、数値やリファレンスも入れることができます。 SV に C の int が入っているときには、SV の被参照にマクロ SvIVX() を使い、C の double が入っているときには、SvNVX() を使います。 SV が Perl のリファレンスのときには、SV の被参照にマクロ SvRV() が使えます。 結果は実際の Perl の変数を指す別の SV となります。 これは、SvPVX()、SvNVX()、SvIVX() で被参照する ことができます。 次の XSUB では、SvRV() を使っています。 void rpcb_gettime() PPCODE: { char *host; SV *rv; SV *hostsv; time_t timep; rv = perl_get_sv( "RPC::host", FALSE ); if( rv != NULL ){ hostsv = SvRV( rv ); host = SvPVX( hostsv ); if( rpcb_gettime( host, &timep ) ) PUSHs(sv_2mortal(newSVnv(timep))); } } 次の Perl コードは、$MY::host へのリファレンスとなる、変数 $RPC::host を生成します。 変数 $MY::host には、使用するホ スト名が入っています。 $MY::host = "localhost"; $RPC::host = \$MY::host; $timep = rpcb_gettime(); perl_get_sv() の第 2 引数は、上の例に示したように、通常は FALSE にします。 この引数を TRUE とすると、変数が存在しない ときに、生成するようになります。 空の SV かも知れないものを 扱うために段階を踏むのでなければ、TRUE は使用すべきではあり ません。 XSUB では、Per の配列値、ハッシュ値、コード値をアクセスする ために、perl_get_av()、perl_get_hv()、perl_get_cv() を使うこ とができます。 Perl manpages Last change: Release 5.0 Patchlevel 00 13 PERLAPI(1) USER COMMANDS PERLAPI(1) インタフェースの戦略 Perl と C ライブラリの間のインタフェースを設計する段階では、 ほとんどの場合、C から XS への直訳で十分です。 そのインタフ ェースは、多くは C 風のものとなり、特に C の関数が引数を変更 するようなときには、直感的とはいえないものとなります。 もっ と Perl 風なインタフェースを作りたい場合には、インタフェース が、どちら寄りに傾くかを左右するような部分を特定するのに、以 下のような戦略をとると良いでしょう。 引数を変更する C 関数を見つけること。 こういった関数の XSUB は、Perl へリストを返すようにし、失敗時には undef や空リスト を返すようにすることができるでしょう。 C と XSUB の関数のみで使用される値を見つけなさい。 Perl 側 でその値の中身に触れる必要がないのであれば、その値を C から Perl へ変換する必要もありません。 C の関数の引数リストと返却値から、ポインタを探しなさい。 ポ インタによっては、XS で変数名に単項演算子 & を付けて扱うこと ができますし、型名に * 演算子を使わなければならないものもあ ります。 一般に、& 演算子で処理する方が簡単です。 C の関数で使われる構造体を特定しなさい。 多くの場合、そのよ うな構造体が bless されたオブジェクトとして Perl から扱える ように、T_PTROBJ typemap をこの構造体に使うとよいでしょう。 Perl モジュール Perl モジュールは、XS コードから作られた拡張ライブラリと、 Perl インタプリタとの間をつなぐ役割を果たします。 このモジ ュールを使って、Perl に拡張ライブラリの内容を知らせるのです。 モジュールの名前とパッケージは、ライブラリの名前に合わせた方 がよいでしょう。 以下は、あるONC+ RPC 結合ライブラリ関数を拡張する Perl モジ ュールです。 package RPC; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw( rpcb_gettime rpcb_getmaps rpcb_getaddr rpcb_rmtcall rpcb_set rpcb_unset ); bootstrap RPC; 1; @EXPORT リスト内にある関数が、RPC 拡張に含まれています。 RPC モジュールは、Exporter モジュールを使って、これらの関数 Perl manpages Last change: Release 5.0 Patchlevel 00 14 PERLAPI(1) USER COMMANDS PERLAPI(1) 名がモジュール外の Perl プログラムからも見えるようにしていま す。 DynaLoader モジュールによって、RPC モジュールは、拡張 ライブラリをブートストラップできるようにします。 この拡張機 能を実際にロードし、関数を使えるようにするために、 use RPC; という Perl の実行文を使います。 DynaLoader についてより詳 しくは、Perl のソースディレクトリの ext/DynaLoader にあるド キュメントを見てください。 Perl のオブジェクトと C の構造体 C の構造体を扱うときには、XS の型に T_PTROBJ か T_PRTREF を 選択する必要があります。 どちらの型も複雑なオブジェクトへの ポインタを扱うように設計されています。 T_PTRREF 型では、 Perl のオブジェクトが bless されていなくてもよいが、T_PTROBJ 型では、オブジェクトが bless されていなければなりません。 T_PTROBJ を使うことで、XSUB は Perl オブジェクトが期待する型 であることを確認するようになりますから、型チェックを行なうこ とができます。 次の XS コードでは、getnetconfigent() 関数を ONC TIRPC とい っしょに使っています。 getnetconfigent() 関数は、C の構造体 へのポインタを返すもので、以下のプロトタイプで示されるもので す。 この例では、C のポインタを Perl のリファレンスにする方 法を示すことにします。 Perl は、このリファレンスを bless さ れたオブジェクトへのポインタとみなして、このオブジェクトに対 してデストラクタを起動しようとします。 デストラクタは、 getnetconfigent() が使ったメモリを解放するために、XS ソース 内で記述しす。 XS でのデストラクタは、DESTROY で終わる名前 を持つ XSUB 関数を記述することで作ることができます。 XS デ ストラクタは、別の XSUB が malloc したメモリを、free するた めに使うことができます。 struct netconfig *getnetconfigent(const char *netid); struct netconfig のために typedef を行なっています。 Perl のオブジェクトは、C での型の名前に Ptr というタグを付けたも のにマッチする名前のクラスで bless され、その名前は、Perl の パッケージ名として使われるときには、空白を入れません。 デス トラクタは、そのオブジェクトの属するクラスに対応するクラスに 置かれ、Perl 側では DESTROY という名前で参照されますので、 PREFIX キーワードを使用して関数名の先頭部分をみないようにし ます。 Perl manpages Last change: Release 5.0 Patchlevel 00 15 PERLAPI(1) USER COMMANDS PERLAPI(1) typedef struct netconfig Netconfig; MODULE = RPC PACKAGE = RPC Netconfig * getnetconfigent(netid) char * netid MODULE = RPC PACKAGE = NetconfigPtr PREFIX = rpcb_ void rpcb_DESTROY(netconf) Netconfig * netconf CODE: printf("Now in NetconfigPtr::DESTROY\n"); free( netconf ); この例には、次のような typemap のエントリが必要になります。 拡張に際して、新しい typemap を設けるときには、typemap の節 の情報も参考にしてください。 TYPEMAP Netconfig * T_PTROBJ 今回の例は、 use RPC; $netconf = getnetconfigent("udp"); という Perl の文で使用されます。 Perl は $netconf で参照されるオブジェクトを消去するとき、そ のオブジェクトを用意された XSUB DESTROY 関数に送ります。 Perl では、このオブジェクトが C の構造体であって、Perl のオ ブジェクトではないと知るすべがありませんし、関与もしません。 この意味で、getnetconfigent() XSUB で作られたオブジェクトと、 普通の Perl のサブルーティンで作られたオブジェクトの区別はあ りません。 C のヘッダファイルと Perl h2xs コンパイラは、/usr/include の C のヘッダファイルを Perl の拡張モジュールに変換するために設計されています。 このコン パイラは、Perl ソースの ext ディレクトリの下に、ディレクトリ を作り、ここに Mekefile、Perl モジュール、XS ソースファイル、 MANIFEST ファイルを入れます。 次のコマンドは、<rpcsvc/rusers.h> ヘッダから、Rusers という 拡張モジュールを作ります。 h2xs rpcsvc/rusers Perl manpages Last change: Release 5.0 Patchlevel 00 16 PERLAPI(1) USER COMMANDS PERLAPI(1) Rusers 拡張モジュールがコンパイルされ、インストールされると、 C のヘッダにあった #define 文を Perl から参照するために、こ のモジュールを使うことができます。 use Rusers; print "RPC program number for rusers service: "; print &RUSERSPROG, "\n"; 新しい拡張モジュールの作成 h2xs コンパイラは、テンプレートのソースファイルと Mekefile を生成できます。 多くの拡張モジュールで、このテンプレートを 叩き台として使うことができます。 以下の例では、このドキュメ ントの RPC 関数を含む拡張モジュールを生成するために、どのよ うに h2xs を使うのかを示します。 この拡張モジュールでは、自動ロード関数を使用しておらず、定数 の定義もしていませんから、-A オプションを h2xs に付けていま す。 Perl のソースディレクトリで実行すると、h2xs コンパイラ は、ext/RPC ディレクトリを作成し、RPC.xs、RPC.pm、Makefile.PL、 MANIFEST というファイルをそこに置きます。 RPC 関数の XS コ ードは、RPC.xs ファイルに付け加えます。 RPC.pm の @EXPORT リストは、RPC.xs から、その関数をインクルードするように更新 しておきます。 h2xs -An RPC 動的ローディングを行なう拡張モジュールのコンパイルには、 make dynamic というコマンドを ext/RPC ディレクトリで実行します。 拡張モ ジュールを Perl のバイナリに静的にリンクする場合には、Perl のソースディレクトリの makefile (Makefile ではなく、makefile を使います) を編集して、ext/RPC/RPC.a を static_ext 変数に追 加します。 この変更を行なう前に、Perl を作ってないといけま せん。 makefile を更新したあとで、 make というコマンドを、Perl のソースディレクトリで実行します。 拡張モジュールを追加するために、Perl の Configure スクリプト を使うこともできます。 この場合、Perl を作成する前に、 Configure の実行に先立って、拡張モジュールを Perl のソースデ ィレクトリの ext ディレクトリに置いておく必要があります。 Configure を実行するときに、他の拡張モジュール共々、この拡張 モジュールが ext ディレクトリで発見されると、作成される拡張 モジュールのリストに加えられます。 make が実行されると、こ の拡張モジュールも、他の拡張モジュールといっしょに、組み込ま Perl manpages Last change: Release 5.0 Patchlevel 00 17 PERLAPI(1) USER COMMANDS PERLAPI(1) れることになります。 Configure は、拡張モジュールが、そのディレクトリ名と一致する XS ソースファイルを用意しているときに、その存在を認識します。 Configure は、Perl ソースディレクトリの MANIFEST にあげられ ている、すべての .SH ファイルを抽出した後で、拡張モジュール のディレクトリにも MANIFEST ファイルがあると、そのファイルで も .SH ファイルを探して抽出します。 その後、Perl のソースデ ィレクトリの Makefile が、拡張モジュールのディレクトリ名に一 致する XS ファイルを見つけると、そのディレクトリで make を実 行します。 typemap typemap は、xsubpp コンパイラが、C の関数の引数や値を Perl の値にマッピングするために使用する、コードを集めたものです。 typemap ファイルは、TYPEMAP、INPUT、OUTPUT というラベルを付 けた、3 つのセクションに分けることができます。 INPUT セクシ ョンは、Perl の値を、特定の型の C の変数に変換する方法を、コ ンパイラに伝えるものです。 OUTPUT セクションは、特定の型の C の値から、Perl が認識できる値に変換する方法をコンパイラに 伝えます。 TYPEMAP セクションでは、与えられた C の型を Perl の値にマッピングするために、どの INPUT コードと OUTPUT コー ドを使用するかを、コンパイラに教えます。 typemap のどのセク ションも、TYPEMAP、INPUT、OUTPUT のいずれかのキーワードで始 まることになります。 Perl のソースディレクトリの ext ディレクトリにあるデフォルト の typemap には、Perl の拡張モジュールで使用することができる 便利な型をたくさん入れてあります。 拡張モジュールによっては、 自分のディレクトリに、追加の typemap を定義して置いているも のもあります。 そのような追加の typemap は、デフォルトの typemap の INPUT と OUTPUT のマッピングを参照することができ ます。 xsubpp コンパイラでは、デフォルトの typemap のマッピ ングが、拡張モジュールの typemap でオーバライドすることが許 されています。 独自の typemap を必要とする拡張モジュールでは、ほとんどの場 合、typemap ファイルの TYPEMAP セクションだけが必要になりま す。 getnetconfigent() の例で示した独自 typemap は、拡張モ ジュールの typemap の使用例としては、典型的なものかもしれま せん。 この typemap は、C の構造体と T_PTROBJ typemap を等 しいものとして扱うために使われてます。 getnetconfigent() で 使った typemap を再掲します。 C の型は、XS の型とタブで区切 られ、C の単項演算子 * は、C の型名の一部とみなされます。 TYPEMAP Netconfig *<tab>T_PTROBJ Perl manpages Last change: Release 5.0 Patchlevel 00 18 PERLAPI(1) USER COMMANDS PERLAPI(1) EXAMPLES ファイル RPC.xs: いくつかの ONC+ RPC 結合ライブラリ関数への インタフェース #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <rpc/rpc.h> typedef struct netconfig Netconfig; MODULE = RPC PACKAGE = RPC void rpcb_gettime(host="localhost") char * host CODE: { time_t timep; ST(0) = sv_newmortal(); if( rpcb_gettime( host, &timep ) ) sv_setnv( ST(0), (double)timep ); } Netconfig * getnetconfigent(netid="udp") char * netid MODULE = RPC PACKAGE = NetconfigPtr PREFIX = rpcb_ void rpcb_DESTROY(netconf) Netconfig * netconf CODE: printf("NetconfigPtr::DESTROY\n"); free( netconf ); ファイル typemap: RPC.xs のための独自 typemap。 TYPEMAP Netconfig * T_PTROBJ Perl manpages Last change: Release 5.0 Patchlevel 00 19 PERLAPI(1) USER COMMANDS PERLAPI(1) ファイル RPC.pm: RPC 拡張のための Perl モジュール。 package RPC; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw(rpcb_gettime getnetconfigent); bootstrap RPC; 1; ファイル rpctest.pl: RPC 拡張のための Perl テストプログラム。 use RPC; $netconf = getnetconfigent(); $a = rpcb_gettime(); print "time = $a\n"; print "netconf = $netconf\n"; $netconf = getnetconfigent("tcp"); $a = rpcb_gettime("poplar"); print "time = $a\n"; print "netconf = $netconf\n"; AUTHOR Dean Roehrich <roehrich@cray.com> September 27, 1994 Perl manpages Last change: Release 5.0 Patchlevel 00 20