1、# -*- mode: outline; outline-regexp: “=+“ -*- =encoding gb2312 =head1 NAME perlembed - 在 C 程序中嵌入 perl =head1 DESCRIPTION =head2 导言 你是想要: =over 5 =item B 阅读 L、L、L、L 和 L。 =item B 阅读反引用符(back-quote)和 L 中的 C 以及 C。 =item B 阅读 L、L、L 以及 L。 =item B 重新考虑一下你的设计。 =item B 请继续 =back =head2 路标 =over 5 =item * PDF
2、 created with pdfFactory Pro trial version 编译你的 C 程序 =item * 在你的 C 程序中加入一个 Perl 解释器 =item * 在 C 程序中调用一个 Perl 函数 =item * 在 C 程序中对一个 Perl 语句求值 =item * 在 C 程序中进行 Perl 模式匹配和替换 =item * 在 C 程序中修改 Perl 参数栈 =item * 保持一个持久的解释器 =item * 保持多个解释器实例 =item * 在 C 程序中使用 Perl 模块,模块本身使用 C 库 =item * 在 Win32 下内嵌 Perl =b
3、ack =head2 编译你的 C 程序 你不是唯一一个在编译本文档的例子时遇到困难的。一个重要规则是:用编译你的 PDF created with pdfFactory Pro trial version Perl 相同规则来编译程序(对不起,对你大声喊了)。 每个使用 Perl 的 C 程序都必须链接到 I。I 是 什么?Perl 本身是用 C 来写的,perl library 是一系列编译过的 C 程序,这 些将用于创建你的可执行 perl 程序(I 或者等价的东西)。 (推论:除非 Perl 是在你的机器上编译的,或者合适安装的,否则你将不能在 C 程序中使用 Perl这也是为什么你不
4、应该从另一台机器中复制 Perl 的可执 行程序而不复制 I 目录。) 当你在 C 中使用 Perl 时,你的 C 程序将(通常是这样)分配、运行然后释放 一个 I 对象,这个对象是在 perl 库中定义的。 如果你的 Perl 足够新,包含了本文档(版本 5.002 或者更新的),那么 perl 库(还有必须的 I 和 I)将在看上去像这样的目录中: /usr/local/lib/perl5/your_architecture_here/CORE 或者可能就是 /usr/local/lib/perl5/CORE 或者可能像这样 /usr/opt/perl5/CORE 执行这样的语句可以找到
5、CORE: perl -MConfig -e print $Configarchlib 这是在我的 Linux 机器上编译下一节中例子 L 的方法: % 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,这个“咒语”有一点不同: % c
6、c -O2 -Olimit 2900 -DSTANDARD_C -I/usr/local/include -I/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE PDF created with pdfFactory Pro trial version -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.
7、001 之后,执行 C 命令,特别要注意“cc”和“ccflags”信息。 你必须选择合适的编译器(I、I 等等)。在你的机器上:C 将告诉你要使用什么。 你还要为你的机器选择合适的库目录(I)。如果你的编 译器抱怨某个函数没有定义,或者它找不到 I,这时你需要更改在 C 之后的路径。如果它抱怨找不到 I 和 I,你需要更 改在 C 之后的路径。 你可能还要加上一些额外的库。加什么呢?可能是用下面语句输出的那些: perl -MConfig -e print $Configlibs 如果你的 perl 库配置是适当的,已经安装了 B 模块,它会 为你决定所有的这些信息: % cc -o int
8、erp interp.c perl -MExtUtils:Embed -e ccopts -e ldopts 如果 B 模块不是你的 Perl 发行版的一部分,你可以从 http:/ 获得。(如果本文档是来自你的 Perl 发行版,那你用的是 5.004 或者更好, 你就已经有这个模块了。) CPAN 上 B 套装也包含本文档例子的所有源代码,测试,额 外的例子以及其它可能有用的信息。 =head2 在 C 程序中加入 Perl 解释器 在某种意义上说,perl(这里指 C 程序)是一个内嵌 Perl(这里指语言)的一 个很好的例子。所以我将用包含在发行版源文件中的 I 来演 示。这是一个拙劣
9、的、不可移植的 I 版本,但是包含了内嵌 的本质: #include /* from the Perl distribution */ #include /* from the Perl distribution */ PDF created with pdfFactory Pro trial version static PerlInterpreter *my_perl; /* The Perl interpreter */ int main(int argc, char *argv, char *env) PERL_SYS_INIT3( my_perl = perl_alloc(); pe
10、rl_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(); 注意,我们没有用到 C 指针。通常只是作为 C 的最后一个 参数提供给它。这里 C 用 C 代替了,表示使用当前的环境。 PERL_SYS_INIT3() 和 PERL_SYS_TERM() 宏为 Perl
11、 解释器的运行提供了必要 的、系统特定的 C 运行环境。由于 PERL_SYS_INIT3() 可能修改 C,所 有最好提供 perl_parse() 一个 C 参数。 现在编译成可执行程序(我称之为 I): % cc -o interp interp.c perl -MExtUtils:Embed -e ccopts -e ldopts 在成功编译后,你就可以用 I 就像 perl 本身一样: % interp print “Pretty Good Perl n“; print “10890 - 9801 is “, 10890 - 9801; Pretty Good Perl 10890
12、- 9801 is 1089 或者 % interp -e printf(“%x“, 3735928559) deadbeef 可以在你的 C 程序中读入和执行 Perl 语句,只需要在调用 I 前放 置文件名在 I 中。 =head2 在 C 程序中调用 Perl 函数 PDF created with pdfFactory Pro trial version 要调用单个 Perl 函数,你可以使用任何一个在 L 中介绍的 B 函数。 在这个例子中,我们使用 C。 下面显示一个我称为 I 的程序: #include #include static PerlInterpreter *my_pe
13、rl; int main(int argc, char *argv, char *env) char *args = NULL ; PERL_SYS_INIT3( 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_d
14、estruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); 这里 I 是一个没有参数的 Perl 函数(就是 I),而且 忽略一返回值(就是 I)。在 L 中有讨论这些以及其它 标签。 我在一个称为 I 文件中定义这个 I 函数: print “I shant be printed.“; sub showtime print time; 很简单。现在编译并运行: PDF created with pdfFactory Pro trial version % cc -o showtime showtime.c perl -MExtUtils:Em
15、bed -e ccopts -e ldopts % showtime showtime.pl 818284590 产生从 1970 年 1 月 1 日(Unix 纪元的开始)到现在的秒数,这是我写这句 话的时间。 在这个特殊例子中,我们不必调用 I,因为我们设置了 PL_exit_flag PERL_EXIT_DESTRUCT_END,这将在 perl_destruct 中执行 END 块。 如果你想要传递参数给 Perl 函数,你可以在以 C 结尾的 C 列表 中加入字符串传递给 I。对于其它数据类型,或者要检查返回值类 型,你需要操作 Perl 参数栈。在 L 中演示了这个过程。 =hea
16、d2 在 C 程序中对 Perl 语句求值 Perl 提供两个 API 函数来对一小段 Perl 代码进行求值。这就是 L 和 L。 在 C 程序中只有这两个函数,你可以执行一段 Perl 代码。你的代码可以任意 长,可以包含多个语句,你可以用 L、L、 和 L 来引入一个 Perl 文件。 I 可以对单个的 Perl 字符串求值,然后可以提取出变量转换为 C 类 型。下面这个程序 I 执行三个 Perl 字符串,第一个提取出一个 I 变量,第二个提取 C 变量,第三个提取 C 变量。 #include #include static PerlInterpreter *my_perl; mai
17、n (int argc, char *argv, char *env) STRLEN n_a; char *embedding = “, “-e“, “0“ ; PERL_SYS_INIT3( my_perl = perl_alloc(); PDF created with pdfFactory Pro trial version perl_construct( my_perl ); perl_parse(my_perl, NULL, 3, embedding, NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_run(my_perl);
18、 /* Treat $a as an integer */ eval_pv(“$a = 3; $a *= 2“, TRUE); printf(“a = %dn“, SvIV(get_sv(“a“, FALSE); /* Treat $a as a float */ eval_pv(“$a = 3.14; $a *= 2“, TRUE); printf(“a = %fn“, SvNV(get_sv(“a“, FALSE); /* Treat $a as a string */ eval_pv(“$a = rekcaH lreP rehtonA tsuJ; $a = reverse($a);“,
19、TRUE); printf(“a = %sn“, SvPV(get_sv(“a“, FALSE), n_a); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); 所有在名字中含有 I 的奇怪函数都是为了协助将 Perl 标量转换为 C 类型。 这在 L 和 L 中有描述。 如果你编译并运行 I,你可以用 I 创建一个 C,I 创建一个 C,I 创建一个字符串,这样可 以看到结果。 a = 9 a = 9.859600 a = Just Another Perl Hacker 在上面的例子中,我们创建了一个全局变量来临时保存
20、求值后计算的结果。也可 以,并在大多数情况下最好用 I 的返回值。例如: . STRLEN n_a; SV *val = eval_pv(“reverse rekcaH lreP rehtonA tsuJ“, TRUE); printf(“%sn“, SvPV(val,n_a); . PDF created with pdfFactory Pro trial version 这样不用创建一个全局变量,可以避免污染名字空间,也同样使代码简化。 =head2 在 C 程序中进行 Perl 模式匹配和替换 I 函数可以对 Perl 代码字符串求值,所以我们可以定义一些函数 专门进行匹配和替换:I,I
21、 和 I。 I32 match(SV *string, char *pattern); 假定有一个字符串和一个模式(例如 C 或者 C,在你的 C 程序中可能是这样的 “/bw*b/“)。如果字符串匹配一个模式则返回 1,否则返回 0。 int substitute(SV *string, char *pattern); 假定有一个指向 C 的指针和 C 操作符(例如 C 或 者 C),substitute() 根据这个操作符修改 C,返回替换 操作的次数。 int matches(SV *string, char *pattern, AV *matches); 假定有一个 C,一个模式和一个
22、指向一个空 C 的指针,match() 在一 个列表上下文中对 C 求值,在 I 中填充数 组,返回匹配的数目。 这是一个使用了三个函数的样例,I(过长的行折叠了): #include #include 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; S
23、TRLEN n_a; PUSHMARK(SP); PDF created with pdfFactory Pro trial version eval_sv(sv, G_SCALAR); SPAGAIN; retval = POPs; PUTBACK; if (croak_on_error return retval; /* match(string, pattern) * * Used for matches in a scalar context. * * Returns 1 if the match was successful; 0 otherwise. */ I32 match(SV
24、 *string, char *pattern) SV *command = NEWSV(1099, 0), *retval; STRLEN n_a; sv_setpvf(command, “my $string = %s; $string = %s“, SvPV(string,n_a), pattern); retval = my_eval_sv(command, TRUE); SvREFCNT_dec(command); return SvIV(retval); /* substitute(string, pattern) * * Used for = operations that mo
25、dify 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) PDF created with pdfFactory Pro trial version SV *command = NEWSV(1099, 0), *retval; STRLEN n_a; sv_setpvf(command, “$s
26、tring = %s; ($string = %s)“, SvPV(*string,n_a), pattern); retval = my_eval_sv(command, TRUE); SvREFCNT_dec(command); *string = get_sv(“string“, FALSE); return SvIV(retval); /* matches(string, pattern, matches) * * Used for matches in a list context. * * Returns the number of matches, * and fills in
27、*matches with the matching substrings */ I32 matches(SV *string, char *pattern, AV *match_list) SV *command = NEWSV(1099, 0); I32 num_matches; STRLEN n_a; sv_setpvf(command, “my $string = %s; array = ($string = %s)“, SvPV(string,n_a), pattern); my_eval_sv(command, TRUE); SvREFCNT_dec(command); *matc
28、h_list = get_av(“array“, FALSE); num_matches = av_len(*match_list) + 1; /* assume $ is 0 */ return num_matches; main (int argc, char *argv, char *env) char *embedding = “, “-e“, “0“ ; AV *match_list; I32 num_matches, i; PDF created with pdfFactory Pro trial version SV *text; STRLEN n_a; PERL_SYS_INI
29、T3( 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(1099,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 s
30、omething 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 tw
31、o 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.nn“); else printf(“match: Text doesnt contain the word quarter.nn“); if (match(text, “m/eighth/“) /* Doe
32、s text contain eighth? */ printf(“match: Text contains the word eighth.nn“); else printf(“match: Text doesnt contain the word eighth.nn“); /* Match all occurrences of /wi/ */ num_matches = matches(text, “m/(wi)/g“, printf(“matches: m/(wi)/g found %d matches.n“, num_matches); for (i = 0; i 和 L 中有说明。
33、然后你要知道如何操纵 Perl 参数栈。在 L 中有说明。 一旦你明白这些,在 C 中嵌入 Perl 是很简单的。 因为 C 没有内建的函数进行整数的指数运算,让我们用 Perl 的 * 运算符实 现它(这比它听上去没用得多,因为 Perl 用 C I 函数实现 *)。首 先在 I 中创建一个简短的指数函数: sub expo my ($a, $b) = _; return $a * $b; 现在我创建一个 C 程序 I,通过 I (包含所有必须的 perlguts)将两个参数放到I 并取出返回值。深吸一口气: #include #include static PerlInterpreter
34、*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 */ PDF created with pdfF
35、actory Pro trial version 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
36、“, 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( my_perl = perl_alloc(); perl_construct( my_perl ); perl_parse(my_perl, NULL, 2, my_argv, (char *)NULL
37、); 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. PDF created with pdfFactory Pro tr
38、ial version =head2 保持一个持久的解释器 当开发一个交互而且(或者)可能是持久运行的应用程序,不要多次分配构建新 的解释器,保持一个持久的解释器是一个好主意。最主要的原因是速度:因为 Perl 只要导入到内存中一次。 尽管这样,当使用一个持久的解释器时要特别小心名字空间和变量作用域。在前 面的例子中,我们在默认的包 C 中使用全局变量。我们很清楚地知道代 码是怎样运行的,并且假定我们能够避免变量冲突和符号表的增长。 假定你的应用程序是一个服务器,它偶尔运行一些文件中的 Perl 代码。你的服 务器是不知道要运行什么代码的。这很危险。 如果文件用 C 引入的,编译成一个新创建的
39、解释器,然后接着 用 C 作一次清理,这样就可以屏蔽了大多数的名字空间的问 题。 一个避免名字空间冲突的方法是将文件名转换成一个唯一的包名,然后用 L 将这段代码编译到这个包中。在下面的例子中,每个文件只 编译一次。或者这个应用程序在一个文件中的符号表不再需要时可能会清除这个 符号表。使用 L,我们调用在 C 文件中 的 C,传递一个文件名以及一个清除或者缓冲 的标签作为参数。 注意到对于每个使用的文件,这个进程都要不断增长。另外,可能有 C 函数或者其它条件导致 Perl 符号表的增长。你可能想加入一些逻 辑判断来跟踪进程的大小,或者在一定次数的请求之后重新启动一次,这样来保证内 存的消耗是
40、保证最小的。你可能还会在可能的时候用 L 限定变量的范围。 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
41、(“/_%2x“,unpack(“C“,$1)|eg; PDF created with pdfFactory Pro trial version # 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$packagemtime
42、else local *FH; open FH, $filename or die “open $filename $!“; local($/) = undef; my $sub = ; close FH; #wrap the code into a subroutine inside our unique package my $eval = qqpackage $package; sub handler $sub; ; # hide our variables within this block my($filename,$mtime,$package,$sub); eval $eval;
43、 die $ if $; #cache it unless were cleaning out each time $Cache$packagemtime = $mtime unless $delete; eval $package-handler; die $ if $; delete_package($package) if $delete; #take a look if you want PDF created with pdfFactory Pro trial version #print Devel:Symdump-rnew($package)-as_string, $/; 1;
44、_END_ /* persistent.c */ #include #include /* 1 = clean out filenames symbol table after each request, 0 = dont */ #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
45、“ ; char *args = “, DO_CLEAN, NULL ; char filenameBUFFER_SIZE; int exitstatus = 0; STRLEN n_a; PERL_SYS_INIT3( if(my_perl = perl_alloc() = NULL) fprintf(stderr, “no memory!“); exit(1); perl_construct(my_perl); exitstatus = perl_parse(my_perl, NULL, 2, embedding, NULL); PL_exit_flags |= PERL_EXIT_DES
46、TRUCT_END; if(!exitstatus) exitstatus = perl_run(my_perl); while(printf(“Enter file name: “) /* strip n */ /* call the subroutine, passing it the filename as an argument */ args0 = filename; call_argv(“Embed:Persistent:eval_file“, G_DISCARD | G_EVAL, args); /* check $ */ if(SvTRUE(ERRSV) fprintf(std
47、err, “eval error: %sn“, SvPV(ERRSV,n_a); PL_perl_destruct_level = 0; perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); exit(exitstatus); Now compile: % cc -o persistent persistent.c perl -MExtUtils:Embed -e ccopts -e ldopts Heres an example script file: #test.pl my $string = “hello“; foo($string); sub foo print “foo says: _n“; Now run: % persistent Enter file name: test.pl f