perlembed - C プログラムへの Perl の埋め込み方
あなたのやりたいことは
バッククォートに関することと、 perlfuncにあるsystem
、exec
に関することを読みましょう。
"do" in perlfunc、 "eval" in perlfunc、"require" in perlfunc、 "use" in perlfuncを読みましょう。
デザインを考え直しましょう。
読み続けてください…
C プログラムをコンパイルする
C プログラムに Perl インタプリタを追加する
C プログラムから Perl サブルーチンを呼び出す
C プログラムから Perl の文を評価する
C プログラムから Perl のパターンマッチングや置換を実行する
C プログラムから Perl のスタックを扱う
永続的(persistent)インタプリタを取り扱う
複数のインタプリタのインスタンスを取り扱う
C ライブラリを使っている Perl のモジュールを C プログラムから使う
Win32 における組み込み Perl
このドキュメントにあるスクリプトのコンパイルがうまく行かなくても、 あなたは孤独ではありません。 基本的なルールはこうです: あなたが Perl をコンパイルしたときと全く同じやり方でプログラムを コンパイルしなさい。 (叫んでしまってすみません。)
また、Perlを使うすべてのCプログラムは perl ライブラリ にリンクして いなければなりません。 それって何、と聞きますか? Perl それ自身が C で書かれています。 Perl ライブラリはあなたの perl 実行ファイル(/usr/bin/perl か これに等価な)を作るために使われたコンパイル済みの C プログラムの 集合体です(あなたの使うマシンで Perl がコンパイルされているか、正しく インストールされていなければ C プログラムから Perl を使うことはできません。 これが、Perl の実行ファイルをあるマシンからマシンへ lib ディレクトリ 抜きでコピーしてしまうような軽率なことをすべきでないという理由です)。
Perl を C から使うとき、その C プログラムは -- 普通は -- perl ライブラリで 定義されている PerlInterpreter オブジェクトの割り付け、「実行」、解放を 行います。
あなたの使っている Perl がこのドキュメントの内容にあっている(バージョン 5.002 以降)であるのであれば、perl ライブラリ(とこれも必要な EXTERN.H と perl.h)が次のようなディレクトリに 置かれていることでしょう。
/usr/local/lib/perl5/your_architecture_here/CORE
あるいは以下のような場所かもしれませんし
/usr/local/lib/perl5/CORE
こうかもしれません
/usr/opt/perl5/CORE
CORE の場所を見つけ出すために以下のような文を実行します。
perl -MConfig -e 'print $Config{archlib}'
次のセクション "Adding a Perl interpreter to your C program" に ある例をコンパイルするこうします(私のLinux boxでの場合)
% gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include
-I/usr/local/lib/perl5/i586-linux/5.003/CORE
-L/usr/local/lib/perl5/i586-linux/5.003/CORE
-o interp interp.c -lperl -lm
(これは実際には一行です。) 私の使う DEC Alpha 上で古い 5.003_05 を使う場合では、オマジナイはちょっと 違います:
% cc -O2 -Olimit 2900 -DSTANDARD_C -I/usr/local/include
-I/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE
-L/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE -L/usr/local/lib
-D__LANGUAGE_C__ -D_NO_PROTO -o interp interp.c -lperl -lm
追加するのものを見つけるにはどうすればよいでしょうか? あなたの使う Perl が 5.001 より後のものであれば、perl -V
を実行して、 "cc" と "ccflags" の情報に特に注意して見ましょう。
あなたは、あなたの使うマシンのために適切なコンパイラ(cc, gcc など)を選ぶ必要があります。 perl -MConfig -e 'print $Config{cc}'
はあなたに使うべき コンパイラを教えてくれるでしょう。
同様に、適切なライブラリディレクトリ(/usr/local/lib/...) も 選択せねばなりません。 もしあなたの使うコンパイラが幾つかの関数が 未定義であるとか、-lperl が見つからないという報告をしてきたら、 -L
の後に続くパスを変更する必要があります。 EXTERN.h や perl.h が見つからないというエラーであれば、 -I
の後にあるパスを変更する必要があります。
おそらく、さらにライブラリを追加指定する必要があるでしょう。 どれを? おそらく以下のようにすれば表示されます:
perl -MConfig -e 'print $Config{libs}'
あなたの使っている perl のバイナリが適切なコンフィギュレーションが 行われているもので、かつ ExtUtils::Embed モジュールが インストールされていれば、これによって必要な全ての情報が決定できます。
% cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
ExtUtils::Embed モジュールは Perl 配布パッケージに含まれていなければ、 http://www.perl.com/perl/CPAN/modules/by-module/ExtUtils/ から 入手できます(もしこのドキュメントがあなたの持っている Perl 配布パッケージにあったものならば、あなたは 5.004 以降のものを 使っているはずで、すでにそのモジュールを持っているはずです。)
CPAN にある ExtUtils::Embed キットにもこのドキュメントにある例の すべてのソースコードがあり、テストと追加された例、その他の情報が あります。
ある意味で、perl(C プログラムとしての perl)は組み込み Perl(言語としての Perl)の良い例です; ですから、ソース配布にある miniperlmain.c を 使って組込みのデモンストレーションをします。 以下に示すのは、組込みの本質を持っていて配布ソースにも含まれている (そして粗悪で移植性に欠ける)miniperlmain.c です。
#include <EXTERN.h> /* from the Perl distribution */
#include <perl.h> /* from the Perl distribution */
static PerlInterpreter *my_perl; /*** The Perl interpreter ***/
int main(int argc, char **argv, char **env)
{
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct(my_perl);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
perl_parse(my_perl, NULL, argc, argv, (char **)NULL);
perl_run(my_perl);
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
ここで、env
ポインタを使っていないことに注意してください。 通常は perl_parse
はその最終引数として env
を取りますが、 ここではカレントの環境をそのまま使うことを示す NULL
に置き換えられています。
PERL_SYS_INIT3() と PERL_SYS_TERM() のマクロは、Perl インタプリタを 実行するのに必要な C ランタイム環境の、システム固有の調整を提供します; これらはインタプリタが何回作成または破壊されたかに関わらず、1 回だけ 呼び出されるべきです。 最初のインタプリタを作る前に PERL_SYS_INIT3() を呼び出し、 最後のインタプリタを解放した後に PERL_SYS_TERM() を呼び出します。
PERL_SYS_INIT3() は env
を変更するかもしれないので、perl_parse() の 引数として env
を提供する方がより適切です。
また、どんな引数を perl_parse() に渡したかに関わらず、PERL_SYS_INIT3() は C main() argc, argv, env から一度だけ起動されなければなりません。
ここでこのプログラム(interp.c と呼びましょう)をコンパイルして 実行ファイルを作りましょう。
% cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
コンパイルに成功すれば、perl そのものと同じように interp を使うことができるでしょう。
% interp
print "Pretty Good Perl \n";
print "10890 - 9801 is ", 10890 - 9801;
<CTRL-D>
Pretty Good Perl
10890 - 9801 is 1089
または
% interp -e 'printf("%x", 3735928559)'
deadbeef
perl_run を呼び出す前に argv[1] にファイル名を置くことで、 C プログラムの中ほどで Perl 文をファイルから読み込んで実行するという こともできます。
独立した Perl サブルーチンを呼び出すために、perlcall に記載されている call_* の類の関数を使うことができます。 次の例では call_argv
を使います。
以下に示すのは、showtime.c という名前のプログラムです。
#include <EXTERN.h>
#include <perl.h>
static PerlInterpreter *my_perl;
int main(int argc, char **argv, char **env)
{
char *args[] = { NULL };
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct(my_perl);
perl_parse(my_perl, NULL, argc, argv, NULL);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
/*** skipping perl_run() ***/
call_argv("showtime", G_DISCARD | G_NOARGS, args);
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
showtime は引数をとらない Perl サブルーチン(G_NOARGS)で、その 戻り値を無視します(G_DISCARD)。 これらのフラグ等は perlcall に説明されています。
showtime.pl と呼ばれるファイルで、showtime サブルーチンを 定義しましょう:
print "I shan't be printed.";
sub showtime {
print time;
}
単純にして十分です。 さあコンパイルして実行してみましょう:
% cc -o showtime showtime.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
% showtime showtime.pl
818284590
1970 年 1 月 1 日(UNIX 紀元の開始時点)からこのセンテンスを書いた時点までの 経過秒数が得られます。
この特殊な状況では、私たちは perl_run を呼ぶ必要はありません; perl_destruct で END ブロックを実行する PL_exit_flag PERL_EXIT_DESTRUCT_END をセットしているからです。
Perl サブルーチンに引数を渡したいという場合、call_argv に 渡される NULL
で終端されたリスト args
に文字列を追加することが できます。 他のデータ型を使ったり戻り値をチェックするには Perl スタックを 操作する必要があるでしょう。 これは本ドキュメントの最後の セクション "Fiddling with the Perl stack from your C program" で 説明されています。
Perl は Perl コードのかけら(pieces of Perl code)を評価するための二つの API 関数を提供しています。 "eval_sv" in perlapi と "eval_pv" in perlapi です。
これらの関数は、C プログラムの中で Perl のコードの断片を実行するのに 必要だったルーチンにすぎません。 あなたはコードを好きなだけ長くでき、複数の文を含むことができます; また、外部の Perl ファイルを取り込むために "use" in perlfunc, "require" in perlfunc, "do" in perlfunc を使うことができます。
eval_pv は独立した Perl の文字列を評価し、強制的に C の型へと 変数を展開します。 以下に示すプログラムは string.c は、三つの Perl 文字列を実行し、最初のものを int
に、二番目のものを float
に、 三番目のものを char *
へ 展開します。
#include <EXTERN.h>
#include <perl.h>
static PerlInterpreter *my_perl;
main (int argc, char **argv, char **env)
{
char *embedding[] = { "", "-e", "0" };
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct( my_perl );
perl_parse(my_perl, NULL, 3, embedding, NULL);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
perl_run(my_perl);
/** Treat $a as an integer **/
eval_pv("$a = 3; $a **= 2", TRUE);
printf("a = %d\n", SvIV(get_sv("a", 0)));
/** Treat $a as a float **/
eval_pv("$a = 3.14; $a **= 2", TRUE);
printf("a = %f\n", SvNV(get_sv("a", 0)));
/** Treat $a as a string **/
eval_pv("$a = 'rekcaH lreP rehtonA tsuJ'; $a = reverse($a);", TRUE);
printf("a = %s\n", SvPV_nolen(get_sv("a", 0)));
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
名前に sv の付いた奇妙な関数は Perl のスカラから C の型への変換を 手助けします。 これらは perlguts と perlapi に記述されています。
string.c をコンパイルして実行すれば、SvIV() を使って int
を、 SvNV() を使って float
を、SvPV() を使って文字列を生成した 結果を見ることになるでしょう。
a = 9
a = 9.859600
a = Just Another Perl Hacker
先の例では、私たちが式を評価した値を一時的に格納するためのグローバル 変数を生成しました。 これと同様なことが eval_pv() の戻り値を取ることで可能であり、かつ ほとんどの場合にはこれが良い戦略なのです。 例えば:
...
SV *val = eval_pv("reverse 'rekcaH lreP rehtonA tsuJ'", TRUE);
printf("%s\n", SvPV_nolen(val));
...
このやり方によればグローバル変数を生成せず、またプログラムを単純に したことによって名前空間の汚染を防ぎます。
関数 eval_pv は Perl コードの文字列を評価するものなので、 私たちはマッチや置換に「特化」(specilalize)するために使う match(), substitute(), matches()といった幾つかの関数を 定義できました。
I32 match(SV *string, char *pattern);
文字列と m/clasp/
や /\b\w*\b/
のようなパターン(これは C プログラムでは"/\\b\\w*\\b/" のようになっているでしょう)を与えると、 match() は文字列がパターンにマッチしたときには1を、そうでないときは 0 を返します。
int substitute(SV **string, char *pattern);
SV
へのポインタと =~
操作(s/bob/robert/g
もしくは tr[A-Z][a-z]
)を与えると、substitue() はその操作に従って SV
の中の文字列の書き換えを行って、置換の回数を返します。
int matches(SV *string, char *pattern, AV **matches);
SV
、パターン、空の AV
へのポインタを渡すと、 matchs() はリストコンテキストで $string =~ $pattern
を評価して matches を配列の要素(メモリを割り当てます)で満たし、マッチした 回数を返します。
以下に示すのはサンプルプログラム match.c で、三つの関数すべてを 使います(長い行は整形されてます)。
#include <EXTERN.h>
#include <perl.h>
static PerlInterpreter *my_perl;
/** my_eval_sv(code, error_check)
** kinda like eval_sv(),
** but we pop the return value off the stack
**/
SV* my_eval_sv(SV *sv, I32 croak_on_error)
{
dSP;
SV* retval;
PUSHMARK(SP);
eval_sv(sv, G_SCALAR);
SPAGAIN;
retval = POPs;
PUTBACK;
if (croak_on_error && SvTRUE(ERRSV))
croak(SvPVx_nolen(ERRSV));
return retval;
}
/** match(string, pattern)
**
** Used for matches in a scalar context.
**
** Returns 1 if the match was successful; 0 otherwise.
**/
I32 match(SV *string, char *pattern)
{
SV *command = newSV(0), *retval;
sv_setpvf(command, "my $string = '%s'; $string =~ %s",
SvPV_nolen(string), pattern);
retval = my_eval_sv(command, TRUE);
SvREFCNT_dec(command);
return SvIV(retval);
}
/** substitute(string, pattern)
**
** Used for =~ operations that modify their left-hand side (s/// and tr///)
**
** Returns the number of successful matches, and
** modifies the input string if there were any.
**/
I32 substitute(SV **string, char *pattern)
{
SV *command = newSV(0), *retval;
sv_setpvf(command, "$string = '%s'; ($string =~ %s)",
SvPV_nolen(*string), pattern);
retval = my_eval_sv(command, TRUE);
SvREFCNT_dec(command);
*string = get_sv("string", 0);
return SvIV(retval);
}
/** matches(string, pattern, matches)
**
** Used for matches in a list context.
**
** Returns the number of matches,
** and fills in **matches with the matching substrings
**/
I32 matches(SV *string, char *pattern, AV **match_list)
{
SV *command = newSV(0);
I32 num_matches;
sv_setpvf(command, "my $string = '%s'; @array = ($string =~ %s)",
SvPV_nolen(string), pattern);
my_eval_sv(command, TRUE);
SvREFCNT_dec(command);
*match_list = get_av("array", 0);
num_matches = av_len(*match_list) + 1;
return num_matches;
}
main (int argc, char **argv, char **env)
{
char *embedding[] = { "", "-e", "0" };
AV *match_list;
I32 num_matches, i;
SV *text;
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct(my_perl);
perl_parse(my_perl, NULL, 3, embedding, NULL);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
text = newSV(0);
sv_setpv(text, "When he is at a convenience store and the "
"bill comes to some amount like 76 cents, Maynard is "
"aware that there is something he *should* do, something "
"that will enable him to get back a quarter, but he has "
"no idea *what*. He fumbles through his red squeezey "
"changepurse and gives the boy three extra pennies with "
"his dollar, hoping that he might luck into the correct "
"amount. The boy gives him back two of his own pennies "
"and then the big shiny quarter that is his prize. "
"-RICHH");
if (match(text, "m/quarter/")) /** Does text contain 'quarter'? **/
printf("match: Text contains the word 'quarter'.\n\n");
else
printf("match: Text doesn't contain the word 'quarter'.\n\n");
if (match(text, "m/eighth/")) /** Does text contain 'eighth'? **/
printf("match: Text contains the word 'eighth'.\n\n");
else
printf("match: Text doesn't contain the word 'eighth'.\n\n");
/** Match all occurrences of /wi../ **/
num_matches = matches(text, "m/(wi..)/g", &match_list);
printf("matches: m/(wi..)/g found %d matches...\n", num_matches);
for (i = 0; i < num_matches; i++)
printf("match: %s\n", SvPV_nolen(*av_fetch(match_list, i, FALSE)));
printf("\n");
/** Remove all vowels from text **/
num_matches = substitute(&text, "s/[aeiou]//gi");
if (num_matches) {
printf("substitute: s/[aeiou]//gi...%d substitutions made.\n",
num_matches);
printf("Now text is: %s\n\n", SvPV_nolen(text));
}
/** Attempt a substitution **/
if (!substitute(&text, "s/Perl/C/")) {
printf("substitute: s/Perl/C...No substitution made.\n\n");
}
SvREFCNT_dec(text);
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
この出力は以下のようになります(繰り返しますが、長い行は 整形されています)。
match: Text contains the word 'quarter'.
match: Text doesn't contain the word 'eighth'.
matches: m/(wi..)/g found 2 matches...
match: will
match: with
substitute: s/[aeiou]//gi...139 substitutions made.
Now text is: Whn h s t cnvnnc str nd th bll cms t sm mnt lk 76 cnts,
Mynrd s wr tht thr s smthng h *shld* d, smthng tht wll nbl hm t gt bck
qrtr, bt h hs n d *wht*. H fmbls thrgh hs rd sqzy chngprs nd gvs th by
thr xtr pnns wth hs dllr, hpng tht h mght lck nt th crrct mnt. Th by gvs
hm bck tw f hs wn pnns nd thn th bg shny qrtr tht s hs prz. -RCHH
substitute: s/Perl/C...No substitution made.
スタックを説明しようとするとき、ほとんどのコンピュータ科学の教科書は 食堂のプレート皿のばね仕掛けの仕切りのようなはっきりとしない解説をします: 最後にスタックに押し込んだ(push)ものが最初に取り出す(pop)ものです。 これが、私たちの目的のために行うことです: あなたの C プログラムでは、幾つかの 引数を「Perl スタック」へプッシュして、魔法が掛かる間に目を閉じれば結果、 つまりあなたの使った Perl サブルーチンの戻り値がスタックの一番上に できます -- これをポップします。
まず第一に、あなたは newSViv()、sv_setnv()、newAV() そしてその他の 関数を使った C の型と Perl の型との間の変換の方法を知る必要があります。 これらは perlguts と perlapi に記述されています。
それから Perl スタックの操作方法を知る必要があります。 これは perlcall に説明があります。
これらを理解すれば、C に Perl を組み込むのは簡単です。
C には整数のべき乗を行う組み込み関数がないので、Perl の ** 演算子を 使えるようにしましょう(これは思ったほど便利ではありません; なぜなら Perl は ** を C の pow() 関数を使って実装しているからです)。 最初に power.pl にべき乗関数を作成しましょう。
sub expo {
my ($a, $b) = @_;
return $a ** $b;
}
今度は expo() への二つの引数をプッシュするのとそこからの戻り値 をポップするに必要なすべての perlguts を含んでいる PerlPower() と いう関数を持った C プログラム power.c を作ります。 深く息をすって…
#include <EXTERN.h>
#include <perl.h>
static PerlInterpreter *my_perl;
static void
PerlPower(int a, int b)
{
dSP; /* initialize stack pointer */
ENTER; /* everything created after here */
SAVETMPS; /* ...is a temporary variable. */
PUSHMARK(SP); /* remember the stack pointer */
XPUSHs(sv_2mortal(newSViv(a))); /* push the base onto the stack */
XPUSHs(sv_2mortal(newSViv(b))); /* push the exponent onto stack */
PUTBACK; /* make local stack pointer global */
call_pv("expo", G_SCALAR); /* call the function */
SPAGAIN; /* refresh stack pointer */
/* pop the return value from stack */
printf ("%d to the %dth power is %d.\n", a, b, POPi);
PUTBACK;
FREETMPS; /* free that return value */
LEAVE; /* ...and the XPUSHed "mortal" args.*/
}
int main (int argc, char **argv, char **env)
{
char *my_argv[] = { "", "power.pl" };
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct( my_perl );
perl_parse(my_perl, NULL, 2, my_argv, (char **)NULL);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
perl_run(my_perl);
PerlPower(3, 4); /*** Compute 3 ** 4 ***/
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
コンパイルして実行しましょう:
% cc -o power power.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
% power
3 to the 4th power is 81.
対話的であるとか、長い時間実行される可能性のあるアプリケーションを 開発するとき、複数回新しいインタプリタを割り当てて構築する よりは永続的なインタプリタを保持するのが良い考えです。 主な理由はスピードです: (永続的にすることで)Perl はメモリーに一度しか ロードされません。
しかしながら永続的なインタプリタを使うときには、名前空間や変数の スコープをより注意深く扱わねばならないでしょう。 先の例では、私たちはデフォルトパッケージ main
でグローバル変数を 使いました。 私たちは実際にあのコードが実行できることを知りましたが、変数の衝突や シンボルテーブル拡大の限界に当たらないことを仮定していました。
あなたのアプリケーションが、時折幾つかの任意のファイルから Perl コードが 実行されるサーバーであるとしましょう。 あなたのサーバーは実行させたコードを知る術がありません。 非常に危険です。
ファイルが perl_parse()
を使って引き込まれた場合、新しく構築された インタプリタへコンパイルされ、後の perl_destruct()
で始末されます; これによってほとんどの名前空間にまつわるトラブルから守られます。
このシナリオにおいて名前空間の衝突を防ぐ一つの方法は、ファイル名を ユニークであることが保証されているパッケージ名に変換し、それから "eval" in perlfunc を使ったコードにコンパイルするのです。 先の例では、各ファイルは一回だけコンパイルされました。 あるいは、アプリケーションはファイルに結び付けられたシンボルテーブルを それが必要なくなった時点で掃除してしまうことを選択するかもしれません。 "call_argv" in perlapi を使うことで、私たちは persistent.pl
という ファイルにあるサブルーチン Embed::Persistent::eval_file
を 呼び出し、それにファイル名と cleanup/cache を表わすブール値を 引数として渡します。
プロセスはそれぞれのファイルを使うごとに大きくなるということに 注意してください。 それに加えて、AUTOLOAD
されたサブルーチンなどの条件によっても、 Perl のシンボルテーブルは大きくなります。 あなたはプロセスの大きさを記録しつづける何等かのロジックを 欲するかもしれませんし、あるいはメモリの消費量を最少にするために 特定回数のリクエストの後で自分自身を 再スタートするようにしたいかもしれません。 また、可能であるときにはいつでも "my" in perlfunc を使ってあなたの 使う変数を見通したいでしょう。
package Embed::Persistent;
#persistent.pl
use strict;
our %Cache;
use Symbol qw(delete_package);
sub valid_package_name {
my($string) = @_;
$string =~ s/([^A-Za-z0-9\/])/sprintf("_%2x",unpack("C",$1))/eg;
# second pass only for words starting with a digit
$string =~ s|/(\d)|sprintf("/_%2x",unpack("C",$1))|eg;
# Dress it up as a real package name
$string =~ s|/|::|g;
return "Embed" . $string;
}
sub eval_file {
my($filename, $delete) = @_;
my $package = valid_package_name($filename);
my $mtime = -M $filename;
if(defined $Cache{$package}{mtime}
&&
$Cache{$package}{mtime} <= $mtime)
{
# we have compiled this subroutine already,
# it has not been updated on disk, nothing left to do
print STDERR "already compiled $package->handler\n";
}
else {
local *FH;
open FH, $filename or die "open '$filename' $!";
local($/) = undef;
my $sub = <FH>;
close FH;
#wrap the code into a subroutine inside our unique package
my $eval = qq{package $package; sub handler { $sub; }};
{
# hide our variables within this block
my($filename,$mtime,$package,$sub);
eval $eval;
}
die $@ if $@;
#cache it unless we're cleaning out each time
$Cache{$package}{mtime} = $mtime unless $delete;
}
eval {$package->handler;};
die $@ if $@;
delete_package($package) if $delete;
#take a look if you want
#print Devel::Symdump->rnew($package)->as_string, $/;
}
1;
__END__
/* persistent.c */
#include <EXTERN.h>
#include <perl.h>
/* 1 = clean out filename's symbol table after each request, 0 = don't */
#ifndef DO_CLEAN
#define DO_CLEAN 0
#endif
#define BUFFER_SIZE 1024
static PerlInterpreter *my_perl = NULL;
int
main(int argc, char **argv, char **env)
{
char *embedding[] = { "", "persistent.pl" };
char *args[] = { "", DO_CLEAN, NULL };
char filename[BUFFER_SIZE];
int exitstatus = 0;
PERL_SYS_INIT3(&argc,&argv,&env);
if((my_perl = perl_alloc()) == NULL) {
fprintf(stderr, "no memory!");
exit(1);
}
perl_construct(my_perl);
PL_origalen = 1; /* don't let $0 assignment update the proctitle or embedding[0] */
exitstatus = perl_parse(my_perl, NULL, 2, embedding, NULL);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
if(!exitstatus) {
exitstatus = perl_run(my_perl);
while(printf("Enter file name: ") &&
fgets(filename, BUFFER_SIZE, stdin)) {
filename[strlen(filename)-1] = '\0'; /* strip \n */
/* call the subroutine, passing it the filename as an argument */
args[0] = filename;
call_argv("Embed::Persistent::eval_file",
G_DISCARD | G_EVAL, args);
/* check $@ */
if(SvTRUE(ERRSV))
fprintf(stderr, "eval error: %s\n", SvPV_nolen(ERRSV));
}
}
PL_perl_destruct_level = 0;
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
exit(exitstatus);
}
さあ、コンパイルしましょう:
% cc -o persistent persistent.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
スクリプトファイルの例です。
#test.pl
my $string = "hello";
foo($string);
sub foo {
print "foo says: @_\n";
}
これを実行してみましょう:
% persistent
Enter file name: test.pl
foo says: hello
Enter file name: test.pl
already compiled Embed::test_2epl->handler
foo says: hello
Enter file name: ^C
伝統的に END ブロックは perl_run の最後に実行されてきました。 これは、perl_run を呼び出さないアプリケーションで問題となります。 perl 5.7.2 から、新しい振る舞いを取らせるために PL_exit_flags |= PERL_EXIT_DESTRUCT_END
を指定できます。 これはまた、もし perl_parse が失敗して perl_destruct
が終了値を返した ときにも END ブロックを実行するようにします。
perl スクリプトが $0 に値を代入すると、perl のランタイムは、perl_parse() に 渡された argv が指すメモリを更新して、利用可能なら setproctitle() のような API 関数も呼び出すことによって、その値を "ps" で報告される プログラム名として表示させようとします。 この振る舞いは組み込み perl では不適切かもしれないので、perl_parse() が 呼び出される前に PL_origalen
に 1
を代入することで無効にできます。
例えば、上述の persistent.c の例は、もし PL_origalen = 1;
代入を 取り除くと、$0 が代入されたときにおそらくセグメンテーションフォルトが 起きるでしょう。 これは embedding[]
文字列の読み込み専用メモリに 書き込もうとするからです。
ごく一部のアプリケーションでは、二つ以上のインタプリタを生成する 必要があるかもしれません。 そのようなアプリケーションはインタプリタに結び付けられたリソースを 解放することを決定することがあるかもしれません。
プログラムは次のインタプリタが構築されるより前に、解放が 発生することに注意を払い、それを保証しなければなりません。 デフォルトでは、perl が特殊なオプション付きでビルドされていなければ、 グローバル変数 PL_perl_destruct_level
は 0
に設定されています; これはプログラムが生涯ただ一つのインタプリタを作っただけの ときには、普通は余分な後始末が不要であるからです。
全てを始末させるためには PL_perl_destruct_level
を 1
に設定します。
while(1) {
...
/* reset global variables here with PL_perl_destruct_level = 1 */
PL_perl_destruct_level = 1;
perl_construct(my_perl);
...
/* clean and reset _everything_ during perl_destruct */
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
...
/* let's go do it again! */
}
perl_destruct() が呼ばれたとき、インタプリタの構文解析木と シンボルテーブルは始末され、そしてグローバル変数がリセットされます。 PL_perl_destruct_level
への第 2 引数は、perl_construct を 0
に リセットするために必要です。
現在、二つ以上のインタプリタのインスタンスを同時に実行することが サポートされています。 これは可能なことですが、Perlビルドしたときに Configure オプション -Dusemultiplicity
か -Dusethreads -Duseithreads
を 使ったときのみです。 デフォルトでは、これらの Configure オプションの一つが有効になると インタプリタ単位のグローバル変数 PL_perl_destruct_level
が 1
に 設定され、これによって徹底的なクリーニングは自動的で、インタプリタ変数は 正しく初期化されます。 同時に複数のインタプリタを実行するつもりがない場合でも、上述の例のように 順番に実行させるつもりなら、-Dusemultiplicity
オプションをつけて perl をビルドすることを推奨します; さもなければ連続した実行の間に インタプリタ変数が正しく初期化されずに、アプリケーションが クラッシュするかもしれません。
"Thread-aware system interfaces" in perlxs も参照してください。
もし、異なるスレッドで同時に複数のインタプリタを実行するつもりなら、 -Dusemultiplicity
ではなく -Dusethreads -Duseithreads
がより適切です; なぜならこれはインタプリタにシステムのスレッドライブラリを リンクすることへの対応を有効にするからです。
試してみましょう:
#include <EXTERN.h>
#include <perl.h>
/* we're going to embed two interpreters */
#define SAY_HELLO "-e", "print qq(Hi, I'm $^X\n)"
int main(int argc, char **argv, char **env)
{
PerlInterpreter *one_perl, *two_perl;
char *one_args[] = { "one_perl", SAY_HELLO };
char *two_args[] = { "two_perl", SAY_HELLO };
PERL_SYS_INIT3(&argc,&argv,&env);
one_perl = perl_alloc();
two_perl = perl_alloc();
PERL_SET_CONTEXT(one_perl);
perl_construct(one_perl);
PERL_SET_CONTEXT(two_perl);
perl_construct(two_perl);
PERL_SET_CONTEXT(one_perl);
perl_parse(one_perl, NULL, 3, one_args, (char **)NULL);
PERL_SET_CONTEXT(two_perl);
perl_parse(two_perl, NULL, 3, two_args, (char **)NULL);
PERL_SET_CONTEXT(one_perl);
perl_run(one_perl);
PERL_SET_CONTEXT(two_perl);
perl_run(two_perl);
PERL_SET_CONTEXT(one_perl);
perl_destruct(one_perl);
PERL_SET_CONTEXT(two_perl);
perl_destruct(two_perl);
PERL_SET_CONTEXT(one_perl);
perl_free(one_perl);
PERL_SET_CONTEXT(two_perl);
perl_free(two_perl);
PERL_SYS_TERM();
}
PERL_SET_CONTEXT() の呼び出しに注意してください。 実行されている個々のプロセスやスレッドで、どのインタプリタが 「カレント」なのかを追跡するグローバルな状態を初期化するために必要です。 もし複数のインタプリタがあって、両方のインタプリタに交互に perl API 呼び出しを行うなら、常にこれを行うべきです。
(perl_alloc() か、より難解な perl_clone() を使って、) interp
を 作ったのではないスレッドによって使われるときはいつでも、 PERL_SET_CONTEXT(interp) も呼び出されるべきです。
いつもと同じようにコンパイルします:
% cc -o multiplicity multiplicity.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
実行、実行:
% multiplicity
Hi, I'm one_perl
Hi, I'm two_perl
あなたがこれまでにでてきた例を使っていて、(Socket のような)そ れ自身が C や C++ のライブラリを使っている Perl モジュールを use() しているスクリプトを埋め込もうとしているのであれば、こうなる 可能性があります。
Can't load module Socket, dynamic loading not available in this perl.
(You may need to build a new perl executable which either supports
dynamic loading or has the Socket module statically linked into it.)
なにがまずいのでしょう?
あなたの使うインタプリタはこういったエクステンションと どのように交信するのかを知りません。 ちょっとした糊(glue)が助けになります。 これまでは、perl_parse() を呼び出すときにはその第二引数として NULL を使っていました:
perl_parse(my_perl, NULL, argc, my_argv, NULL);
ここは Perl と、リンクされた C/C++ ルーチンとの間の初期化時の交信を 生成するために挿入することのできる糊コード(glue code)の場所です。 Perl がどのようにこれを行っているのかを知るために、perlmain.c の 一部を見てみましょう。
static void xs_init (pTHX);
EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
EXTERN_C void boot_Socket (pTHX_ CV* cv);
EXTERN_C void
xs_init(pTHX)
{
char *file = __FILE__;
/* DynaLoader is a special case */
newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
newXS("Socket::bootstrap", boot_Socket, file);
}
あなたの使う Perl の実行ファイルにリンクされているエクステンション毎に、 そのエクステンションのルーチンを組み込むための Perl サブルーチンが 生成されます。 通常はこういったサブルーチンは Module::bootstrap() という名称で、 あなたが use Module としたときに起動されます。 次にこれは、各エクステンションの XSUB に対する Perl の counterpart として 生成されるXSUB boot_Module にフックされます。 この部分に関して心配することはありません; xsubpp と、エクステンションの 作者に任せましょう。 あなたの使うエクステンションが動的ロードを必要とするならば、 DynaLoader はあなたのためにその場で Module::bootstrap() を生成します。 実際のところ、あなたが DynaLoader を使っているのであれば、他の エクステンションを静的にリンクする必要性はほとんどないでしょう。
このコードを一度書いてしまえば、それを perl_parse() の第二引数に 置けます:
perl_parse(my_perl, xs_init, argc, my_argv, NULL);
そしてコンパイルします:
% cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
% interp
use Socket;
use SomeDynamicallyLoadedModule;
print "Now I can use extensions!\n"'
ExtUtils::Embed も xs_init 糊コードを書くことを 自動化できます。
% perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c
% cc -c perlxsi.c `perl -MExtUtils::Embed -e ccopts`
% cc -c interp.c `perl -MExtUtils::Embed -e ccopts`
% cc -o interp perlxsi.o interp.o `perl -MExtUtils::Embed -e ldopts`
詳しくは perlxs, perlguts, perlapi を参照してください。
もし Perl public API の短い形式を完全に隠したいなら、 コンパイルオプションに -DPERL_NO_SHORT_NAMES を追加してください。 これは、例えば以下のように書く代わりに:
warn("%d bottles of beer on the wall", bottlecount);
以下のように明示的に完全な形式で書く必要があります:
Perl_warn(aTHX_ "%d bottles of beer on the wall", bottlecount);
(aTHX_
の説明については "Background and PERL_IMPLICIT_CONTEXT" in perlguts を参照してください。) 短い形式を隠すことは、その他のソフトウェアパッケージとのあらゆる種類の 不快なもの(C プリプロセッサやその他のもの)との衝突を避けるためにとても 便利です。
あなたは時々 C で より速いコードを 書くことができるかもしれませんが、 あなた常に Perl で コードをより早く 書くことができるのです。 これは片方でもう片方のものを作ることができ、望むままに 組み合わせることができるからです。
Jon Orwant <orwant@media.mit.edu> と Doug MacEachern <dougm@covalent.net>; さらに Tim Bunce, Tom Christiansen, Guy Decoux, Hallvard Furuseth, Dov Grobgeld, Ilya Zakharevich からの 小さな貢献によります。
Doug MacEachern は The Perl Journal ( http://www.tpj.com/ ) の Volume 1, Issue 4 に組み込みの記事を書いています。 Doug はまた、もっとも広く使われている Perl 組み込みの作者です: mod_perl (perl.apache.org) は Apache ウェブサーバに Perl を組み込みます。 Oracle, Binary Evolution, ActiveState, Ben Sugars の nsapi_perl は このモデルを Oracle, Netscape, Internet Information Server の Perl プラグインに使っています。
Copyright (C) 1995, 1996, 1997, 1998 Doug MacEachern and Jon Orwant. All Rights Reserved.
This document may be distributed under the same terms as Perl itself.