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:これが冗談になる日が来るといいんだが。