perlxs - XS言語リファレンスマニュアル
XSはPerlと(perlと一緒に使いたい)Cライブラリとの間の拡張インターフェースを 作るのに使われる言語です。XSインターフェースはライブラリとリンクされて、 Perlとリンクすることのできる新しいライブラリを生成します。 XSUBはXS言語における関数であり、また、Perl application interface のコアコンポーネントです。
XSコンパイラーはxsubppと呼ばれます。このコンパイラーはXSUBが 必要とするCの関数を隠す構造やPerlが値を扱ったりXSUBにアクセスす るために必要な糊(glue)を作るための、構造(constructs)を組み込みま す。このコンパイラーはtypemapsを使ってCの関数の引数とPerlに対 する値の変数とをどのようにマップするかを決定します。デフォルトの typemapは多くの標準的なCの型を扱います。補助typemapは、リンクさ れるライブラリのための特殊な構造体であるとか型を扱えるように作成 しなければなりません。
エクステンションの作成手順についてのチュートリアルはperlxstut を参照してください。
+Note: For many extensions, Dave Beazley's SWIG system provides a +significantly more convenient mechanism for creating the XS glue +code. See http: for more +information.
この後で出てくる例の多くは、PerlとONC+ RPC bind ライブラリ関数と の間のインターフェースを具体的に生成します。rpcb_gettime()という 関数が、XS言語の多くの機能を説明するために使われます。この関数は 二つの引数を取ります。最初のものは入力パラメータで、二番目のもの は出力パラメータです。関数の戻り値はステータスを表わす値です。
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 );
XSUBがこの関数とperlとの間の直接的な変換をするように作られていれ ば、このXSUBはPerlから以下のようにして使われます。$statusと$timep という変数は関数の出力を保持します。
use RPC;
$status = rpcb_gettime( "localhost", $timep );
以下に示すXSファイルはXSサブルーチン、もしくはrpcb_gettime()関数
に対して可能なインターフェースの一つをデモンストレーションする
XSUBの例です。このXSUBはCとPerlとの間の直接的な変換を表わし、ま
た、Perlからもインターフェースを保護します。このXSUBはPerlから、
先程の例のようにして呼び出されます。最初の三つの#include文、
EXTERN.h, perl.h, XSUB.h, は常にXSファイルの先頭に置か
れることに注意してください。このアプローチなどはこのドキュメント
の後のほうで説明します。
#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
Perlに対する任意のエクステンションは、このようなXSUBも含めて、
Perlにエクステンションを押し込むブートストラップとしてのPerlモジ
ュールを持つべきです。このモジュールはエクステンションの関数と変
数とをエクスポートし、PerlプログラムやPerlにリンクされるエクステ
ンションのXSUBを起動します。以下のモジュールはこのドキュメントの
ほとんどの例で使われるもので、(先に挙げた例のように)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関数の
間の抽象作用(abstraction)であり、かつ、XSUBは 常に実際の
rpcb_gettime()が正しいパラメーターで呼ばれることを
保証しなければなりません。この抽象作用はプログラマーが
Cに対するよりPerl的なインターフェースを作成することを許します。
以下に示すXSUBは、Perlプログラムに対してsin()と呼ばれるCの ライブラリ関数にアクセスすることを許します。このXSUBは 引数を一つ取り、一つの値を返すCの関数を模倣します。
double
sin(x)
double x
前述の関数 rpcb_gettime() のように、
Cのポインターを使うとき間接参照演算子 *は 型の一部とみなすべきで、アドレス演算子 &は変数の一部であると みなすべきです。
Cの型における単項演算子や修飾子(qualifiers)の扱いに関する詳細は
typemapに関するセクションを参照してください。
関数名と、その戻り値の型は別々の行に分けておかなければなりません。
間違い 正しい
double sin(x) double
double x sin(x)
double x
関数本体はインデントを付けたり、左寄せすることができます。 以下の例では関数の本体を左寄せしています。本ドキュメントにある ほとんどの例では本体にインデントを付けています。
正しい
double sin(x) double x
引数スタックはXSUBにパラメータとして渡される値やXSUBから返ってきた値を 格納するのに使われます。すべてのPerl関数は、その値をこのスタックに同時 に保持していて、それぞれこのスタック上の位置の範囲によって制限されます。 このドキュメントでは、アクティブな関数に所属するスタックの最初の位置は その関数からは位置 0として参照されます。
XSUBはそのスタックにある引数に対して ST(x) というマクロを使って、 xが示しているXSUBのスタック上にある位置の参照を行います。 関数に対する位置 0はST(0)となります。XSUBに対するパラメーターと XSUBから返される値は常にST(0)から始まります。多くの単純な場合においては、 xsubppコンパイラーはtypemapsにある埋め込みコード片(embedding code fragments)から引数スタックを扱うコードを生成します。 もっと複雑な場合には、プログラマーがコードを提供しなければなりません。
RETVALという変数はCのライブラリ関数が返す型に常にマッチするマジック変 数(magic variable)です。xsubppコンパイラーは各XSUBに対してこの変数 を提供し、デフォルトではCライブラリ関数が呼ばれたときの戻り値の型を保 持するのに使われます。単純な場合には、RETVALの値はPerlからXSUBの戻り 値として受け取ることのできる引数スタックのST(0)に置かれます。
XSUBの戻り値の方がvoidであった場合には、コンパイラーは その関数に対して変数 RETVALを提供しません。PPCODE:
ディレクティブ
を使った場合には、変数RETVALは(陽にそれを使わない限り)必要ありません。
PPCODE:ディレクティブを使わないのであれば、void な戻り値は CODE:ディレクティブがST(0)へ陽にセットするのに使われていたと
しても、値を返さないサブルーチンに対してのみ使うべきです。
このドキュメントの以前のものでは、そういった場合に戻り値としてvoidを 使うことを勧めていました。これはXSUBが本当に voidであるときに
segfalutsを起こす可能性があることが確認されました。このやり方は今では
使わないことが推奨されていて、将来のバージョンでサポートされなくなるで
しょう。こういった場合、戻り値 SV * を使います(現在 xsubppは ``truely-void'' な関数と``old-practice-declared-as-void'' な関数とを明確に
しようとするheuristicなコードが入っています。このためあなたのプログラ
ムはあなたが戻り値としてSV * を使わない限り、この heuristicsの振る 舞いに左右されます。)。
キーワード MODULE は XS コードの始まりと、定義する関数のパッケー ジを指定するのに使われます。キーワード MODULE よりも前にあるテキ ストはCプログラムとして扱われ、何の変更も加えられずに出力されま す。すべてのXSモジュールは、XSUBをPerlにフックするのに使われるブ ートストラップ関数を持ちます。このブートストラップ関数のパッケー ジ名は、XSのソースファイルにある最後のMODULE文の値に一致します。 MODULEの値は同じXSファイルの中では変更されないようにすべきです(が、 必須ではありません)。
以下の例はXSコードを開始し、すべての関数をRPCという名前のパッケ ージに置きます。
MODULE = RPC
XSソースファイルに関数がある場合には、キーワードPACKAGEを 使ってパッケージに分割しなければなりません。 このキーワードはMODULEキーワードと共に使われ、かつ、そのすぐ後 になければなりません。
MODULE = RPC PACKAGE = RPC
[ XS code in package RPC ]
MODULE = RPC PACKAGE = RPCB
[ XS code in package RPCB ]
MODULE = RPC PACKAGE = RPC
[ XS code in package RPC ]
このキーワードは省略可能であって、また、一部のケースにおいては 重複する情報となるにもかかわらず、常に使うべきものです。 このキーワードはXSUBが要求したパッケージにあることを 保証します。
キーワード PREFIXはPerlの関数名から取り除かれるべきプリフィクス
を指定するものです。C関数がrpcb_gettime() で、そのPREFIXの値 がrpcb_であったとすると、Perlはこの関数をC<gettime()>として見 ます。
このキーワードはキーワードPACKAGEを使った後にあるべきです。 PACKAGEが使われなければ、PREFIXはキーワード MODULE の後にあるべきです。
MODULE = RPC PREFIX = rpc_
MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_
キーワード OUTPUT:は 幾つかの関数パラメーターがXSUBが終了すると
きに更新されるべき(Perlのために新しい値を見えるようにする)もので
あることや、幾つかの値が呼び出し元のPerl関数に戻されるべきもので
あることを示します。前述の sin()のような単純な関数には、変数
RETVALは自動的にoutput valueに割り当てられます。もっと複雑な関数 では、xsubppコンパイラーはどの変数が 出力変数(output variable)
であるかを決めるための助けが必要です。
このキーワードは、通常 キーワード CODE: を完全にするために使われ ます。変数 RETVALは、キーワード CODE:が使われている場合には出力 変数としては認識されません。キーワード OUTPUTはこういった場合に コンパイラーにRETVALが本当に出力変数であることを教えるために使わ れます。
キーワード OUTPUT: はまた、関数のパラメーターが出力変数であると ということを示すのにも使えます。これは関数の中でパラメーターが変 更されたり、プログラマーが更新したことをPerlに教えたいといったと きに必要になります。
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep
キーワード OUTPUT: はまた、出力パラメーターを (typemapではなく) それにマッチするコード片にマッピングするのにも使えます。
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep sv_setnv(ST(1), (double)timep);
+xsubpp emits an automatic SvSETMAGIC() for all parameters in the +OUTPUT section of the XSUB, except RETVAL. This
is the usually desired +behavior, as it takes care of properly invoking
'set' magic on output +parameters (needed for hash or array element
parameters that must be +created if they didn't exist). If for some reason,
this behavior is +not desired, the OUTPUT section may contain a SETMAGIC: DISABLE line +to disable it for the remainder of the parameters in the OUTPUT
section. +Likewise, SETMAGIC: ENABLE can be used to reenable it for the +remainder of the OUTPUT section. See perlguts for more details +about 'set' magic. + =head2 The CODE: Keyword
このキーワードは、Cの関数に対して特殊な操作を必要とするようなよ り複雑なXSUBで使われます。変数RETVALを使うことはできますが、キー ワードOUTPUT:を使って陽に指定しない限りは値を戻すことはありませ ん。
以下に示す XSUBは パラメーターに対する特殊な操作を必要とするC関 数のためのものです。Perlでの使い方を最初に示します。
$status = rpcb_gettime( "localhost", $timep );
The XSUB follows. XSUBはこうです。
bool_t
rpcb_gettime(host,timep)
char *host
time_t timep
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
キーワード INIT: は、コンパイラーがCの関数の呼び出しを生成する前 にXSUBへ初期化ルーチンを挿入することを許します。キーワード CODE: と違って、このキーワードはコンパイラーのRETVALの扱いに何の影響も 及ぼします。
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
INIT:
printf("# Host is %s¥n", host );
OUTPUT:
timep
キーワード NO_INITは関数のパラメーターが出力値としてのみ使われる ことを示すのに使われます。xsubppコンパイラーは通常、すべての 関数パラメーターの値を引数スタックから読み取って、関数の入り口で Cの変数にそれを代入するようなコードを生成します。NO_INITは、コン パイラーに一部のパラメーターが入力としてではなく出力として使われ ることと、それらが関数を終了する前に操作されることを示します。
以下に示す例では、関数 rpcb_gettime() の変種を示します。この関数は
変数 timepを出力変数としてのみ使っており、その初期値は考慮しません。
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($arg,PL_na);
time_t &timep = 0;
OUTPUT:
timep
これはパラメーターに対するデフォルト値を設定するために使うべきで はありません。通常これは、使用するよりも前に他のライブラリ関数に よってパラメーターを処理しなければならないようなときに使われるで しょう。デフォルトパラメーターは次のセクションで説明します。
+If the initialization begins with =, then it is output on +the same line where the input variable is declared.
If the +initialization begins with ; or +, then it is output after +all of the input variables have been declared.
The = and ;
+cases replace the initialization normally supplied from the typemap. +For
the + case, the initialization from the typemap will preceed +the initialization
code included after the +. A global +variable, %v, is available for the truely rare case where +information from one
initialization is needed in another +initialization. + + bool_t +
rpcb_gettime(host,timep) + time_t &timep ;
/*¥$v{time}=@{[$v{time}=$arg]}*/ + char *host +
SvOK($v{time}) ? SvPV($arg,PL_na) : NULL; +
OUTPUT: + timep +
デフォルト値はパラメーターリスト中に代入文を置くことによって関数 パラメーターを指定できます。デフォルト値には数字か文字列を使えま す。デフォルト値は常に、最も右にあるパラメーターに対してのみ使う べきです。
rpcb_gettime() に対するXスBpがデフォルトの host の値を持つことが
できるように、XSUBに対するパラメーターの順序を変えます。このXSUB
は実際のrpcb_gettime() 関数を正しい順序の引数で呼び出します。
PerlはこのXSUBを以下に示すいずれかの文で呼び出します。
$status = rpcb_gettime( $timep, $host );
$status = rpcb_gettime( $timep );
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
キーワード PREINIT:は typemapが展開されるより前に余分の(extra)変 数を宣言します。もし変数が CODE:ブロックで宣言されていると、その 変数はtypemapのコードに従うことになります。これは、Cの構文エラー となる可能性があります。変数をtypemapコードより前に宣言することを 強制するために、変数をPREINIT:ブロックに置きます。キーワード PREINIT:はXSUBの中で複数回使うことができます。
以下の例は、等価であるけれども複雑なtypemapを使った場合には最初 の例が安全である、という例です。
bool_t
rpcb_gettime(timep)
time_t timep = NO_INIT
PREINIT:
char *host = "localhost";
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
正しいがエラーになりやすい例。
bool_t
rpcb_gettime(timep)
time_t timep = NO_INIT
CODE:
char *host = "localhost";
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
キーワード SCOPE: は特定のXSUBに対してスコーピングを許可するため に使います。許可された場合、XSUBはENTERとLEAVEを自動的に起動しま す。
複雑な型マッピングをサポートするために、typemapのエントリーが<PRE>
/*scope*/
</PRE>
のようなコメントを含むXUSBによって使われていれば、ス
コーピングはそのようなXSUBでは自動的に許可されます。
スコーピングを許可するには
SCOPE: ENABLE
スコーピングを禁止するには
SCOPE: DISABLE
とします。
XSUBのパラメーターは、通常XSUBに入った直後に評価されます。キーワ ード INPUT:によって、指定したパラメーターが少々遅れて評価するよ うにできます。キーワード INPUT:は一つのXSUBの中で複数回使うこと や、一つ以上の入力変数リストに使うことができます。このキーワード は キーワード PREINIT:と一緒に使われます。
以下の例では、入力パラメーター timepの評価を遅らせて、PREINIT の後で行います。
bool_t
rpcb_gettime(host,timep)
char *host
PREINIT:
time_t tt;
INPUT:
time_t timep
CODE:
RETVAL = rpcb_gettime( host, &tt );
timep = tt;
OUTPUT:
timep
RETVAL
次の例は各入力パラメーターの評価を遅らせます。
bool_t
rpcb_gettime(host,timep)
PREINIT:
time_t tt;
INPUT:
char *host
PREINIT:
char *h;
INPUT:
time_t timep
CODE:
h = host;
RETVAL = rpcb_gettime( h, &tt );
timep = tt;
OUTPUT:
timep
RETVAL
XSUBは、パラメーターリストで(...)という省略記法で指定すること
によって、可変長のパラメーターリストを取ることができます。この省
略記法の仕様はANSI Cにあるものと似ています。プログラマーはXSUBに
渡された引数の数を、xsubppコンパイラーがすべてのXSUBに提供する
itemsという変数をチェックすることによって決定することができま
す。この機構を使うことによって、長さが不定のパラメーターリストを
受け付ける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
PREINIT:
char *host = "localhost";
STRLEN n_a;
CODE:
if( items > 1 )
host = (char *)SvPV(ST(1), n_a);
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
+=head2 The C_ARGS: Keyword + +The C_ARGS: keyword allows creating of XSUBS
which have different +calling sequence from Perl than from C, without a
need to write +CODE: or CPPCODE: section. The contents of the C_ARGS:
paragraph is +put as the argument to the called C function without any
change. + +For example, suppose that C function is declared as + + symbolic
nth_derivative(int n, symbolic function, int flags); + +and
that the default flags are kept in a global C variable +default_flags. Suppose that you want to create an interface which +is called as + +
$second_deriv = $function->nth_derivative(2); + +To do
this, declare the XSUB as + + symbolic +
nth_derivative(function, n) + symbolic function + int n +
C_ARGS: + n, function, default_flags +
キーワード PPCODEは キーワード CODE:の代替であり、xsubppコン パイラーにプログラマーがXSUBの戻り値のための引数スタックを制御す るコードを提供しているということを指示するのに使われます。ある XSUBで、一つの値ではなく値のリストを返したいというときに必要にな ります。この場合PPCODE:を使わなければならず、また、値のリストを 陽にスタックへプッシュしなければなりません。PPCODE: と CODE:は同 一のXSUBで同時に使われることはありません。
次のXSUBはCの rpcb_gettime() 関数を呼び出し、二つの出力値(output
values) timepとstatusを、シングルリストとしてPerlに返します。
void
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
bool_t status;
PPCODE:
status = rpcb_gettime( host, &timep );
EXTEND(SP, 2);
PUSHs(sv_2mortal(newSViv(status)));
PUSHs(sv_2mortal(newSViv(timep)));
プログラマーは、rbcb_gettimeを実際に呼び出すコードと、戻り値を引 数スタックの適切な場所にプッシュするコードを提供しなければならな いことに注意してください。
この関数に対する戻り値の型 voidは、xsubppコンパイラーに変
数RETVALが必要ないとか、使われないので(RETVALを)生成すべきではな
いことを指示します。ほとんどの場合、戻り値の型 voidはPPCODE: ディレクティブと共に使うべきです。
引数スタックに二つの値を置くための場所を作るのに EXTEND()という
マクロが使われています。 PPCODE: ディレクティブはxsubppコンパ イラーに SP と呼ばれるスタックポインターを生成させ、このポイ
ンターはEXTEND()マクロに使われます。値のスタックに対するプッシュ
は、PUSHs()というマクロを使います。
関数 rpcb_gettime()
は、Perlから次のような文で使うことができます。
($status, $timep) = rpcb_gettime("localhost");
When handling output parameters with a PPCODE section, be sure to handle 'set' magic properly. See perlguts for details about 'set' magic.
ときとしてプログラマーには、関数が失敗したときに(戻り値とは)別に
ステータスを表わす値を返すよりは、単純に undefや空リストを返 したいというときがあるでしょう。rpcb_gettime()関数はまさにこうい
った状況を提示しています。関数が成功したときには私たちは関数が時
刻を返すことを望み、失敗したときにはundefを返して欲しいと望んで
います。以下に示すPerlプログラムでは、$timepの値はundefか正しい
時刻のいずれかになります。
$timep = rpcb_gettime( "localhost" );
以下のXSUBは、SV *を ニーモニック(mnemonic)のみの戻り値の型 として使っていて、CODE:
ブロックをコンパイラーに対してプログラマ
ーが必要なコードすべてを提供していることを示すために使っています。
sv_newmortal() の呼び出しは戻り値をundefで初期化して、それをデフ
ォルトの戻り値とします。
SV *
rpcb_gettime(host)
char * host
PREINIT:
time_t timep;
bool_t x;
CODE:
ST(0) = sv_newmortal();
if( rpcb_gettime( host, &timep ) )
sv_setnv( ST(0), (double)timep);
次の例は、戻り値の中で陽にundefをどのように置き、arise (発生する、起こる) する必要があるかという例です。
SV *
rpcb_gettime(host)
char * host
PREINIT:
time_t timep;
bool_t x;
CODE:
ST(0) = sv_newmortal();
if( rpcb_gettime( host, &timep ) ){
sv_setnv( ST(0), (double)timep);
}
else{
ST(0) = &PL_sv_undef;
}
空リストを返すには、PPCODE: ブロックを使わなければならず、 かつその後でスタックに戻り値をプッシュしてはいけません。
void
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
PPCODE:
if( rpcb_gettime( host, &timep ) )
PUSHs(sv_2mortal(newSViv(timep)));
else{
/* 何もスタックにプッシュされないので、空リストは */
/* 暗黙のうちに返されます。 */
}
一部には、上記のXSUBにおいて、最後の文へfall through するように
制御するよりは陽にreturnを含めるようにしたいと考える人もいる でしょう。そういった場合には、代わりに XSRETURN_EMPTYを使いま す。これはXSUBスタックが適切に調整されることを保証します。他の
XSRETURN マクロについては perlguts を参照し てください。
キーワード REQUIRE: は xsubppコンパイラーがXSモジュールを コンパイルするために最低限必要なバージョンを示すために使われます。 以下のような文のあるXSモジュールはバージョン 1.922以降のxsubpp でのみコンパイルできます。
REQUIRE: 1.922
このキーワードはXSUBがその終了前に特別なクリーンアップ手続きを必 要とする場合に使うことができます。キーワード CLEANUP: が使われる ときには、それはCODE:, PPCODE, OUTPUT:ブロックのいずれかに続いて いなければなりません。クリーンアップブロックのためのコードはXSUB にある最後のステートメントに付加えられます。
キーワードBOOT:はエクステンションのブートストラップ関数のために コードを追加するのに使われます。ブートストラップ関数はxsubpp コンパイラーによって生成され、通常はPerlに登録するようなXSUBに必 要な文を保持します。BOOT:キーワードによってプログラマーはコンパ イラーに対して、ブートストラップ関数にコードを追加することを指示 できます。
このキーワードは最初に キーワード MODULEが現れたあとの任意の場所で 使うことができ、その行にはこのキーワードのみがあるようにすべきです。 キーワードの後で最初に現れる空行がコードブロックの終端を 示します。
BOOT:
# 次のメッセージは、ブートストラップ関数が実行され
# たときに表示されます。
printf("Hello from the bootstrap!¥n");
キーワード VERSIONCHECK: は xsubppのオプションである
-versioncheck や -noversioncheck に相当します。このキーワ
ードはコマンドラインオプションをオーバーライドします。バージョン
チェックはデフォルトでは有効になっています。バージョンチェックが
有効になっている場合、XSモジュールはそのバージョンがPMモジュール
のバージョンとマッチするかどうかが検査されます。
バージョンチェックを有効にするには
VERSIONCHECK: ENABLE
バージョンチェックを無効にするには
VERSIONCHECK: DISABLE
キーワード PROTOTYPES:は xsubppの-prototypes や -noprototypes
といったオプションに相当します。このキーワードはコマンドラインオ
プションをオーバーライドします。デフォルトではプロトタイプが有効
になっています。プロトタイプが有効になっているとき、XSUBはPerlに
プロトタイプを与えます。このキーワードはXSモジュールの中で、モジ
ュールの異なった部分で何回でもプロトタイプを許可したり禁止したり できます。
プロトタイプを許可するには
PROTOTYPES: ENABLE
プロトタイプを禁止するには
PROTOTYPES: DISABLE
このキーワードは前述したキーワード PROTOTYPES: に似ていますが、 XSUBに対する特定のプロトタイプを使うのにxsubppを強制的に使う ことができます。このキーワードは他のすべてのプロトタイプオプショ ンやキーワードを上書き(override)しますが、カレントのXSUBにのみ影 響します。Perlのプロトタイプについては perlsub を 参照してください。
bool_t
rpcb_gettime(timep, ...)
time_t timep = NO_INIT
PROTOTYPE: $;$
PREINIT:
char *host = "localhost";
STRLEN n_a;
CODE:
if( items > 1 )
host = (char *)SvPV(ST(1), n_a);
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
ALIAS:というキーワードは、XSUBに対して二つ以上のユニークなPerlでの名
前を持たせ、また、起動されたときに使われている(そういったユニ
ークなPerlでの)名前を知るための手段を持たせます。Perlでの名前は fully-qualified
なパッケージ名とすることができます。それぞれの別
名はインデックスとして与えられます。コンパイラーは、使用される別
名のインデックスが格納されているC<ix>と呼ばれる変数をセットアッ プします。XSUBがixという名前と共に呼び出されたとき、その値は0 となります。
以下に挙げる例では、FOO::gettime() と BAR::getit() という 別名をこの関数のために作成します。
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
ALIAS:
FOO::gettime = 1
BAR::getit = 2
INIT:
printf("# ix = %d¥n", ix );
OUTPUT:
timep
+
+=head2 The INTERFACE: Keyword
+
+This keyword declares the current XSUB as a keeper of the given
+calling signature. If some text follows this keyword, it is
+considered as a list of functions which have this signature, and
+should be attached to XSUBs.
+
+Say, if you have 4 functions multiply(), divide(), add(), subtract() all
+having the signature
+
+ symbolic f(symbolic, symbolic);
+
+you code them all by using XSUB
+
+ symbolic
+ interface_s_ss(arg1, arg2)
+ symbolic arg1
+ symbolic arg2
+ INTERFACE:
+ multiply divide
+ add subtract
+
+The advantage of this approach comparing to ALIAS: keyword is that one
+can attach an extra function remainder() at runtime by using
+
+ CV *mycv = newXSproto("Symbolic::remainder",
+ XS_Symbolic_interface_s_ss, __FILE__, "$$");
+ XSINTERFACE_FUNC_SET(mycv, remainder);
+
+(This example supposes that there was no INTERFACE_MACRO: section,
+otherwise one needs to use something else instead of
+C<XSINTERFACE_FUNC_SET>.)
+
+=head2 The INTERFACE_MACRO: Keyword
+
+This keyword allows one to define an INTERFACE using a different way
+to extract a function pointer from an XSUB. The text which follows
+this keyword should give the name of macros which would extract/set a
+function pointer. The extractor macro is given return type, C<CV*>,
+and C<XSANY.any_dptr> for this C<CV*>. The setter macro is given cv,
+and the function pointer.
+
+The default value is C<XSINTERFACE_FUNC> and C<XSINTERFACE_FUNC_SET>.
+An INTERFACE keyword with an empty list of functions can be omitted if
+INTERFACE_MACRO keyword is used.
+
+Suppose that in the previous example functions pointers for
+multiply(), divide(), add(), subtract() are kept in a global C array
+C<fp[]> with offsets being C<multiply_off>, C<divide_off>, C<add_off>,
+C<subtract_off>. Then one can use
+
+ #define XSINTERFACE_FUNC_BYOFFSET(ret,cv,f) ¥
+ ((XSINTERFACE_CVT(ret,))fp[CvXSUBANY(cv).any_i32])
+ #define XSINTERFACE_FUNC_BYOFFSET_set(cv,f) ¥
+ CvXSUBANY(cv).any_i32 = CAT2( f, _off )
+
+in C section,
+
+ symbolic
+ interface_s_ss(arg1, arg2)
+ symbolic arg1
+ symbolic arg2
+ INTERFACE_MACRO:
+ XSINTERFACE_FUNC_BYOFFSET
+ XSINTERFACE_FUNC_BYOFFSET_set
+ INTERFACE:
+ multiply divide
+ add subtract
+
+in XSUB section.
このキーワードはXSモジュールに他のファイルを取り込むのに使われます。 取り込むファイルはXSコードを持つこともできます。 INCLUDE: は あるコマンドを実行してそこで生成されたXSコードを モジュールに取り込むこともできます。
Rpcb1.xsh というファイルは私たちのrpcb_gettime() 関数を 含んでいます。
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep
XSモジュールは、ファイルを取り込むためにINCLUDE:を使うことができます。
INCLUDE: Rpcb1.xsh
キーワード INCLUDE:に続くパラメーターが パイプ (|)を伴っていれば、
コンパイラーはそのパラメーターをコマンドとして解釈します。
INCLUDE: cat Rpcb1.xsh |
キーワード CASE: は XSUBが、それぞれが仮想的なXSUBとして扱うことの できる複数の部品を持つことを許可します。CASE: は大食い (greedy)で、 使われた場合には他の XSキーワードはCASE:の中になければなりません。 これはXSUBにある最初の CASE: より前に他のキーワードを置けないという ことであり、最後のCASEよりも後にあるものはその case に含まれるという ことです。
CASE: は XSUBのパラメーターや、ix ALIAS: 変数(キーワード ALIAS:を参照)、変数 itemsを通じて 切り替えることができます。最後の CASE: は、そこに結び付けられて
いる条件がない場合には defaultとなります。以下の例はCASE: が
ixによって x_gettime() という別名を持つ関数 rpcb_gettime()
を切り替えるものです。この関数がrpcb_gettime() として呼ばれた とき、そのパラメーターは通常、 (char *host, time_t *timep)で すが、x_gettime() として呼ばれた場合にはそのパラメーターは 反転して (time_t *timep, char *host)となります。
long
rpcb_gettime(a,b)
CASE: ix == 1
ALIAS:
x_gettime = 1
INPUT:
# 'a' is timep, 'b' is host
char *b
time_t a = NO_INIT
CODE:
RETVAL = rpcb_gettime( b, &a );
OUTPUT:
a
RETVAL
CASE:
# 'a' is host, 'b' is timep
char *a
time_t &b = NO_INIT
OUTPUT:
b
RETVAL
こういった関数は、以下に示すいずれかの文で呼び出すことができます。 引数リストが異なっていることに注意してください。
$status = rpcb_gettime( $host, $timep );
$status = x_gettime( $timep, $host );
単項演算子 & はコンパイラーに、それがCの関数を呼び出すときに参照
外し(dereference)すべきオブジェクトであることを教えます。これは
CODE:ブロックが使われてなくて、かつ、オブジェクトがポインター型
でないとき(オブジェクトがintかlongで、int*やC<long*>で ない)に使われます。
次のXSUBは正しくないCコードを生成してしまいます。xsubppコンパイ
ラーはこれを、(char *host, time_t timep) という引数を伴って
rpcb_gettime()を呼ぶコードに変換しますが、実際に
rpcb_gettime() がtimepパラメーターの型として要求しているの は time_tではなくtime_t* です。
bool_t
rpcb_gettime(host,timep)
char *host
time_t timep
OUTPUT:
timep
この問題は&演算子を使うことによって修正できます。xsubppコンパ イラーはこれによって、rpcb_gettime() を正しく
(char *host, time_t *timep)というパラメーターで呼ぶようなコー ドにします。これは&を付けることでなされるので、関数の呼び出し はrpcb_gettime(host, &timep)のように見えます。
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep
CプリプロセッサのディレクティブはBOOT:, PREINIT:, INIT:, CODE:, PPCODE:, CLEANUP: といったブロックなしに、関数の外側のときと同じ ように使えます。コメントはMODULEキーワードの後の任意の場所で使え ます。コンパイラーはプリプロセッサーディレクティブをいじらずにそ のまま出力し、コメント行は削除します。
コメントはXSUBに、行の最初にある空白でないキャラクタとして#を 使うことで追加できます。コメントをCのプリプロセッサーディレクテ
ィブと取り違えられないように注意してください。これを防ぐ単純な方 法は、#の前に空白を置くことです。
プリプロセッサのディレクティブを二つのバージョンの関数のうちの一 つを選ぶために使うのであれば、
#if ... version1
#else /* ... version2 */
#endif
を使うようにし、
#if ... version1
#endif
#if ... version2
#endif
は使わないでください。
これは、そうしなければxsubppがあなたが関数の重複した定義をしよう としていると信じてしまうからです。同様に、#else/#endif の前に空 白行を入れて、それが関数本体の一部とみなされないようにしてくださ い。
関数がC++のメソッドとして定義されているのであれば、その関数に対
する第一引数がオブジェクトポインターであると仮定されるでしょう。
このオブジェクトポインターは THIS と呼ばれる変数に格納されます。
オブジェクトはC++のnew()を使って作成されたものであるべきで、かつ、
sv_setref_pv() マクロによってPerlからblessされているべきものです。
Perlによるオブジェクトのblessingはtyepmapによって扱うことができ
ます。typemapの例はこのセクションの最後にあります。
メソッドが静的に定義されているのであれば、class::method()構文を
使ってC++関数を呼び出します。メソッドが静的でないのであれば、関 数は THIS->method() 構文を使って呼び出されます。
次の例では、以下のC++のクラスを使用します。
class color {
public:
color();
‾color();
int blue();
void set_blue( int );
private:
int c_blue;
};
blue() や set_blue()
といったメソッドに対するXSUBはクラス名を伴って 定義されますが、オブジェクト(THIS
もしくは“self”)に対する
パラメータは明示されておらず、リストにありません。
int
color::blue()
void
color::set_blue( val )
int val
両方の関数ともその第一引数としてオブジェクトを期待しています。
xsubppコンパイラーはTHISと指定されたメソッドを呼び出すのに
使われるオブジェクトを呼びます。このため、C++コード bule()、
set_blee() メソッドは以下のようにして呼び出されます。
RETVAL = THIS->blue();
THIS->set_blue( val );
関数名が DESTROY である場合、C++の delete関数が呼び出されて、
THISはそれに対するパラメータとして与えられます。
void
color::DESTROY()
この C++コードはdeleteを呼ぶでしょう。
delete THIS;
関数名がnewであった場合、動的にC++オブジェクトを作成するため にC++のnew関数が呼び出されます。XSUBはCLASSと呼ばれる変数
に保持されるクラス名が、第一引数として与えられることを期待します。
color *
color::new()
The C++ code will call new. C++コードはnewを呼び出します。
RETVAL = new color();
このC++の例を使うことのできるtypemapの例を示します。
TYPEMAP
color * O_OBJECT
OUTPUT
# Perlオブジェクトは、blessするためのパッケージ名を保持する
# char*である'CLASS'にblessされます。
O_OBJECT
sv_setref_pv( $arg, CLASS, (void*)$var );
INPUT
O_OBJECT
if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) )
$var = ($type)SvIV((SV*)SvRV( $arg ));
else{
warn( ¥"${Package}::$func_name() -- $var is not a blessed SV reference¥" );
XSRETURN_UNDEF;
}
PerlとCライブラリとの間のインターフェースをストレートにCからXSへ の変換とデザインした場合、大概はそれで十分です。インターフェース はしばしば非常にCに似たものとなり、時折、特にCの関数がそのパラメ ーターを変更するような場合には直感的でないものになります。プログ ラマーがもっとPerl的なインターフェースを作りたいと望んでいる場合 には、以下に示す戦略(strategy)がインターフェースのより重要な部分 を認識するための手助けとなるでしょう。
パラメーターの変更をするようなCの関数を見つける。こういった関数に 対する XSUB はPerlにリストを返したり、関数が失敗したときには空リ ストやundefを返すこともできます。
CとXSUBの関数それ自身でしか使われないような値を見つけ出します。 Perlがそういった値に対するアクセスを必要としないのであれば、その 値をCからPerlへ変換する必要はないということになります。
Cの関数に対する引数リストや戻り値の中にあるポインターを見つけ出 します。一部のポインターは、XSの中で 型名で *演算子 を使うことが 要求されることに対して、&単項演算子を変数名に付けて扱うことがで きます。一般的には &演算子と動作するのは簡単です。
Cの関数によって使われている構造体を見つけ出します。多くの場合、 こういった構造体に対して T_PTROBJ typemapを使うのが助けになるの で、(そのような構造体を) blessされたオブジェクトとしてPerlから扱 うことができます。
Cの構造体を扱うときには、XSの型としてT_PTROBJ か T_PTRREF のいずれかを選択すべきです。これら二つの型は複雑なオブジェクトへ のポインターを扱うためにデザインされました。T_PTRREF 型はT_PTROBJ 型がblessされたオブジェクトを要求するのに対して、blessされていな いPerlオブジェクトも使うことができます。T_PTROBJを使うことによって、 XSUB は Perl オブジェクトが期待する型であることを確認するようにな るので、型チェックを行なうことができます。
以下に示す XS コードは ONC+ TIRPC と共に使われた関数
getnetconfigent() です。関数 getnetconfigent()
はCの構造体へのポインターを返し、以
下にあるようなCのプロトタイプを持ちます。この例はどのようにしてC
のポインターをPerlの参照 (reference)にするかを示します。Perlはこ
の参照をblessされたオブジェクトへの参照とみなし、そしてオブジェ
クトに対するデストラクターの呼び出しを試みます。デストラクターは
XSのソースで、getnetconfigent() が使ったメモリを解放するために提
供されます。XSにあるデストラクターは、DESTROYで終わる名前の XSUB 関数を指定することで作成することができます。XS デストラクタ
ーは別のXSUBによって割り当てられた (malloc'd)メモリを解放するた
めに使うこともできます。
struct netconfig *getnetconfigent(const char *netid);
typedefは、struct netconfigを生成するためにあります。Perl のオブジェクトは
Cの型の名前とマッチするクラスにおいて(Ptrと いうタグが付加されて) blessされます。その名前は、Perlのパッケー
ジ名として使うのであれば空白を含むべきではありません。デストラク ターは
オブジェクトのクラスに対応するクラスに置かれて、キーワー ド
PREFIXはPerlが期待するようにワード DESTOROYの名前の切り詰め
(trim)るのに使われます。
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
この例は次のようなPerl文によって使われます。
use RPC;
$netconf = getnetconfigent("udp");
Perlが $netconfによって参照されるオブジェクトを始末する (destroy) とき、そのオブジェクトに (そのオブジェクトのための) XSUB DESTROY が送られます。PerlはそのオブジェクトがCの構造体なのか、Perlのオブ ジェクトでないのかを決定することも考慮することもできません。この 意味では、getnetconfigent() XSUB によって作られたオブジェクトと 通常のPerlサブルーチンによって作られたオブジェクトには違いはあり ません。
typemapはC関数の引数と値をPerlの値にマッピングするために xsubpp
コンパイラーによって使われるコード片の集合(collection)です。typemap
ファイルはTYPEMAP, INPUT, OUTPUTというラベルの付いた三 つのセクションから構成されます。
ラベルのついていない初期化セクションは、名前が陽に指定されたもの
でないのであればTYPEMAPであるかのように仮定されます。 INPUTセクションは、コンパイラー
に対してPerlの値をどのように(幾つかある)Cの型に変換するかを指示
します。OUTPUTセクションは、コンパイラーに対してどのようにしてC
の型をPerlが認識できる値に変換するのかを指示します。TYPEMAPセク
ションは、コンパイラーに対して指示されたCの型をPerlの値にマッピ
ングするのに使うべきINPUTセクションもしくはOUTPUTセクションにあ
るコード片を指示します。セクションラベルC<TYPEMAP>,INPUT,
OUTPUTは行の先頭におかれ、大文字でなければなりません。
Perlのソースの extディレクトリにあるデフォルトのtypemapはPerl
のエクステンションから使うことのできるたくさんの便利な型がありま
す。一部のエクステンションではそれに固有のディレクトリに、typemap
に対する追加の定義を置いています。これらの追加されたtypemapはメ
インのtypemapにあるINPUTやOUTPUTのマッピングを参照することができ ます。xsubppコンパイラーは、エクステンションに固有のtypemapが
デフォルトのtypemapにあるマッピングをオーバーライドすることを許
しています。
カスタムtypemapを要求するエクステンションのほとんどは、typemapフ
ァイルのTYEPMAPセクションだけを必要としています。カスタムtypemap は
getnetconfigent() の例で拡張typemapの典型的な使用例として使わ
れています。そういったtypemapはCの構造体とT_PTROBJ tyepmapの
equateのために使われています。getnetconfigent() で使われるtypemap
はここにあります。Cの型はXSの型とタブで分けられていて、Cの単項演 算子 *はCの型名の一部としてみなされることに注意してください。
TYPEMAP
Netconfig *<tab>T_PTROBJ
もっと複雑な例を挙げましょう。struct netconfigをC<Net::Config>
というクラスにblessしたいと考えていると仮定しましょう。これを行
うやり方の一つは、アンダースコア(_)を以下の様にパッケージ名を区
切るために使うというものです。
typedef struct netconfig * Net_Config;
それからアンダースコアをダブルコロン(::)にマップするtypemapエン トリー T_PTROBJ_SPECIAL を用意して、Net_Config をその型と して宣言します。
TYPEMAP
Net_Config T_PTROBJ_SPECIAL
INPUT
T_PTROBJ_SPECIAL
if (sv_derived_from($arg, ¥"${(my $ntt=$ntype)=‾s/_/::/g;¥$ntt}¥")) {
IV tmp = SvIV((SV*)SvRV($arg));
$var = ($type) tmp;
}
else
croak(¥"$var is not of type ${(my $ntt=$ntype)=‾s/_/::/g;¥$ntt}¥")
OUTPUT
T_PTROBJ_SPECIAL
sv_setref_pv($arg, ¥"${(my $ntt=$ntype)=‾s/_/::/g;¥$ntt}¥",
(void*)$var);
INPUTセクションとOUTPUTセクションはアンダースコアをダブルコロン へその場(on the fly)で置換して、期待される効果をもたらします。こ の例ではtypemap機構の威力と適用範囲の広さをデモンストレーション します。
ONC+ RPC bind ライブラリ関数に対するインターフェース
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <rpc/rpc.h>
typedef struct netconfig Netconfig;
MODULE = RPC PACKAGE = RPC
SV *
rpcb_gettime(host="localhost")
char *host
PREINIT:
time_t timep;
CODE:
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 );
RPC.xsのためのカスタムtypemap
TYPEMAP
Netconfig * T_PTROBJ
RPCエクステンションのためのPerlモジュール。
package RPC;
require Exporter;
require DynaLoader;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw(rpcb_gettime getnetconfigent);
bootstrap RPC;
1;
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";
This document covers features supported by xsubpp 1.935.
Dean Roehrich <roehrich@cray.com> Jul 8, 1996