はじめに
今回は、PerlとC言語をシームレスに連携させる Inline::C モジュールについて記載します。
Perl自体はC言語で実装されています。そのためC言語との親和性は非常に高く、Perlスクリプトの中からC言語のコードを直接呼び出すことができます。
Inline::C モジュールを使用することで、Perlスクリプトの中に直接Cのコードを記述し、実行時に自動的にコンパイルして利用することが可能になります。
use Inline C => <<'END_C';
void greet(char* name) {
printf("Hello, %s! from C code\n", name);
}
int add(int x, int y) {
return x + y;
}
END_C
greet("Perl Monger");
print "1 + 2 = ", add(1, 2), "\n";
これを実行すると、初回実行時に自動的にCコンパイラが起動し、共有ライブラリが生成・ロードされます。
生成されたバイナリは、カレントディレクトリに作成される _Inline/ ディレクトリ内にキャッシュされます。そのため、2回目以降はコンパイル不要で、非常に高速に動作します。
Perl内部データ構造へのアクセス
Inline::C の強力な点は、C言語側からPerlの内部データ構造(API)に直接アクセスできることです。
先ほどの例では char* や int を引数にしていましたが、この場合 Inline::C が持つ「Typemap」という仕組みにより、Perlの値とCの型が自動的に変換されます。
一方、引数を SV* (Scalar Value) という構造体へのポインタとして受け取ることで、自動変換を行わずにPerlのスカラー変数を直接操作することができます。
use Inline C => <<'END_C';
void dump_sv(SV* sv) {
// SvIOK: 整数として有効か判定
if (SvIOK(sv)) {
printf("It is an Integer: %d\n", SvIV(sv));
}
// SvPOK: 文字列として有効か判定
else if (SvPOK(sv)) {
printf("It is a String: %s\n", SvPV_nolen(sv));
}
else {
printf("It is something else...\n");
}
}
void mutate_sv(SV* sv) {
// 引数で渡された変数の値を直接書き換える
sv_setpv(sv, "Modified by C");
}
END_C
my $v = 100;
dump_sv($v);
$v = "Perl";
dump_sv($v);
mutate_sv($v);
print $v; # "Modified by C"
SvIV(整数値の取得)、SvPV(文字列値の取得)、sv_setpv(文字列値の設定)といったPerl APIを駆使することで、C言語のスピードを生かした処理や、高度なメモリ操作が可能になります。
ただし、C言語レベルでのメモリ操作となるため、間違うとプロセスが落ちる危険性もあります。
他言語との連携
Inline モジュールファミリーはC言語だけでなく、Python, Java, Ruby など、様々な言語に対応しています。
use Inline Python => <<'END_PY';
def python_func():
print("I am Python running inside Perl")
END_PY
python_func();
※ Inline::Python などを利用するには、別途CPANから各モジュールのインストールが必要です。
これにより、既存の他言語ライブラリをPerlから利用したり、特定の処理だけを別の言語で記述したりといった柔軟な開発が可能になります。
まとめ
全5回にわたり、シンボルテーブル、型グロブ、tie、ソースフィルタ、Bモジュール、そしてInline::Cと、Perlの内部構造や高度な機能について記載してきました。
これらの機能は、日常的なWebアプリケーション開発で頻繁に使うものではないかもしれませんが、言語が裏側でどのように動作しているかを知ることは、デバッグ能力の向上や、より効率的なコードを書くための基礎となります。
参考:
https://perldoc.jp/
https://metacpan.org/dist/Inline-C/view/lib/Inline/C.pod
本連載が、Perlという言語の奥深さを知るきっかけになれば幸いです。