地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Perl】CによるPerl拡張 XSに入門する

概要

休み明け一発目から古のperlを使った謎モジュールのビルドエラーの確認作業になった。cもmakefileもそれなりに読めるし余裕だろって思ってたらそんなことはなかったのでメモ

XSでハマったというよりはそもそもXSが何なのかをそんなに理解してなかったので理解用に書く

XSとは

XS は Perl と(Perl と一緒に使いたい)C のコード(または C ライブラリ)との 間の拡張インターフェースを作るのに使われるインターフェース記述 ファイルフォーマットです。 
XS インターフェースはライブラリと動的または静的にリンクされて、 Perl とリンクすることのできる新しいライブラリを生成します。 
XS インターフェース記述はは XS 言語で書かれており、 Perl 拡張インターフェースのコアコンポーネントです。

perldoc.jp

XSはCをベースに独自のマクロを持った言語。PerlからCコードを呼び出すぐらいにしかわかってなかったけどリファレンス読むと思ったより高機能なのが分かる

XS 言語の特徴

拡張子は(.xs)でファイルの中にはC言語ソースコードとXSUBが含まれます。XSUBはXSで言語で書かれC言語の関数にぱっと見は似ています。正直拡張を見ないとCの独自仕様かな?と思うくらいには似ているので理解するのも難しいです。

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

#include "ppport.h"


MODULE = SomeModule       PACKAGE = SomeModule  

なのでXSを取得するにCとPerlあたりの知識が必須となっている。

使ってみる

minilという雛形生成ツールを使って最小コードのxsを生成してみた。

qiita.com

ぱっと見はC。SVとかその辺はそのままperlの用語に紐づく形となっている

#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);
}

SVの操作

XSからPerlのデータを操作する方法

SV の中身をダンプして出力

sv_dump(sv)

新しい SV をつくりたい。整数値から SV をつくりたい

SV* sv = newSViv(5963);

符号なし整数値から SV をつくりたい

SV* sv = newSVuv(5963);

文字列から SV をつくりたい

SV* sv = newSVpvn("hello", strlen("hello"));

SV から SV をつくりたい

SV* new_sv = newSVsv(sv);

SV の値が真か偽かがしりたい

bool b = SvTRUE(sv);

sprintf したい。

SV* sv = newSVpvf("%d", 3);

参照カウンターをインクリメントしたい

SvREFCNT_inc(sv);

参照カウンターをデクリメントしたい

SvREFCNT_dec(sv);

ppport.hのインターフェース

metacpan.org

PerlのC APIを呼び出すことでPerlのデータ構造に対する操作を行う事ができるのはなんとなく上まで書いていて理解はできました。CのAPIPerlの歴史が経つにつれて変わっていくのが上記のCPANで公開されているモジュールから見て取れます。

モジュールを使うことである程度の変更は吸収してくれるようですが例えばperl5.10で追加された関数を使ったモジュールを5.8のperlでビルドしようとすると失敗するみたいなことは往々にして起き得ます。

仮に存在しない関数を使った場合この辺はビルド時にエラーになるので関数名がわかったら上記のcpanchangelogを眺めてどこで追加されたのかを確認するのが良さそうです。

参考

xsubtut.github.io

tutorial.perlzemi.com