最近はMinillaでXSモジュールの雛形をつくることができます。
minil new -p XS Acme::MyModuele
すると、こんな感じのPerlコードと、
package Acme::MyModuele;
use 5.008001;
use strict;
use warnings;
our $VERSION = "0.01";
use XSLoader;
XSLoader::load(__PACKAGE__, $VERSION);
1;
こんな感じのXSコードが生成されます。
#ifdef __cplusplus
extern "C" {
#endif
#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#ifdef __cplusplus
} /* extern "C" */
#endif
#define NEED_newSVpvn_flags
#include "ppport.h"
MODULE = Acme::MyModuele PACKAGE = Acme::MyModuele
PROTOTYPES: DISABLE
void
hello()
CODE:
{
ST(0) = newSVpvs_flags("Hello, world!", SVs_TEMP);
}
XSはCをベースに独自のマクロを持った言語です。
上のコードでいうところの、 MODULE
、 PACKAGE
、 CODE:
などがそれに当たります。
詳しくは、 perlxs のドキュメントを読むといいでしょう。
XSの世界で定義したC関数(のように見えるモノ)はXSUBという特殊なC関数に変換され、Perlの世界から呼び出せるようになります。(普通のC関数ではなくなるため、hello()
という名前でもなくなります。)
上のコードで言うならば、 void hello()
はXSUBですが MODULE
と PACKAGE
より前の行に定義したC関数は普通のC関数になります。
なお、XSの関数はXSUBという特殊なC関数になるわけですが、中のコードは普通のCコードです。PerlのC APIを呼び出すことでPerlのデータ構造に対する操作を行う事ができます。
PerlのC APIは EXTERN.h
や perl.h
などで定義されているのでそれがつかえます。
PerlのAPIについては[perlapi] (http://perldoc.jp/docs/perl/5.12.1/perlapi.pod)や[perlguts] (http://perldoc.jp/docs/perl/5.20.1/perlguts.pod)を見るといいです。
また、Perlも歴史を経て新しいバージョンと古いバージョンでC APIのインターフェースが変わっている事があります。
それを吸収してくれるマクロが ppport.h
です。
Devel::PPPortとしてCPANに上がっています。
Minillaを使うと基本的に自動生成してくれるようになっています。
また、XSUtilを使ってこういう具合に指定しておくと各ビルド環境で生成するようにもできます。
Perlのデータ構造も普通のC構造体として定義されているので、普通のCコードから操作することができます。
そのため、Perlに対してAPIを提供する部分はXSUBで、単体として独立可能なコードは普通のCで書くのが個人的には良い書き方だと思います。
まとめ
XSこわくない!
CによるPerl拡張入門(α)がとても良いので読みましょう。
明日は @songmu さんです!