XSモジュールを作成する
Perlから呼び出すことのできるように作成したC言語(あるいはC++)の関数のことをエクステンションといいます。一般的にはXSと総称して呼ばれることが多いようですので、XSと呼ぶことにします。XSモジュールを作成してPerlから呼び出すためのモジュールの作成までの流れを書きたいと思います。次のような作業の流れになります。
- XSファイルの作成
- XSファイルからC言語のソースファイルを生成
- C言語のソースファイルからダイナミックリンクライブラリを生成
- ダイナミックリンクライブラリを呼び出すためのPerlモジュールの作成
C言語がまったくわからないという方は、以下の講座をご用意しています。
XSモジュール作成の流れ
XSファイルの作成
最初はXSファイルの作成です。拡張子は(.xs)です。ファイルの中にはC言語のソースコードとXSUBが含まれています。XSUBはXS言語で書かれC言語の関数にとてもよく似ています。実質としてはC言語の関数を記述するものだと思ってください。
# C言語セクション(ライブラリの読み込みなど) ... # XSUBセクション(XS言語で書かれる) 戻り値 関数名 型 処理(処理の部分はC言語) 戻り値 関数名 型 処理(処理の部分はC言語)
XSファイルからC言語のソースファイルを生成
XSファイルをxsubppと呼ばれるプログラムを使って、C言語のソースファイルに変換します。このときにtypemapと呼ばれる、C言語のPerlの型を相互変換するため定義が利用されます。typemapはExtUtils::MakeMakerモジュールに含まれています。
xsubpp XSファイル + typemap ------------> C言語ソースファイル
XS言語を利用すると、多くの場合C言語とPerlの型の変換の処理を自分で書く必要がありません。xsubppがtypemapファイルを参照して、自動的に変換処理を追加してくれます。変換処理を自分で書くことやtypemapを拡張することもできます。
ダイナミックリンクライブラリの生成
C言語ソースファイルはCのコンパイラ(gccなど)を使ってコンパイルされ、ダイナミックリンクライブラリ(*.soなど)に変換されます。
Cコンパイラ C言語ソースファイル ------------------> ダイナミックリンクライブラリ
ダイナミックリンクライブラリは動的に呼び出すことが可能なライブラリのことです。通常はこの形式でライブラリは作成されます。
ダイナミックリンクライブラリを呼び出すためのPerlモジュールの作成
最後にダイナミックリンクライブラリを呼び出すためのPerlのモジュールを作成します。XSLoaderというモジュールを使うと、ダイナミックリンクライブラリを読み込んで、Perlの関数として、ダイナミックリンクライブラリに書かれた関数を呼び出すことができるようになります。
package YourPackage; use XSLoader; XSLoader::load 'YourPackage', $YourPackage::VERSION;
XSLoaderは@INCに含まれる検索パスのautoというディレクトリの下にあるダイナミックリンクライブラリを読み込みます。ダイナミックリンクライブラリは、Windowsではdll、Linux等ではsoという拡張子です。
実際にXSモジュールを作成するには
上記の流れをしっかりと覚えておきましょう。XSモジュールを作成する場合はどのようなものを作成する場合でも流れは同じです。プログラマが専念する必要があるのはXSファイルの作成です。残りのほとんどの作業は自動化することができますので、簡単なXSモジュールの作成は難しいものではありません。
ただし一般的にいえばXSモジュールの作成は難しいものです。「XS言語」「typemap」「Perl API」「make」「Cコンパイラ」「リンカ」「ExtUtils::MakeMaker」などのそれほど簡単ではない周辺知識を大量に必要とするからです。ですから、まず基本きっちり押さえた上で、周辺知識を身につけることが大切です。
XSモジュールの作成 - h2xsコマンド
XSモジュールを作成するにはh2xsというコマンドを使用します。
# h2xsコマンド h2xs [オプション] C言語ヘッダファイル
このコマンドは本来はC言語のヘッダファイルからXSモジュールを作成するものです。
そのためh2xs(つまり「C言語のヘッダファイル to XS」という意味)という名前がついていますが、単にXSモジュールの雛形を作成するのにも利用できます。
XSモジュールを作成するには次のオプションでh2xsを実行します。
# XSモジュールの作成 h2xs -A -n SomeModule
「-A」はオートローディングの機能を省くオプションです。XSモジュールを作成するときは通常オートローディングの機能を省きます。「-n」でXSモジュールの名前を指定します。
このコマンドを実行するとカレントディレクトリに「SomeModule」という名前のディレクトリが作成されます。
モジュールの構成
作成されたモジュールは次のような構成になっています。
Changes SomeModule.xs lib - SomeModule.pm Makefile.PL MANIFEST ppport.h README t - SomeModule.t
簡単にそれぞれのファイルの役割について解説したいと思います。
- Changes - モジュールの変更履歴を記述します。
- SomeModule.xs - XSファイル。XS言語でXSUBを記述します。
- lib - Perlのモジュールが格納されます。このモジュールからC言語で書かれたライブラリが呼び出されます。
- Makefile.PL - C言語ソースファイルのコンパイルやモジュールのインストールなどのためのmakeファイルを生成するためのPerlスクリプトです。
- MANIFEST -XSモジュールを配布するときに必要なファイルを記載します。
- ppport.h - 古いPerlとの互換性を維持するためのC言語ヘッダファイルです。
- README - モジュールの簡単な説明です。
- t - テストスクリプトが格納されます。
この中で一番大切なのは「SomeModule.xs」です。最も簡単なXSを書く場合に記述する必要があるのは「SomeModule.xs」だけです。少し難しいことをするためには、Makefile.PLの書き方を覚える必要があります。lib以下に含まれるPerlモジュールのファイルに追加で記述することもあるでしょう。また配布用にパッケージ化するときには、Changes、MANIFEST、READMEを書く必要があります。またテストスクリプトを書くことも必要になります。
Makefile.PLの書き方については以下で詳しく解説しています。
XSファイルの雛形
では最初にSomeModule.xsを開いてみてください。次のような雛形が生成されているはずです。
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = SomeModule PACKAGE = SomeModule
XSファイルの先頭にはC言語の記述を行うことができます。「#include "EXTERN.h"」などはC言語の記述です。C言語を書いたことがない人のために少し解説をしておきます。「#include」は他のファイルをソースコードに取り込むためのものです。C言語では#から始まる命令はディレクティブと呼ばれ、プリプロセッサに対する命令になります。プリプロセッサとはソースコードをコンパイルする前に、ディレクティブの指定に従ってソースコードに変更を加えるプログラムのことです。
#include "EXTERN.h"
という記述は、EXTERN.hというC言語のヘッダファイルをソースコードの中に取り込むということを意味しています。
「.h」という拡張子を持つファイルはC言語のヘッダファイルです。ヘッダファイルというのは、C言語の関数の宣言やマクロの定義が記述されているファイルです。C言語のソースコードの中でヘッダに書かれた関数を使用したい場合にインクルードする必要があります。
XSファイルを書く場合に必要になるC言語ヘッダファイルについて簡単に解説しておきます。
- EXTERN.h - Perlのグローバル変数を参照するために必要なヘッダファイル
- perl.h - Perlのヘッダファイル
- XSUB.h - XSUBのためのヘッダファイル
- ppport.h - Perlのバージョン間のAPIの差異を吸収するためのヘッダファイル
次の記述からXS言語での記述が始まります。
MODULE = SomeModule PACKAGE = SomeModule
MODULEはXS言語の始まりと、定義される関数の名前空間を指定するのに使われます。これはPerlのパッケージとは異なるので注意してください。本来C言語というのは名前空間を持たない言語です。簡単な名前の関数を定義したとするならば、衝突してしまうことでしょう。XSファイル内ではXSUBと呼ばれる関数を定義することになりますが、XSUBの関数名はシンプルなものでかまいません。たとえば数値を2倍する関数であれば、twiceという名前でよいでしょう。けれども、これをC言語でそのまま利用したとするならば、名前の衝突が発生するでしょう。そこでこのtwiceという名前の関数はxsubppで処理されるときに「XS_SomeModule_twice」という名前に置き換えられます。衝突しない仕組みを与えるために必要になるのがMODULEです。
PACKAGEはPerlのパッケージ名です。つまりMODULEの記述とPACKAGEの記述でPerlとC言語の関数の間に次のような対応ができることになります。このような対応を作成して、PerlからC言語の関数を呼び出せるようにすることをブートストラップ(bootstrap)するといいます。
Perl C言語 SomeModule::twice ---> XS_SomeModule_twice
XSUBの記述
ここからはXS言語を記述する部分です。XS言語を使ってXSUBを記述してみましょう。XSUBはXS言語を使った関数の記述で、C言語の関数とよく似ています。以下では受け取った整数を2倍する関数を書いています。
void twice (...) PPCODE: { // 引数の個数をチェック if (items != 1) { croak("Usage twice(x)"); } // Perlのスカラをintに変換。「ST(0)」は第一引数 int x = SvIV(ST(0)); // 2倍を計算 int x2 = x * 2; // intをPerlのSVに変換して、戻り値に設定。XPUSHsは、戻り値をひとつ積む。 XPUSHs(sv_2mortal(newSViv(x2))); // 戻り値の個数を指定してreturn XSRETURN(1); }
itemsという変数には引数の個数が代入されているのでチェックに利用できます。croakという関数を使って、エラーメッセージを出力してプログラムを終了することができます。
XSで書かなければいけないことは、型変換という作業につきます。引数で受けとったPerlの型をC言語の型に変換します。それから、C言語による処理を行って、C言語の型を、再びPerlの型に変換します。型変換については、後ほど詳しく解説します。
また以下の形はそのまま覚えてしまいましょう。
void 関数名(...) PPCODE: { }
XSにおけるPerlの型については以下で詳しく解説しているので、参考にしてください。
コンパイルと実行
ここまでで解説したようにPerlのモジュールから利用するためには、XSファイルをxsubppで処理し、C言語ソースファイルをコンパイルして、ダイナミックリンクライブラリを作成する必要がありました。けれどもこの処理はmakeというプログラムによって自動化することができます。Makefile.PLはこの作業を自動化するmakeファイルを生成してくれます。ですから以下のコマンドを実行するだけです。
# makeファイルの生成 perl Makefile.PL # xsubppの処理とコンパイル make
成功しない場合はXS言語に間違いがある可能性があります。この作業が終わるとダイナミックリンクライブラリやPerlのモジュールが含まれるblibというディレクトリが生成されます。
モジュールを呼び出すスクリプトを記述してみましょう。XSファイルがあるディレクトリと同じディレクトリにtest.plというファイルを作成してください。
# test.pl use SomeModule; print SomeModule::twice(2);
このスクリプトは次のように実行できます。blibのモジュールを読み込むために、blibというモジュールを-Mオプションを使って読み込む必要があります。
# C言語で書かれた関数を実行 perl -Mblib test.pl
出力結果は次のようになりました。
4
これでXSの基礎を学び終えました。これをアレンジすれば、XSを応用することができます。