アルパカ三銃士

〜アルパカに酔いしれる獣たちへ捧げる〜

Gotanda.pm #16 で 「XS で作るクロージャ」というタイトルで LT しました

gotanda-pm.connpass.com

で LT してきた。

XS を触ったことない人にとっては内容がちんぷんかんぷんだったはずなため、そのカバーをこの記事でできればいいなと思う。

今回作成しようとしたクロージャは次のような、どこかで作成した一つの値をサブルーチン内に保存して返すようなものである。

my $i = 0;
my $c = sub { $i };

まず XSUB は extarnal subroutine の略で、その名の通り Perl コードとしてではなく外部にサブルーチンを定義することを意味している。
今回の LT で紹介した 2 パターンの内 XS(subroutine_name) で定義した場合のコードをちょこっとおさらいしたい。

XS(XS_prototype_getter)
{
    dVAR; dXSARGS;
    SV *retval = (SV *)CvXSUBANY(cv).any_ptr;
    SP -= items; /* PPCODE */
    PUTBACK;
    push_values(aTHX_ retval);
}
  • dVAR; dXSARGS; が行うことはグローバルポインタやローカル変数のセットアップである。あんまり深いことを考えず、XS(subroutine_name) を書く場合はおまじないとして書くと良い。
  • (SV *)CvXSUBANY(cv).any_ptr; はどこかで作った値を cv に結びつけて、サブルーチン内で取り出すことができる。 any_ptr は一つのフィールドなため、詳しくは Perl Structures を参照してほしい。
  • SP -= items; PUTBACK; は返り値のためのスタックアドレスへ移動してくれる。PPCODE と同じような役割を持つ。
  • push_values(aTHX_ retval); でスタックへ返り値を push している。

それでクロージャを作成する部分はこのように書く。

static CV *
make_closure(pTHX_ SV *retval)
{
    CV *xsub;
    xsub = newXS(NULL /* anonymous */, XS_prototype_getter, __FILE__);
    CvXSUBANY(xsub).any_ptr = (void *)retval;
    return xsub;
}
  • 引数で SV を受け取った後、作成するクロージャ(CV) へ引数で受け取った SV を紐付けする。

このようにクロージャを割と簡単に作成することができる。

XS 始めたい場合、以下のページを参考にすると簡単に入門ができるはず。

www.perl.com

一応サンプルコードも書いたので是非。

github.com