length(NAME)
キーワード
perlxs - XS 言語リファレンスマニュアル
XS は Perl と(Perl と一緒に使いたい)C のコード(または C ライブラリ)との 間の拡張インターフェースを作るのに使われるインターフェース記述 ファイルフォーマットです。 XS インターフェースはライブラリと動的または静的にリンクされて、 Perl とリンクすることのできる新しいライブラリを生成します。 XS インターフェース記述はは XS 言語で書かれており、 Perl 拡張インターフェースのコアコンポーネントです。
XSUB は XS インターフェースの基本単位を形成します。 xsubpp コンパイラによるコンパイル後、 それぞれの XSUB は Perl 呼び出し規則と C 呼び出し規則の間の糊を提供する C 関数定義となります。
糊のコードは Perl スタックから引数を取り出し、Perl の値を C 関数が 想定している形式に変換し、C 関数を呼び出し、C 関数の返り値を Perl に 返します。 ここでの返り値は、伝統的な C の返り値か、出力パラメータの役目をする C 関数引数です。 これらの返り値は、Perl スタックに置かれるか、Perl 側から供給された 引数を変更することによって Perl に返されます。
上記は実際に起きることをいくらか単純化したものです。 Perl は C よりもより柔軟な呼び出し規則を認めているので、 実際には XSUB は、検証のために入力をチェックする、もし C 関数からの 返り値が失敗を示していたら例外を投げる(あるいは undef/空リストを返す)、 引数の数と型によって異なった C 関数を呼び出す、オブジェクト指向 インターフェースを提供する、といった、遥かに多くのことができます。
もちろん、このような糊のコードを直接 C で書くこともできます。 しかし、これはつまらない仕事です; 特に複数の C 関数に対する糊を書く 必要があったり、Perl スタックの分野やその奥義に親しくない場合はそうです。 XS はこれを助けます: 糊の C コードを長々と書く代わりに、糊にしてほしいことに関する、 もっと簡潔で短い 記述 を書いて、XS コンパイラ xsubpp に 残りを扱わせることができます。
XS 言語は、どのように C ルーチンが使うのかと、どのように対応する Perl
ルーチンが使うのかとのマッピングを記述できます。
これはまた、直接 C コードに変換され、既に存在する C 関数と関係ない
Perl ルーチンの作成も可能にします。
C インターフェースが Perl インターフェースと同期している場合、
XSUB 宣言はほとんど (K&R 形式の) C 関数の宣言と同じです。
このような事情から、C ヘッダ全体から、ヘッダファイルに記述されている
関数/マクロへの糊を提供する XS ファイルへ変換する、h2xs
と呼ばれる
もう一つのツールがあります。
XS コンパイラは xsubpp と呼ばれます。 このコンパイラは XSUB が Perl の値を扱うための構造や、 Perl が XSUB を呼び出すために必要なものを作成します。 このコンパイラは typemaps を使って C の関数の引数とと出力値を Perlの値とどのようにマッピングするかを決定します。 デフォルトの typemap (Perl に同梱しているもの)は多くの標準的な C の型を 扱います。 リンクされるライブラリのための特殊な構造体や型を扱えるようするための 補助的な typemap も必要になるかもしれません。
XS 形式のファイルは、最初の MODULE =
指示子が現れるまで C 言語
セクションから開始します。
その他の XS 指示子と XSUB 定義はこの行に続きます。
ファイルのこの部分で使われる「言語」は普通 XS 言語として参照されます。
xsubpp は C と XS の言語セクションで POD (perlpod を
参照してください)を認識して呼び飛ばすので、XS ファイルに組み込み
ドキュメントを含めることができます。
エクステンションの作成手順についてのチュートリアルは perlxstut を 参照してください。
注意: エクステンションによっては、エクステンションの糊コードの作成には Dave Beazley の SWIG システムがはるかに便利な機構を提供するでしょう。 さらなる情報については http://www.swig.org/ を参照してください。
この後で出てくる例の多くは、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 は 3 つの部分からなります: 返り値の記述、XSUB ルーチンの名前とその引数の名前、引数の型や形式の記述です。
以下に示す XSUB は、Perl プログラムに対して sin()
と呼ばれる C の
ライブラリ関数にアクセスすることを許します。
この XSUB は引数を一つ取り、一つの値を返す C の関数を模倣します。
double sin(x) double x
オプションとして、型の記述と引数の名前のリストを結合できます; これを以下のように書き換えます:
double sin(double x)
これによって XSUB を ANSI C 宣言と似せて見せます。 以下のように、引数の後にオプションのセミコロンも付けられます:
double sin(double x);
C ポインタ型の引数は異なった意味論を持ちます: 同じような定義の C 関数:
bool string_looks_as_a_number(char *s); bool make_char_uppercase(char *c);
は完全に互換性のない方式です。 これらの関数への引数は以下のように xsubpp が記述します:
char * s char &c
これらの XS 宣言の両方とも C の char*
型に対応しますが、異なる
意味論を持ちます; & 単項演算子 を参照してください。
間接参照演算子 *
は型の一部とみなすべきで、アドレス演算子 &
は
変数の一部であるとみなすと便利です。
C の型における単項演算子や修飾子(qualifiers)の扱いに関する詳細は
typemap を参照してください。
関数名と、その返り値の型は別々の行に分けておかなければなりません; また左揃えにするべきです。
間違い 正しい
double sin(x) double double x sin(x) double x
残りの関数記述はインデントを付けたり、左寄せすることができます。 以下の例では関数の本体を左寄せしています。 本ドキュメントにあるほとんどの例では読みやすいように本体にインデントを 付けています。
CORRECT
double sin(x) double x
もっと複雑な XSUB は多くのその他のセクションを含んでいるかもしれません。 XSUB のそれぞれのセクションは、INIT: や CLEANUP: のような、対応する キーワードで始まります。 しかし、XSUB の最初の 2 行はいつも同じデータからなります: 返り値の種類の記述と、関数およびその引数の名前です。 これらに引き続くものはなんでも、明示的にその他のキーワードがない限りは INPUT: セクションと考えられます。 (INPUT: キーワード を参照してください。)
XSUB セクションは他のセクション開始キーワードが現れるまで続きます。
Perl の引数スタックは XSUB にパラメータとして渡される値や XSUB から返って きた値を格納するのに使われます。 すべての(非 XSUB 関数を含む)Perl 関数は、その値をこのスタックに全て同時に 保持していて、それぞれこのスタック上の位置の範囲によって制限されます。 このドキュメントでは、アクティブな関数に所属するスタックの最初の位置は その関数からは位置 0として参照されます。
XSUB はそのスタックにある引数に対して ST(x) というマクロを使って、
x が示している XSUB のスタック上にある位置の参照を行います。
関数に対する位置 0 は ST(0)
となります。
XSUB に対するパラメータと XSUB から返される値は常に ST(0)
から始まります。
多くの単純な場合においては、xsubppコンパイラは typemap にある
埋め込みコード片(embedding code fragments)から引数スタックを扱う
コードを生成します。
もっと複雑な場合には、プログラマがコードを提供しなければなりません。
RETVAL という変数は自動的に宣言される特別な C 変数です。
RETVAL の C での型は C のライブラリ関数が返す型に一致します。
xsubpp コンパイラは各 XSUB の非 void
返り値に対してこの変数を
宣言します。
デフォルトでは生成された C 関数は RETVAL を C ライブラリ関数が呼ばれたときの
返り値の型を保持するのに使われます。
単純な場合には、RETVAL の値は Perl から XSUB の返り値として
受け取ることのできる引数スタックの ST(0)
に置かれます。
XSUB の返り値の方が void
であった場合には、コンパイラは
その関数に対して変数 RETVAL を宣言しません。
PPCODE: セクションをを使う場合、RETVAL 変数の操作を操作する必要がないので、
このセクションはスタックに出力値を置くためにスタックを直接操作します。
PPCODE: 指示子を使わないのであれば、void
な返り値は
CODE: 指示子が ST(0)
へ陽にセットするのに使われていたとしても、
値を返さないサブルーチンに対してのみ使うべきです。
このドキュメントの以前のものでは、そういった場合に返り値として void
を
使うことを勧めていました。
これは XSUB が 本当に void
であるときにセグメンテーション違反を起こす
可能性があることが確認されました。
このやり方は今では使わないことが推奨されていて、将来のバージョンで
サポートされなくなるでしょう。
こういった場合、返り値 SV *
を使います(現在 xsubpp
は
"truely-void" な関数と"old-practice-declared-as-void" な関数とを明確に
しようとする経験則的なコードが入っています。
このためあなたのプログラムはあなたが返り値として SV *
を使わない限り、
この経験則の振る舞いに左右されます。)
SV *
を返すために RETVAL を使うとき、言及しておくべき魔法が舞台裏で
行われます。
例えば、ST(x) マクロを使って引数スタックを操作するとき、普通は
参照カウントに特別な注意を払う必要があります。
(参照カウントに関しては、perlguts を参照してください。)
人生をより簡単にするために、SV *
を返そうとしているときは
typemap ファイルは自動的に RETVAL
を揮発性にします。
従って、以下の 2 つの XSUB はだいたい等価です:
void alpha() PPCODE: ST(0) = newSVpv("Hello World",0); sv_2mortal(ST(0)); XSRETURN(1);
SV * beta() CODE: RETVAL = newSVpv("Hello World",0); OUTPUT: RETVAL
これは普通可読性を改善するのでかなり有用です。
これは SV *
ではうまく動作する一方、残念ながら返り値として
AV *
や HV *
を使うときには簡単ではありません。
以下のように書ける べき です:
AV * array() CODE: RETVAL = newAV(); /* do something with RETVAL */ OUTPUT: RETVAL
しかし typemap ファイルにある修正されていないバグのために(これを
修正すると既存の多くの CPANモジュールが動かなくなります)、
AV *
の参照カウントは適切にデクリメントされません。
従って、上述の XSUB は、呼び出されるごとにメモリリークします。
同じ問題は HV *
にもあります。
AV *
や HV *
を返すとき、AV や HV を揮発性にすることによって、
確実に参照カウントを減らす必要があります:
AV * array() CODE: RETVAL = newAV(); sv_2mortal((SV*)RETVAL); /* do something with RETVAL */ OUTPUT: RETVAL
そして、SV *
のためにはこれをする必要はないことも覚えておいてください。
キーワード MODULE は XS コードの始まりと、定義する関数のパッケージを 指定するのに使われます。 キーワード MODULE よりも前にあるテキストはCプログラムとして扱われ、 POD が除去されますが、それ以外は何の変更も加えられずに出力されます。 すべての 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 はこの関数を gettime()
として見ます。
このキーワードはキーワード PACKAGE を使った後にあるべきです。 PACKAGE が使われなければ、PREFIX はキーワード MODULE の後にあるべきです。
MODULE = RPC PREFIX = rpc_
MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_
キーワード OUTPUT: は、幾つかの関数パラメータが XSUB が終了するときに
更新されるべき(Perl のために新しい値を見えるようにする)ものであることや、
幾つかの値が呼び出し元の Perl 関数に戻されるべきものであることを示します。
前述の sin()
のような CODE: や PPCODE: セクションのない単純な関数には、変数
RETVALは自動的に出力値に割り当てられます。
もっと複雑な関数では、xsubpp コンパイラはどの変数が 出力変数であるかを
決めるための助けが必要です。
このキーワードは、通常 キーワード 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 は、XSUB の OUTPUT セクション中、RETVAL を除く全てのパラメータに
対して自動的に SvSETMAGIC()
を出力します。
これは普通は望ましい振る舞いです; なぜなら出力パラメータに適切に
'set' マジックを起動する面倒を見るからです(ハッシュや配列の要素が
存在しなかったとき、作成しなければならないので必要です)。
もし何らかの理由でこの振る舞いが望ましくないなら、
OUTPUT セクションの残りのパラメータでこれを無効にするために、
OUTPUT セクションには SETMAGIC: DISABLE
という行を含めることが出来ます。
同様に、SETMAGIC: ENABLE
は OUTPUT セクションの残りのために
再有効化するために使えます。
'set' マジックに関するさらなる詳細については perlguts を参照してください。
NO_OUTPUT キーワードは XSUB の最初のトークンとして置くことが出来ます。
このキーワードは、インターフェースを提供する C サブルーチンが void
でない
返り値を持ちますが、この C サブルーチンからの返り値は生成された Perl
サブルーチンから返されるべきではないことを示します。
このキーワードが存在すると RETVAL 変数 は作成され、 サブルーチンへの生成された呼び出しの中でこの変数に代入されますが、 この変数の値は自動生成されたコードの中では使われません。
このキーワードは、RETVAL
がユーザーが追加したコードによって
アクセスされる場合にのみ意味があります。
これは、関数インターフェースをより Perl 風にする場合、
特に C の返り値が単にエラーを示すだけのものの場合に特に有用です。
例えば、
NO_OUTPUT int delete_file(char *name) POSTCALL: if (RETVAL != 0) croak("Error %d while deleting file '%s'", RETVAL, name);
ここで生成された XS 関数は成功時には何も返さず、エラー時には意味のある
エラーメッセージと共に die()
します。
このキーワードは、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
キーワード 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
INIT: セクションのもう一つの利用法は、C 関数を呼び出す前の前提条件を チェックすることです:
long long lldiv(a,b) long long a long long b INIT: if (a == 0 && b == 0) XSRETURN_UNDEF; if (b == 0) croak("lldiv: cannot divide by 0");
キーワード NO_INIT は関数のパラメータが出力値としてのみ使われることを 示すのに使われます。 xsubpp コンパイラは通常、すべての関数パラメータの値を引数スタックから 読み取って、関数の入り口で C の変数にそれを代入するようなコードを生成します。 NO_INIT は、コンパイラに一部のパラメータが入力としてではなく出力として 使われることと、それらが関数を終了する前に操作されることを示します。
以下に示す例では、関数 rpcb_gettime()
の変種を示します。
この関数は変数 timep を出力変数としてのみ使っており、その初期値は
考慮しません。
bool_t rpcb_gettime(host,timep) char *host time_t &timep = NO_INIT OUTPUT: timep
C 関数のパラメータは通常、引数スタック(Perl から XSUB に渡された
パラメータが順番に入っています)にある値によって初期化されます。
typemap は Perl の値から C のパラメータへ変換するのに使われるコード片から
構成されています。
しかしプログラマは、typemap をオーバーライドすることや別の
(もしくは追加の)初期化コードを提供することができます。
初期化コードは INPUT: セクションの行で最初の
=
, ;
, +
から始まります。
唯一の例外はこの ;
が行を終端している場合で、この ;
は
暗黙に無視されます。
以下に示すコードは、関数の引数に対する初期化コードをどのように提供するかを
例示するものです。
初期化コードはそれが出力される前にダブルクォートの中でコンパイラによって
評価されるので、たとえばダブルクォートのようなリテラルとして
評価されるようなもの [mainly $
, @
, or \\
]であれば、
バックスラッシュを使って保護しなければなりません。
変数 $var, $arg, $type は typemap として使われます。
bool_t rpcb_gettime(host,timep) char *host = (char *)SvPV_nolen($arg); time_t &timep = 0; OUTPUT: timep
これはパラメータに対するデフォルト値を設定するために使うべきでは ありません。 通常これは、使用するよりも前に他のライブラリ関数によってパラメータを 処理しなければならないようなときに使われるでしょう。 デフォルトパラメータは次のセクションで説明します。
初期化が =
で始まっていると、入力変数の宣言中に出力され、
typemap によって提供された初期化を置き換えます。
初期化が ;
か +
で始まっていると、これは全ての入力変数が
宣言された後に実行されます。
;
の場合、typemap によって通常提供される初期化は実行されません。
+
の場合、変数の宣言は typemap からの初期化に含まれます。
グローバル変数 %v
は、ある初期化からの情報は他の初期化に必要になるという
本当に珍しい場合のために利用可能です。
これは本当に不明瞭な例です:
bool_t rpcb_gettime(host,timep) time_t &timep; /* \$v{timep}=@{[$v{timep}=$arg]} */ char *host + SvOK($v{timep}) ? SvPV_nolen($arg) : NULL; OUTPUT: timep
上述の例で使われている \$v{timep}=@{[$v{timep}=$arg]}
という構文は
2 つの目的があります: 1 つ目に、この行が xsubpp によって処理されると、
Perl スニペット $v{timep}=$arg
が評価されます。
2 つ目に、評価されたスニペットのテキストは生成された C ファイル
(C コメントの内側) へ出力されます!
char *host
の行の処理中に、$arg は ST(0)
に評価され、$v{timep}
は
ST(1)
に評価されます。
XSUB 引数のデフォルト値はパラメータリスト中に代入文を置くことによって
関数パラメータを指定できます。
デフォルト値には数字か文字列、または特別な文字列 NO_INIT
を使えます。
デフォルト値は常に、最も右にあるパラメータに対してのみ使うべきです。
rpcb_gettime()
に対する XSUB がデフォルトの host の値を持つことが
できるように、XSUB に対するパラメータの順序を変えます。
この XSUB は実際の rpcb_gettime()
関数を正しい順序の引数で呼び出します。
この XSUB は以下のどちらかの文で Perl から呼び出せます:
$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: は INPUT: セクションが発行するパラメータ定義の 直前または直後に追加の変数を宣言できるようにします。
もし変数が CODE: セクション内で宣言されていると、その変数は入力
パラメータとして発行した typemap のコードに従うことになります。
これは C コードが終わった後の結果となることがあり、C の構文エラーと
なります。
同様のエラーは、明示的に ;
-型や +
-型のパラメータの初期化が使われた
場合にも起こります
(関数パラメータの初期化 を参照してください)。
これらの変数を INIT: セクションで宣言することは助けにはなりません。
このような場合、追加の変数を他の変数宣言と共に宣言することを 強制するために、宣言を 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
この特定の場合のために、INIT: キーワードは PREINIT: キーワードと同じ C コードを生成します。 もう一つの、正しいがエラーになりやすい例です:
bool_t rpcb_gettime(timep) time_t timep = NO_INIT CODE: char *host = "localhost"; RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL
host
を宣言するもう一つの方法は CODE: セクションで C ブロックを
使うことです:
bool_t rpcb_gettime(timep) time_t timep = NO_INIT CODE: { char *host = "localhost"; RETVAL = rpcb_gettime( host, &timep ); } OUTPUT: timep RETVAL
typemap エントリが処理される前に追加の宣言を置く能力は、typemap 変換が グローバルな状態を操作する場合にはとても便利です:
MyObject mutate(o) PREINIT: MyState st = global_state; INPUT: MyObject o; CLEANUP: reset_to(global_state, st);
ここで、RETVAL の処理がグローバル変数 global_state
を変更するときに
MyObject から INPUT: セクションで MyObject
に変換するとします。
これらの変換が行われた後、(例えば、メモリリークを避けるために)
global_state
の古い値を戻します。
ここにはもう一つの明快さと簡潔さのトレードオフがあります:
INPUT セクションはサブルーチンの引数リストに現れない C 変数の宣言も
許しています。
従って mutate()
のための上述のコードは以下のように書き直せます
MyObject mutate(o) MyState st = global_state; MyObject o; CLEANUP: reset_to(global_state, st);
そして rpcb_gettime()
のためのコードは次のように書き直すことができます:
bool_t rpcb_gettime(timep) time_t timep = NO_INIT char *host = "localhost"; C_ARGS: host, &timep OUTPUT: timep RETVAL
キーワード SCOPE: は特定の XSUB に対してスコーピングを 有効にするために使います。 有効になった場合、XSUB は ENTER と LEAVE を自動的に起動します。
複雑な型マッピングをサポートするために、typemap のエントリーが
/*scope*/
のようなコメントを含む 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
INPUT セクションはサブルーチンの引数リストに現れない C 変数の宣言も 許しているので、これは以下のように短く出来ます:
bool_t rpcb_gettime(host,timep) time_t tt; char *host; char *h = host; time_t timep; CODE: RETVAL = rpcb_gettime( h, &tt ); timep = tt; OUTPUT: timep RETVAL
(char *
のための入力変換は「単純な」ものであるという知識を使っているので、
host
は宣言行で初期化され、h = host
の代入は早すぎることはありません。
さもなければ h = host
の代入は CODE: か INIT: のセクションで
行う必要があります。)
XSUB の引数リストの中で、引数名の前に
IN
/OUTLIST
/IN_OUTLIST
/OUT
/IN_OUT
キーワードを前置できます。
IN
キーワードがデフォルトで、他のキーワードは Perl インターフェースが
C インターフェースとどのように異なるかを示します。
OUTLIST
/IN_OUTLIST
/OUT
/IN_OUT
が前置された引数は
C サブルーチンから ポインタ経由で 使われるとみなされます。
OUTLIST
/OUT
キーワードは、C サブルーチンは
この引数で示されているメモリを検査しないが、追加の返り値を
このポインタを通して書き込むということを示しています。
OUTLIST
キーワードが前置された引数は、生成された Perl 関数の使用法
シグネチャに現れません。
IN_OUTLIST
/IN_OUT
/OUT
が前置された引数は Perl 関数の引数として
現れます。
OUT
-引数の例外として、これらの引数は対応する C 型に変換され、それから
それらのデータへのポインタが C 関数への引数として与えられます。
これは C 関数がこれらのポインタを通して書き込むことを想定しています。
生成された Perl 関数の返り値リストは、関数からの C の返り値
(XSUB の返り値型が void
であるか、
NO_OUTPUT キーワード が使わた場合を除く)に、
全ての OUTLIST
および IN_OUTLIST
引数を(出現順に)
続けたものとなります。
XSUB からの返り時に、IN_OUT
/OUT
Perl 引数は C 関数によって書かれた
値に変更されます。
例えば、以下の XSUB は:
void day_month(OUTLIST day, IN unix_time, OUTLIST month) int day int unix_time int month
Perl から次のようにして使われます:
my ($day, $month) = day_month(time);
対応する関数の C シグネチャは次のようになります:
void day_month(int *day, int unix_time, int *month);
IN
/OUTLIST
/IN_OUTLIST
/IN_OUT
/OUT
のキーワードは、次のように
ANSI 型の宣言と混ぜることができます:
void day_month(OUTLIST int day, int unix_time, OUTLIST int month)
(ここではオプションの IN
キーワードは省略されています)。
IN_OUT
引数は、& 単項演算子 で導入されて
OUTPUT:
セクション (OUTPUT: キーワード を参照してください) に
置かれた引数と同じです。
IN_OUTLIST
引数はとても似ていて、唯一の違いは、C 関数がポインタを
通して書いた値は Perl 引数を変更せず、出力リストに置かれるということです。
OUTLIST
/OUT
引数は、Perl 引数の初期値が読み込まれない
(そして C 関数に渡されない - 代わりに何らかのごみが渡されます)という
点においてだけ、IN_OUTLIST
/IN_OUT
引数と異なります。
例えば、上述の同じ C 関数は以下のようなインターフェースか:
void day_month(OUT int day, int unix_time, OUT int month);
または:
void day_month(day, unix_time, month) int &day = NO_INIT int unix_time int &month = NO_INIT OUTPUT: day month
しかし、生成された Perl 関数はとても C っぽい形で呼び出されます:
my ($day, $month); day_month($day, time, $month);
length(NAME)
キーワードC 関数への入力引数の 1 つが文字列引数 NAME
の長さの場合、
XSUB 宣言において長さ引数の名前を length(NAME)
で置き換えることが
できます。
この引数は、生成された Perl 関数が呼び出されるときには
省略されなければなりません。
例えば:
void dump_chars(char *s, short l) { short n = 0; while (n < l) { printf("s[%d] = \"\\%#03o\"\n", n, (int)s[n]); n++; } }
MODULE = x PACKAGE = x
void dump_chars(char *s, short length(s))
は dump_chars($string)
として呼び出されます。
この指示子は ANSI 風の関数定義にのみ対応しています。
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"; CODE: if( items > 1 ) host = (char *)SvPV_nolen(ST(1)); RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL
C_ARGS: キーワードは、Perl からの呼び出し手順ではなく C からの呼び出し手順を 持つ XSUB を、CODE: や PPCODE: セクションを書くことなく作成することを 可能にします。 C_ARGS: 段落の内容は、呼び出される C 関数の引数として、何の変更もなく 使われます。
例えば、以下のように宣言される C 関数を想定します:
symbolic nth_derivative(int n, symbolic function, int flags);
そしてデフォルトのフラグは C のグローバル変数 default_flags
に
保管されているとします。
以下のようにして呼び出されるインターフェースを作りたいとします:
$second_deriv = $function->nth_derivative(2);
これをするためには、XSUB を以下のように宣言します:
symbolic nth_derivative(function, n) symbolic function int n C_ARGS: n, function, default_flags
キーワード PPCODE: は キーワード CODE: の代替であり、xsubpp コンパイラに プログラマが XSUB の返り値のための引数スタックを制御するコードを 提供しているということを指示するのに使われます。 ある XSUB で、一つの値ではなく値のリストを返したいというときに必要になります。 この場合 PPCODE: を使わなければならず、また、値のリストを陽にスタックへ プッシュしなければなりません。 PPCODE: と CODE: は同一の XSUB で同時に使うべきではありません。
PPCODE: と CODE: セクションの実際の違いは、
(現在の Perl のスタックポインタである) SP
マクロの初期化と、
XSUB から返るときのスタックのデータの扱いです。
CODE: セクションでは SP は XSUB に入ったときの値を保存します:
SP は(最後の引数に続く)関数ポインタを指します。
PPCODE: セクションでは、SP は引数リストの先頭に戻るので、
XSUB が Perl に戻るときに、Perl が想定している場所に PUSH*()
マクロが
出力値を置くことが出来ます。
CODE: セクションの末尾に生成されるコードは、Perl が受け取る返り値の数が
(C 関数の返り値が void
かどうか、および
RETVAL 変数 で記述した経験則に依存して)0 か 1 であることを
保証します。
PPCODE: セクションの末尾に生成されるコードは、返り値の数と
SP
が [X]PUSH*()
マクロで更新された回数に基づきます。
マクロ ST(i)
, XST_m*()
, XSRETURN*()
は CODE: セクションでも
PPCODE: セクションでも同じようにうまく動作することに注意してください。
次の XSUB は C の rpcb_gettime()
関数を呼び出し、二つの出力値 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");
PPCODE セクションの出力パラメータを扱うとき、'set' マジックプロパティを 扱うようにしてください。 'set' マジックに関する詳細については perlguts を参照してください。
ときとしてプログラマには、関数が失敗したときに(返り値とは)別に
ステータスを表わす値を返すよりは、単純に 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: if( rpcb_gettime( host, &timep ) ){ ST(0) = sv_newmortal(); 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{ /* Nothing pushed on stack, so an empty * list is implicitly returned. */ }
一部には、上記のXSUB において、最後の文へ fall through するように
制御するよりは陽に return
を含めるようにしたいと考える人もいるでしょう。
そういった場合には、代わりに XSRETURN_EMPTY
を使います。
これは XSUB スタックが適切に調整されることを保証します。
他の XSRETURN
マクロについては perlapi を参照してください。
XSRETURN_*
マクロは CODE ブロックでも使えるので、この例は以下のように
書き換えられます:
int rpcb_gettime(host) char *host PREINIT: time_t timep; CODE: RETVAL = rpcb_gettime( host, &timep ); if (RETVAL == 0) XSRETURN_UNDEF; OUTPUT: RETVAL
実際のところ、このチェックを POSTCALL: セクションに置くこともできます。 PREINIT: の単純化と共に、これは以下のようになります:
int rpcb_gettime(host) char *host time_t timep; POSTCALL: if (RETVAL == 0) XSRETURN_UNDEF;
キーワード REQUIRE: は xsubpp コンパイラが XS モジュールを コンパイルするために最低限必要なバージョンを示すために使われます。 以下のような文のある XS モジュールはバージョン 1.922 以降の xsubpp でのみ コンパイルできます。
REQUIRE: 1.922
このキーワードは XSUB がその終了前に特別なクリーンアップ手続きを必要とする 場合に使うことができます。 キーワード CLEANUP: が使われるときには、それは CODE:, PPCODE, OUTPUT: ブロックのいずれかに続いていなければなりません。 クリーンアップブロックのためのコードは XSUB にある最後のステートメントに 付け加えられます。
このキーワードは、C サブルーチンを実行した後に特別な手続きが必要な XSUB に 使います。 POSTCALL: キーワードを使う時は、XSUB に存在する OUTPUT: と CLEANUP: ブロックの前になければなりません。
NO_OUTPUT キーワード と undef と空リストを返す の例を 参照してください。
The POSTCALL: ブロックは、C サブルーチン呼び出しがユーザーから CODE: や PPCODE: セクションによって提供される場合はあまり意味はありません。
キーワード BOOT: はエクステンションのブートストラップ関数のためにコードを 追加するのに使われます。 ブートストラップ関数は xsubpp コンパイラによって生成され、通常は Perl に登録するような XSUB に必要な文を保持します。 BOOT: キーワードによってプログラマはコンパイラに対して、 ブートストラップ関数にコードを追加することを指示できます。
このキーワードは最初に キーワード MODULE が現れたあとの任意の場所で 使うことができ、その行にはこのキーワードのみがあるようにすべきです。 キーワードの後で最初に現れる空行がコードブロックの終端を示します。
BOOT: # The following message will be printed when the # bootstrap function executes. printf("Hello from the bootstrap!\n");
キーワード VERSIONCHECK: は xsubpp のオプションである
-versioncheck
や -noversioncheck
に相当します。
このキーワードはコマンドラインオプションをオーバーライドします。
バージョンチェックはデフォルトでは有効になっています。
バージョンチェックが有効になっている場合、XS モジュールはそのバージョンが
PM モジュールのバージョンとマッチするかどうかが検査されます。
バージョンチェックを有効にするには:
VERSIONCHECK: ENABLE
バージョンチェックを無効にするには:
VERSIONCHECK: DISABLE
PM モジュールのバージョンが NV (浮動小数点数) の場合、文字列化するときに 精度が落ちる可能性がある (今のところ 9 桁で切っています) ので、 XS モジュールのバージョンの一致しないかもしれません。 長いバージョン番号を使うときは、$VERSION 宣言をクォートして文字列に することを推奨します。
キーワード PROTOTYPES: は xsubpp の -prototypes
や -noprototypes
といったオプションに相当します。
このキーワードはコマンドラインオプションを上書きします。
デフォルトではプロトタイプが有効になっています。
プロトタイプが有効になっているとき、XSUB は Perl にプロトタイプを与えます。
このキーワードはXSモジュールの中で、モジュールの異なった部分で何回でも
プロトタイプを許可したり禁止したりできます。
プロトタイプを許可するには:
PROTOTYPES: ENABLE
プロトタイプを禁止するには:
PROTOTYPES: DISABLE
このキーワードは前述したキーワード PROTOTYPES: に似ていますが、 XSUB に対する特定のプロトタイプを使うのに xsubpp を強制的に 使うことができます。 このキーワードは他のすべてのプロトタイプオプションやキーワードを 上書きしますが、カレントの XSUB にのみ影響します。 Perl のプロトタイプについては Prototypes in perlsub を参照してください。
bool_t rpcb_gettime(timep, ...) time_t timep = NO_INIT PROTOTYPE: $;$ PREINIT: char *host = "localhost"; CODE: if( items > 1 ) host = (char *)SvPV_nolen(ST(1)); RETVAL = rpcb_gettime( host, &timep ); OUTPUT: timep RETVAL
プロトタイプが有効の場合、以下の例のようにして XSUB の中でローカルに 無効にすることができます:
void rpcb_gettime_noproto() PROTOTYPE: DISABLE ...
ALIAS: というキーワードは、XSUB に対して二つ以上のユニークな Perl での名前を
持たせ、また、起動されたときに使われている(そういったユニークな Perl での)
名前を知るための手段を持たせます。
Perl での名前は完全修飾されたパッケージ名とすることができます。
それぞれの別名はインデックスとして与えられます。
コンパイラは、使用される別名のインデックスが格納されている 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
pure Perl を使ったオーバーロードインターフェースを書く代わりに、
(上述の ALIAS: キーワードと同様に)関数のための追加の Perl の名前を
定義するための OVERLOAD キーワードも使えます。
しかし、オーバーロードした関数は (4 つの引数が必要な nomethod()
関数を
除いて)3 つの引数で定義しなければなりません
関数に OVERLOAD: キーワードがあると、オーバーロードしたマジックを
登録するために、いくつかの追加の行がxsubpp によって生成された
c ファイルで定義されます。
bless されたオブジェクトは実際には RV として保管されるので、 引数を前処理して bless された RV に保管されている実際の SV を取り出すという typemap の昨日を使うのが便利です。 以下の T_PTROBJ_SPECIAL の例を参照してください。
OVERLOAD: キーワードを使うには、以下のように 3 つの入力パラメータ (または C 形式の '...' 定義) を持つ XS 関数を作ります:
SV * cmp (lobj, robj, swap) My_Module_obj lobj My_Module_obj robj IV swap OVERLOAD: cmp <=> { /* function defined here */}
この場合、関数は比較演算子の 3 つの方法全てをオーバーロードします。 非英字を使った全てのオーバーロード操作に関して、引数をクォートなしで タイプする必要があり、複数のオーバーロードは空白で分けられます。 "" (文字列化のオーバーロード) は \"\" と入力する(エスケープする) 必要があります。
OVERLOAD キーワードに追加して、省略したオーバーロード演算子を Perl がどのように自動生成するかを制御したい場合は、以下のように モジュールヘッダセクションに FALLBACK キーワードをセットできます:
MODULE = RPC PACKAGE = RPC
FALLBACK: TRUE ...
ここで FALLBACK は TRUE, FALSE, UNDEF の 3 つの値のいずれかを 取ることができます。 OVERLOAD を使うときに FALLBACK 値がセットされていないと、デフォルトとして UNDEF となります。 1 つまたは複数の関数で OVERLOAD を使っている場合以外は FALLBACK は 使えません。 さらなる詳細については overload/Fallback を参照してください。
このキーワードは現在の XSUB を与えられた呼び出しシグネチャの持ち主として 宣言します。 キーワードになんらかの文字列が続いていると、それはこのシグネチャを 持っている関数のリストとして扱われ、現在の XSUB に付加されたものと 扱われます。
例えば、以下の同じシグネチャを持つ 4 つの C 関数 multiply()
, divide()
,
add()
, subtract()
がある場合:
symbolic f(symbolic, symbolic);
以下のようにして全てを同じ XSUB で使えます:
symbolic interface_s_ss(arg1, arg2) symbolic arg1 symbolic arg2 INTERFACE: multiply divide add subtract
(これは 4 つの Perl 関数のための完全な XSUB コードです!) 4 つの生成された Perl 関数は対応する C 関数と名前を共有します。
ALIAS: キーワードと比べた場合のこの手法の有利な点は、switch 文を
コーディングする必要がなく、それぞれの Perl 関数はどの C 関数を
呼び出すべきかを知っている、ということです。
更に、以下のように使うことで実行時に追加の関数 remainder()
を
他の XSUB から付加できます:
CV *mycv = newXSproto("Symbolic::remainder", XS_Symbolic_interface_s_ss, __FILE__, "$$"); XSINTERFACE_FUNC_SET(mycv, remainder);
(この例は、INTERFACE_MACRO: セクションがないことを想定しています;
さもなければ、XSINTERFACE_FUNC_SET
以外の何かを使う必要があります;
次の章を参照してください。)
このキーワードは、XSUB から関数ポインタを取り出すために異なった方法を
使って INTERFACE を定義できるようにします。
このキーワードに引き続く文字列は、関数ポインタを展開/セットする
マクロの名前になります。
展開マクロは、返り値、CV*
、この CV*
のための XSANY.any_dptr
が
渡されます。
セッターマクロは cv と関数ポインタが渡されます。
デフォルト値は XSINTERFACE_FUNC
と XSINTERFACE_FUNC_SET
です。
空の関数リストのINTERFACE キーワードは、INTERFACE_MACRO キーワードが
使われたときは省略できます。
前述の例で、multiply(), divide()
, add()
, subtract()
の関数ポインタが
C 配列 fp[]
にオフセット multiply_off
, divide_off
, add_off
,
subtract_off
で保管されていると仮定します。
以下のように
#define XSINTERFACE_FUNC_BYOFFSET(ret,cv,f) \ ((XSINTERFACE_CVT_ANON(ret))fp[CvXSUBANY(cv).any_i32]) #define XSINTERFACE_FUNC_BYOFFSET_set(cv,f) \ CvXSUBANY(cv).any_i32 = CAT2( f, _off )
と C セクションに書きます。
symbolic interface_s_ss(arg1, arg2) symbolic arg1 symbolic arg2 INTERFACE_MACRO: XSINTERFACE_FUNC_BYOFFSET XSINTERFACE_FUNC_BYOFFSET_set INTERFACE: multiply divide add subtract
と XSUB セクションに書きます。
このキーワードは 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 );
INPUT: セクションにある &
単項演算子は、
Perl の値と C を、&
の左にある C の型を使って変換する必要があるけれども、
C 関数が呼び出されたときはこの値へのポインタが提供されることを
xsubpp に伝えるために使われます。
これはパラメータを参照渡しする C 関数のための CODE: ブロックを避けるときに
有用です。
典型的には、パラメータがポインタ型でないとき(int*
や long*
ではなく、
int
か 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:,
POSTCALL:, CLEANUP: といったブロックなしに、関数の外側のときと同じように
使えます。
コメントは MODULE キーワードの後の任意の場所で使えます。
コンパイラはプリプロセッサ指示子をいじらずにそのまま出力し、
コメント行は削除します。
POD 文書は C と XS の両方のセクションで、どの地点でも利用できます。
POD は =cut
コマンドで終端されなければなりません;
これがないと xsubpp
はエラーを出して終了します。
ほとんどのインデントスタイルは =
で始まる行の先頭には空白を
入れることになるので、人間が生成した C コードが間違って POD と
みなされることはほとんどありそうにないことです。
機械が生成した XS ファイルは、空白を "\n=" で区切らないように気を付けないと、
同じ罠に引っかかるかもしれません。
行の最初にある空白でないキャラクタとして#
を使うことで、
XSUB にコメントを追加できます。
コメントを C のプリプロセッサ指示子と取り違えられないように
注意してください。
これを防ぐ単純な方法は、#
の前に空白を置くことです。
プリプロセッサの指示子を二つのバージョンの関数のうちの一つを 選ぶために使うのであれば、こうであって:
#if ... version1 #else /* ... version2 */ #endif
こうではありません:
#if ... version1 #endif #if ... version2 #endif
これは、そうしなければ xsubpp があなたが関数の重複した定義をしようと していると信じてしまうからです。 同様に、#else/#endif の前に空白行を入れて、それが関数本体の一部と みなされないようにしてください。
XSUB 名に ::
が含まれている場合、C++ のメソッドとして扱われます。
生成された Perl 関数は、その関数に対する第一引数がオブジェクト
ポインタであると仮定されます。
このオブジェクトポインタは THIS と呼ばれる変数に格納されます。
オブジェクトは C++ の new()
を使って作成されたものであるべきで、かつ、
sv_setref_pv()
マクロによって Perl から bless されているべきものです。
Perl によるオブジェクトの bless は typemap で扱えます。
typemap の例はこのセクションの最後にあります。
XSUB の返り値型に static
が含まれている場合、このメソッドは
静的メソッドとして扱われます。
これは、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
両方の Perl 関数とも第一引数としてオブジェクトを期待しています。
生成された C++ コードではオブジェクトは THIS
と呼ばれ、メソッド呼び出しは
このオブジェクトで実行されます。
このため、C++ コード blue()
, set_blue()
メソッドは以下のようにして
呼び出されます:
RETVAL = THIS->blue();
THIS->set_blue( val );
単一の get/set メソッドを、省略可能な引数を使って書くこともできます:
int color::blue( val = NO_INIT ) int val PROTOTYPE $;$ CODE: if (items > 1) THIS->set_blue( val ); RETVAL = THIS->blue(); OUTPUT: RETVAL
関数名が DESTROY である場合、C++ の delete
関数が呼び出されて、
THIS
はそれに対するパラメータとして与えられます。
このような生成されたコードは:
void color::DESTROY()
以下のようになります:
color *THIS = ...; // Initialized as in typemap
delete THIS;
関数名が new であった場合、動的に C++ オブジェクトを作成するために
C++ の new
関数が呼び出されます。
XSUB は CLASS
と呼ばれる変数に保持されるクラス名が、第一引数として
与えられることを期待します。
color * color::new()
C++ コードは new
を呼び出します。
RETVAL = new color();
この C++ の例を使うことのできる typemap の例を示します。
TYPEMAP color * O_OBJECT
OUTPUT # The Perl object is blessed into 'CLASS', which should be a # char* having the name of the package for the blessing. 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 ライブラリとの間のインターフェースを設計するとき、
しばしば (h2xs -x
によって生成されるように) ストレートに
C から XS への変換することで十分となります。
しかし、時々インターフェースは非常に C に似たものに見え、
特に C の関数がそのパラメータを変更したり、(「返り値が負の場合は
失敗を意味します」のような) 範囲内の失敗を返すような場合には
直感的でないものになります。
プログラマがもっと Perl 的なインターフェースを作りたいと望んでいる場合には、
以下に示す戦略がインターフェースのより重要な部分を認識するための
手助けとなるでしょう。
入出力や出力のパラメータのある C の関数を見つけ出します。 こういった関数に対する XSUB は Perl にリストを返すことができます。
失敗を示すのに範囲内の情報を使う C 関数を見つけ出します。 これらは関数が失敗したときには空リストや undef を返す候補です。 もし C 関数の呼び出しなしに失敗が検出されるなら、 失敗を報告するのに INIT: セクションを使いたいかもしれません。 C 関数から返った後で検出可能な失敗については、失敗を処理するために POSTCALL: セクションを使いたいかもしれません。 もっと複雑な場合では CODE: か PPCODE: のセクションを使ってください。
多くの関数が返り値による同じ失敗表示を使う場合、この状況を扱うための 特別な typedef を作りたいと思うかもしれません。 以下の文を:
typedef int negative_is_failure;
XS ファイルの先頭近くに置き、負の数を undef
に変換するか、おそらく
croak()
するような negative_is_failure
のための OUTPUT typemap エントリを
作成します。
この後、negative_is_failure
型の返り値は、より Perl 風の
インターフェースを作ります。
C と XSUB の関数それ自身でしか使われないような値を見つけ出します; つまり、関数へのパラメータがグローバル変数の内容であるようなものです。 Perl がそういった値に対するアクセスを必要としないのであれば、その値を C から Perl へ変換する必要はないということになります。
C の関数に対する引数リストや返り値の中にあるポインタを見つけ出します。
一部のポインタは、入出力や出力のパラメータのために使われており、
これらは &
単項演算子で扱うことができ、おそらくは、NO_INIT キーワードが
使えます。
その他のいくつかは int *
の方を扱う必要があり、
このような場合に便利な Perl 変換をするかを決定する必要があります。
意味論が明確な場合、変換を typemap ファイルに置くことが望ましいです。
C の関数によって使われている構造体を見つけ出します。
多くの場合、こういった構造体に対して T_PTROBJ typemap を使うのが
助けになるので、(そのような構造体を) bless されたオブジェクトとして
Perl から扱うことができます。
(これは h2xs -x
で自動的に扱われます。)
同じ C の型が、異なった変換を必要とする異なった複数のコンテキストで
使われる場合、この C の型にマッピングされる新しいいくつかの型を
typedef
して、これらの新しい型に対して別々の typemap エントリを
作成します。
これらの型を返り値の宣言と XSUB への引数に使います。
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 のリファレンスにするかを
示します。
Perl はこのリファレンスを bless されたオブジェクトへのリファレンスと
みなし、そしてオブジェクトに対するデストラクタの呼び出しを試みます。
デストラクタは XS のソースで、getnetconfigent() が使ったメモリを
解放するために提供されます。
XS にあるデストラクタは、DESTROYで終わる名前の XSUB 関数を
指定することで作成することができます。
XS デストラクタは別の XSUB によって割り当てられた (malloc された)
メモリを解放するために使うこともできます。
struct netconfig *getnetconfigent(const char *netid);
typedef
が struct netconfig
のために生成されます。
Perl のオブジェクトは C の型の名前とマッチするクラスにおいて( Ptr
と
いうタグが付加されて) bless されます。
その名前は、Perl のパッケージ名として使うのであれば空白を
含むべきではありません。
デストラクタはオブジェクトのクラスに対応するクラスに置かれて、
キーワード PREFIX は Perl が期待するようにワード DESTOROY の名前を
切り詰めるのに使われます。
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 セクションに
あるコード片を指示します。
セクションラベル TYPEMAP
, INPUT
, OUTPUT
は行の先頭におかれ、
大文字でなければなりません。
Perl のソースの lib/ExtUtils
ディレクトリにあるデフォルトの typemap は
Perl のエクステンションから使うことのできるたくさんの便利な型があります。
一部のエクステンションではそれに固有のディレクトリに、typemap に対する
追加の定義を置いています。
これらの追加された typemap はメインの typemap にある INPUT や OUTPUT の
マッピングを参照することができます。
xsubpp コンパイラは、エクステンションに固有の typemap が
デフォルトのtypemapにあるマッピングをオーバーライドすることを許しています。
カスタム typemap を要求するエクステンションのほとんどは、typemap ファイルの
TYEPMAPセクションだけを必要としています。
カスタム typemap は getnetconfigent()
の例で拡張 typemap の典型的な
使用例として使われています。
そういった typemap は C の構造体を T_PTROBJ tyepmap と一致させるために
使われています。
getnetconfigent()
で使われる typemap をここで示します。
C の型は XS の型とタブで分けられていて、C の単項演算子 *
は C の型名の
一部としてみなされることに注意してください。
TYPEMAP Netconfig *<tab>T_PTROBJ
もっと複雑な例を挙げましょう。
struct netconfig
を 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 = INT2PTR($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 セクションはアンダースコアをダブルコロンへ その場で置換して、期待される効果をもたらします。 この例では typemap 機構の威力と適用範囲の広さを例示します。
(perl.h に定義されている) INT2PTR マクロは、整数から与えられた型の ポインタへのキャストを、整数とポインタとのサイズが異なる可能性を 考慮しつつ行います。 また、OUTPUT セクションで有用かもしれない、他の方法でマッピングする、 PTR2IV, PTR2UV, PTR2NV マクロもあります。
Perl 5.8 から、マクロフレームワークは、マルチスレッド Perl から アクセスされる XS モジュールで安全に保管される静的データを許すように 定義されています。
マクロは基本的にはマルチスレッド Perl で使えるように設計されていますが、 非スレッド Perl でも動作するように設計されています。
従って、静的データを使う全ての XS モジュールではこれらのマクロを使うことが 強く推奨されます。
使うマクロのテンプレートを得るための最も簡単な方法は、h2xs に
-g
(--global
) オプションを指定することです
(h2xs を参照してください)。
以下はマクロを使ったサンプルモジュールです。
#include "EXTERN.h" #include "perl.h" #include "XSUB.h"
/* Global Data */
#define MY_CXT_KEY "BlindMice::_guts" XS_VERSION
typedef struct { int count; char name[3][100]; } my_cxt_t;
START_MY_CXT
MODULE = BlindMice PACKAGE = BlindMice
BOOT: { MY_CXT_INIT; MY_CXT.count = 0; strcpy(MY_CXT.name[0], "None"); strcpy(MY_CXT.name[1], "None"); strcpy(MY_CXT.name[2], "None"); }
int newMouse(char * name) char * name; PREINIT: dMY_CXT; CODE: if (MY_CXT.count >= 3) { warn("Already have 3 blind mice"); RETVAL = 0; } else { RETVAL = ++ MY_CXT.count; strcpy(MY_CXT.name[MY_CXT.count - 1], name); }
char * get_mouse_name(index) int index CODE: dMY_CXT; RETVAL = MY_CXT.lives ++; if (index > MY_CXT.count) croak("There are only 3 blind mice."); else RETVAL = newSVpv(MY_CXT.name[index - 1]);
void CLONE(...) CODE: MY_CXT_CLONE;
REFERENCE
このマクロは、XS モジュールのための静的データを参照するためのユニークな キーを定義するために使われます。 h2xs で使われている、推奨される命名スキームは、モジュール名、 文字列 "::_guts"、モジュールのバージョン番号、からなる文字列を 使うことです。
#define MY_CXT_KEY "MyModule::_guts" XS_VERSION
この構造体 typedef は常に my_cxt_t
から 呼び出されなければなりません
-- その他の CXT*
マクロは my_cxt_t
typedef 名の存在を仮定します。
インタプリタローカルにする必要がある全てのデータを含む構造体である
my_cxt_t
という名前の typedef を宣言します。
typedef struct { int some_value; } my_cxt_t;
my_cxt_t
の宣言の直後には常に START_MY_CXT マクロを置きます。
MY_CXT_INIT マクロは my_cxt_t
構造体のための保存場所を初期化します。
これは正確に 1 回だけ -- 典型的には BOOT: セクションで --
呼び出されなければなりません です。
もし複数のインタプリタを管理しているなら、既にあるインタプリタを
クローンしたもの以外では、インタプリタインスタンス毎に一度呼び出します。
(しかし後述する MY_CXT_CLONE
を参照してください。)
MY_CXT にアクセスする全ての関数で dMY_CXT マクロ(宣言) を使います。
my_cxt_t
構造体のメンバにアクセスするために MY_CXT マクロを使います。
例えば、もし my_cxt_t
が
typedef struct { int index; } my_cxt_t;
なら、これを index
メンバのアクセスに使います:
dMY_CXT; MY_CXT.index = 2;
dMY_CXT
は計算するにはかなりコストが高いので、それぞれの関数で起動する
オーバーヘッドを防ぐために、aMY_CXT
/pMY_CXT
マクロを使って
他の関数に宣言を渡すことができます; 例えば:
void sub1() { dMY_CXT; MY_CXT.index = 1; sub2(aMY_CXT); }
void sub2(pMY_CXT) { MY_CXT.index = 2; }
pTHX
と同様に、マクロが複数の引数の最初か最後のときのために、下線を
カンマとして表現した等価な形式があります; つまり
_aMY_CXT
, aMY_CXT_
, _pMY_CXT
and pMY_CXT_
です。
デフォルトでは、新しいインタプリタが既に存在するもののコピーとして
(つまり threads->create()
経由で)作成されたとき、
両方のインタプリタは同じ物理的な my_cxt_t 構造体を共有します。
(典型的にはパッケージの CLONE()
関数経由で) MY_CXT_CLONE
を呼び出すと、
構造体のバイト単位でのコピーが行われ、将来の dMY_CXT は代わりにコピーに
アクセスします。
引数として明示的にインタプリタを取るバージョンのマクロです。
これらのマクロは 同じ ソースファイルの中でだけ共に動作することに 注意してください; これは、あるソースファイルの dMY_CTX は他のソースファイルの dMY_CTX とは異なる構造体にアクセスするということです。
Perl 5.8 から、Perl インタプリタでのマルチスレッドの相互作用を正しく 扱うために、Perl はC/C++ のレベルでどうやってスレッド対応版の システム/ライブラリインターフェース (getpwent_r() など) で フロントエンドマクロ (getpwent() など) をラッピングするかを知っています。 これは透過的に行われるので、しなければならないのは Perl インタプリタを インスタンス化することだけです。
このラッピングは Perl コアソース (PERL_CORE が定義されます) か Perl コアエクステンションを (PERL_EXT が定義されます) をコンパイルするときは 常に行われます。 Perl コアの外側の XS コードをコンパイルするときは行われません。 しかし、(マルチスレッド操作が行われるようにコンパイルされた Perl の) _r-形式と、_r-なし形式を混ぜると、動作は未定義(一貫しない結果、データの 破壊、あるいはクラッシュすら十分あり得ます)で、全く移植性がありません。
ファイル RPC.xs
: 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 );
ファイル typemap
: RPC.xs のためのカスタム typemap。
TYPEMAP Netconfig * T_PTROBJ
ファイル 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";
このドキュメントは xsubpp
1.936 で対応している機能に対応しています。
原文は Dean Roehrich <roehrich@cray.com> によって書かれました。
1996 年から、The Perl Porters <perlbug@perl.org> によって 保守されています。