IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

私的 XS メモ

準備

$ h2xs -A -n MyXSS
Makefile.PL
CC => 'g++',

  • 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

(つづくかも)