Perl Magicは意外と簡単に使える

PerlのC APIにはMagicという考え方があって,任意のSVにフックとプライベートデータを付けることができる。このAPIはXSからでも利用できるのだが,難しそうで敬遠していた。しかし,試してみると意外と簡単に使えることが分かったのでメモしておく
WeakRef::Auto http://search.cpan.org/dist/WeakRef-Auto/

使い方は意外と簡単で,フックしたい操作のためのメソッドを入れたVirtual Tableを用意して,sv_magicext()を呼び出すだけ。

まずVirtual Tableと識別のためのユーティリティをCセクションに置く:

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

typedef SV* SVREF;

static int
autoweak_set(pTHX_ SV* const sv, MAGIC* const mg){
	(void)(mg); /* unused */

	if(!SvWEAKREF(sv)){
		sv_rvweaken(sv);
	}

	return 0; /* success */
}

MGVTBL autoweaker_vtbl = {
	NULL, /* get */
	autoweak_set, /* set */
	NULL, /* len */
	NULL, /* clear */
	NULL, /* free */
	NULL, /* copy */
	NULL, /* dup */
#ifdef MGf_LOCAL
	NULL,  /* local */
#endif
};

そしてXSセクションにsv_magicext()を呼び出すXSUBを書く:

MODULE = WeakRef::Auto	PACKAGE = WeakRef::Auto

PROTOTYPES: DISABLE

void
autoweaken(SVREF var)
PROTOTYPE: \$
CODE:
	SvGETMAGIC(var);

	if(SvREADONLY(var)){
		Perl_croak(aTHX_ PL_no_modify);
	}

	if(!isautoweak(aTHX_ var)){
		sv_magicext(var, NULL, PERL_MAGIC_ext, &autoweaker_vtbl, NULL, 0);
		SvSETMAGIC(var);
	}

これで,&autoweaken($var)すると$varにMagicが掛かり,$varに対する代入と共にCのautoweak_set()が呼び出される。$varにはリファレンスかundefのみ入れることができ,そのリファレンスは常にウィークリファレンスになる。

ところで,Magicメソッドの呼び出しはPerlレベルでの関数呼び出しを伴わないからさぞ高速だろうと思ったので検討してみた。

$ svn co http://coderepos.org/share/lang/perl/Variable-Constraint-Class

しかし,実際には特に高速ということはなかった。XSUBであるData::Util::instance()と比較すると速度はまったく変わらない。MagicはXSからしか利用できない*1ことを考えると,型制約のためにMagicを使う利点は特にないようだ。

*1:tieメカニズムもMagicの一種だが,非常に遅いので速度面での利点はまったくない。