XSとPuerPerlの二つの実装を持つモジュールの書き方
XSとPurePerl(PP)両方の実装を持つモジュールは少なくない。たとえば:
- Data::Dumper
- List::Util/Scalar::Util (List-Scalar-Utils)
- List::MoreUtils
- Class::MOP
- Params::Util (>=0.35)
- Params::Validate
- Data::Util
- DBI
- JSON/JSON::XS
- Text::CSV/Text::CSV::XS
このように一般的に使われることなのだが、その書き方は多様で定説がない。
Data::Dumperは関数を別の名前で用意し、実装を切り替えるインターフェイスを持つ。
List-Scalar-UtilsはXSがデフォルトで、さらにXSでのみ提供している関数があり、そのような関数はインストールの状態によっては利用できない。なお、そのためにTask::Weakenという特定機能の実装を確認するためだけのディストリビューションが存在している。
Class::MOPはPPがデフォルトで、XS版は利用可能であれば呼び出され、既存のPP版を上書きする。
そしてParams::ValidateのようにValidatePP.pmとValidateXS.pmをバックエンドとして利用するものもあれば、JSONやText::CSVのようにPPがデフォルトでXS版を別のディストリビューションとして配布するものもある。
PP版のロードのしかたも様々で、Scalar-List-Utilsはいくつかのまとまりごとにeval()する方式だし、Params::Utilは関数ごとに個別にeval()する。Params::Validate/Data::Util/DBIなどのように別ファイルになっているものもある。
それでは、自分がデュアルバックエンドモジュールを書くときはどうするのか。XSのコンパイル時間が必要ないという特性を利用するには、デフォルトがXSでPP版は必要に応じてロードするのがいいだろう。その場合、開発効率を考えると、PP版のロードについてはeval()は避けたほうがいい。別ファイルのものをロードしたほうがデバッグしやすく、Deve::Coverageの出力も見やすく、プロファイルを分析しやすいからだ。
これらのことを踏まえると、現状では、上に挙げたようなモジュールをいくつか調べて必要な部分をカット&ペーストするのが最善だと思われる*1。
*1:これが冗談になる日が来るといいんだが。