モダンなXSの書き方

PerlソースコードPerl言語以上に変化が大きく,それに伴ってXSの書き方もだいぶ変わってきている。新しいAPIを使ったほうが可読性がよかったり高速だったり使いやすかったりする。

たとえば,スカラー(SV)から文字列(PV: pointer valueつまりchar*)を得るAPIの変遷は次のようになっている。

/* SV*からPVを得る古い書き方 */
STRLEN len;
const char* pv = SvPV(pv, len); # pvとlen両方ほしいとき
const char* pv_only = SvPV(pv, PL_na); # pvだけほしいとき

/* その後SvPV_nolen()が追加され,PL_naはdeprecatedに */
const char* pv_only = SvPV_nolen(pv);

/* 今は*_constが追加されたのでconst char*でいい場合はこちら */
STRLEN len;
const char* pv = SvPV_const(pv, len);
const char* pv_only = SvPV_nolen_const(pv);

SvPV_nolen_const()は「以降のコードで文字列を変更しない」という実行可能な宣言であり,プログラムが見やすくなる。また,COW*1を効かせることができるので実行速度の点でも優れている。

また,SvREFCNT_inc()も現在は用途に応じてバリエーションがある。もっとも,これについては新しいAPIは名前が長く少し使いにくい。

SvREFCNT_inc_simple(sv); // svを一時変数に格納しない
SvREFCNT_inc_void(sv);   // 値を返さない
SvREFCNT_inc_NN(sv);     // NULLチェックをしない(Not NULL)

/* これらは組み合わせることができる(順番は固定) */
SvREFCNT_inc_simple_void_NN(sv); // 最も単純で高速

また,PVリテラル(つまり文字列リテラル)をとるAPIには末尾にsが付くものがあり,それはリテラルのみ受け付け,自動的に文字列の長さもAPIに渡される新しいAPIである。

SV* sv  = newSVpsv("foo"); /* newSVpvn("foo", sizeof("foo")-1) */
SV**svp = hv_fetchpvs(hv, "foo", FALSE);
/* 他,*pv()には大抵*pvs()がある */

このリテラルバージョンが提供されるAPIは非常に沢山あるので,文字列リテラルを渡すときにはとりあえずAPIの末尾にsをつけてみると結構動いたりする。

他にもいくつかあるので,ある程度まとまったらperl-users.jpに載せたい。

*1:5.10.xで利用可能らしいが調べていない。