参考にしたサイト集
- perlxs - perldoc.perl.org
- perlxstut - perldoc.perl.org
- perlguts - perldoc.perl.org
- perlapi - perldoc.perl.org
- http://d.hatena.ne.jp/tokuhirom/20081209/1228829454
- Perl XS メモ - typemap と MAGIC をからめて - daily dayflower
- http://pub.ne.jp/wakapon/?entry_id=1109553
- http://pub.ne.jp/wakapon/?entry_id=1109664
型
- SV
- AV
- 配列
- HV
- ハッシュ
- CV
- コード
- GV
- グロブ
- RV
- リファレンス
- IV
- 整数
- NV
- 実数
- PV
- 文字列
PerlIO_printf と printf
use MyXS; binmode STDOUT, ":encoding(cp932)"; # Windows のエンコーディングを指定 MyXS::hoge();
MODULE = MyXS PACKAGE = MyXS void hoge() CODE: PerlIO_printf(PerlIO_stdout(), "あ\n"); // Windows で化けない fprintf(stdout, "あ\n"); // Windows で化ける
typemap
lib/ExtUtils/typemap にデフォルトの typemap があるので、 int とか返してもおk。基本的に typemap がないと変換できない。
たとえば、lib/ExtUtils/typemap に以下のように書いてあるから
bool T_BOOL INPUT T_BOOL $var = (bool)SvTRUE($arg) OUTPUT T_BOOL $arg = boolSV($var);
以下のようなコードを書いたときに
bool hello(b) bool b CODE: RETVAL = b; OUTPUT: RETVAL
以下のような C が生成されて
XS(XS_Mytest_hello); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mytest_hello) { #ifdef dVAR dVAR; dXSARGS; #else dXSARGS; #endif if (items != 1) Perl_croak(aTHX_ "Usage: %s(%s)", "Mytest::hello", "b"); PERL_UNUSED_VAR(cv); /* -W */ { bool b = (bool)SvTRUE(ST(0)); // typemap の INPUT の定義でこれが作られた bool RETVAL; #line 14 "Mytest.xs" RETVAL = b; #line 40 "Mytest.c" ST(0) = boolSV(RETVAL); // これは OUTPUT の定義 sv_2mortal(ST(0)); } XSRETURN(1); }
自動で Perl の型に変換される。
リファレンスカウンタ
int get_count(sv) SV* sv CODE: RETVAL = SvREFCNT(sv); OUTPUT: RETVAL
my $a = ""; my $b = \$a; my $c = $b; print Mytest::get_count($a) . "\n"; # => 3
SvREFCNT_inc
SvREFCNT_dec
mortal
mortal にしとけばリファレンスカウンタが 0 になったら自動で GC してくれる
newSV** をしたときは、 sv_2mortal をセットで呼び出すようにしとけばいい。
sv_2mortal(newSVnv(0.1));
でも、返り値は、
SV* hello() CODE: RETVAL = newSVnv(0.1); OUTPUT: RETVAL
とか mortal にしなくても大丈夫。
C になったときに以下のように sv_2mortal が呼ばれるから
XS(XS_Mytest_hello); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mytest_hello) { #ifdef dVAR dVAR; dXSARGS; #else dXSARGS; #endif if (items != 0) Perl_croak(aTHX_ "Usage: %s(%s)", "Mytest::hello", ""); PERL_UNUSED_VAR(cv); /* -W */ { SV * RETVAL; #line 13 "Mytest.xs" RETVAL = newSVnv(0.1); #line 39 "Mytest.c" ST(0) = RETVAL; sv_2mortal(ST(0)); // <- ここで勝手にモータル化 } XSRETURN(1); }
シンボルテーブルハッシュ
gv_stashsv とか gv_stashpv を使う。
HV* get_stash(sv) SV* sv CODE: // 第二引数は、シンボルテーブルがなかったら空のシンボルテーブルを作るかどうか。 RETVAL = gv_stashsv(sv, true); OUTPUT: RETVAL
クラス名を知る
SvSTASH でシンボルテーブルハッシュを取得、シンボルテーブルハッシュの名前を取得
char* get_blessed_name(sv) SV* sv CODE: RETVAL = HvNAME(SvSTASH(SvRV(sv))); OUTPUT: RETVAL
Devel::Peek 便利
use Devel::Peek "Dump"; print Dump(MyXS::hoge()) . "\n";
とか書いておけば、リファレンスカウンタの値とか見れて超便利。
複数の値(リスト)を返すとき
PPCODE を使う
void get_child_window(h, type) HWND h; int type; PPCODE: std::vector<HWND> v; EnumChildWindows(h, reinterpret_cast<WNDENUMPROC>(addhwnd), reinterpret_cast<LPARAM>(&v)); for (std::vector<HWND>::iterator i = v.begin(); i != v.end(); ++i) XPUSHs(sv_2mortal(newSViv(reinterpret_cast<IV>(*i)))); // XPUSHs。ちゃんと自分で mortal 化する
UTF8 フラグを on にして文字列を返す
SV* get_text() CODE: RETVAL = newSVpvn("ほげほげ", sizeof("ほげほげ") - 1); SvUTF8_on( RETVAL); OUTPUT: RETVAL
(つづくかも)