免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 12644 | 回复: 14

我翻译的Perl文档-如何在C程序中嵌入Perl,请大家指正 [复制链接]

论坛徽章:
0
发表于 2009-12-15 09:00 |显示全部楼层
名称

如何在C程序中嵌入Perl

描述

前言

你想不想

在Perl中使用C?

    请阅读 perlxstut手册、perlxs手册、h2xs和 perlguts手册。
在Perl中使用一个Unix程序?

    阅读有关反引号` 、system 和exec的部分,它们在 perlfunc手册中。
在Perl中使用Perl?

    阅读有关do、eval、require 和 use的手册.
在C中使用C?

    重新考虑你的设计。
在C中使用Perl?

    继续……

编译你的C程序

如果你编译本文档中的脚本有困难。那么你不是一个人在战斗。记住重要原则: 以编译你的perl的方式,编译你的程序(不好意思)

并且,每一个使用了Perl的C程序都必须连接 perl 库. 你问,那是什么?Perl本身就是用C写的。Perl库是编译过的用来创建Perl可执行程序的C程序集合 (/usr/bin/perl或者类似的)。(结论:你不能在没有编译过或者正确安装过Perl的系统中编译嵌入了Perl的C程序。不正确安装指,只是复制了Perl的二进制可执行文件而没有复制perl的库目录。)

当你在C中使用Perl时, 你的C程序将要--通常--需要创建、执行,然后销毁一个 Perl解释器

如果你的Perl烤备足够新到包含本文档 (版本5.002或者更新), 则Perl库 (以及你将需要的EXTERN.h 和 perl.h) 将会在类似这样的目录中:

    /usr/local/lib/perl5/your_architecture_here/CORE

或者可能就直接是

    /usr/local/lib/perl5/your_architecture_here/CORE

或者可能类似

    /usr/opt/perl5/CORE

执行这个表达式可以定位这个目录:

    perl -MConfig -e 'print $Config{archlib}'

这是如何编译下节的示例代码:往你的C程序中墙加一个Perl解释器的语句,在我的linux机器上是这样:

    % gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include
    -I/usr/local/lib/perl5/i586-linux/5.003/CORE
    -I/usr/local/lib/perl5/i586-linux/5.003/CORE
    -o interp interp.c -lperl -lm

(那些是一行。) 在我跑着老版本perl 5.003_05的DEC Alpha机器上, 有一点点儿不一样:

    % 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}' 将告诉你用什么。

你还得根据你的系统选择正确的Perl库目录 (/usr/local/lib/...) 如果你的编译器抱怨有函数未定义,或者它不能定位 -lperl,则你需要用-L手动指定库文件路径。如果它抱怨找不到EXTERN.h 和 perl.h,你需要用-I手动指定头文件路径。

你可能还需要如法增加其它库哪些?可能包括这行指令打印出的这些:

    perl -MConfig -e 'print $Config{archlib}'

如果你的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发行版,则你已经安装了perl 5.004或者更高的版本,并且你肯定有这个模块)

CPAN上的ExtUtils::Embed,包括了有关本文档的所有的源码、测试以及其它对你有用的示例程序。

往你的C程序中增加一个Perl解释器

通常, perl (C源程序) 是一个嵌入Perl(语言)的好例子,所以我将示范一个miniperlmain.c。当然,它不太标准,也不太精巧。

    #include <EXTERN.h>               /* from the Perl distribution     */
    #include <EXTERN.h>               /* from the Perl distribution     */

    static PerlInterpreter *my_perl;  /***    The Perl interpreter    ***/

    int main(int argc, char **argv, char **env)
    {
        my_perl = perl_alloc();
        perl_construct(my_perl);
        perl_parse(my_perl, NULL, argc, argv, (char **)NULL);
        perl_run(my_perl);
        perl_destruct(my_perl);
        perl_free(my_perl);
    }

注意我们没有使用env指针。通常做为perl_parse的最后一个选项, env参数为NULL,代表使用当前的环境变量。

现在编译它 (我将叫它 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_fun以前替换argv[1],在C程序里从一个文件中读取然后执行Perl代码。

在你的C程序中调用一个Perl子程序

为了调用独立的Perl子程序,你可以使用任何<al>perlcall手册</al>中的perl_call_*类型的函数。在此例中,我们将使用perl_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 };
        my_perl = perl_alloc();
        perl_construct(my_perl);

        perl_parse(my_perl, NULL, argc, argv, (char **)NULL);

        /*** skipping perl_run() ***/

        perl_call_argv("showtime", G_DISCARD | G_NOARGS, args);

        perl_destruct(my_perl);
        perl_free(my_perl);
    }

showtime是一个Perl子程序,它没有参数。 (它是 G_NOARGS) 并且我将忽略它的返回值 (它是G_DISCARD). 那些标记,以及其它一些标记,在perlcall手册中。

我将在文件<el>showtime.pl</el>中定义showtime函数:

    print "I shan't be printed.";

    sub showtime {
        print time;
    }

足够简单。现在编译并运行:

     % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

    % showtime showtime.pl
    818284590

返回从1970年1月1日 (Unix epoch开始时间)到现在我正在执行程序语句的时间。

在这一部分,我们不需要调用perl_run,但是通常好的编程习惯来保证正确的使用库是好的,包括对所有的对象调用 DESTROY 方法以及块的END {}。

如果你想传递参数给Perl函数,你可以填加以NULL结尾的args表给perl_call_argv。为了其它数据类型或者测试返回值,你将需要操作Perl的栈。那将在本文档最后的章节进行讨论: 在你的C程序中操作Perl的栈。

在你的C程序中执行一个Perl表达式

Perl提供了两个API函数来执行Perl代码片断。它们是perl_eval_sv 和 perl_eval_pv。

经常的情况是,你只需要在程序中执行一块的Perl代码。它可以如你所愿的长:它可以包括 use、require和 do 用来包含其它的外部Perl文件。

perl_eval_pv 可以让我执行单独的Perl语句,然后把执行结果变量返回成C的形式。下面的程序 string.c,执行三个Perl语句, 从第一个取得int,从第二个取回float, 从第三个取回char *。

   #include <EXTERN.h>
   #include <perl.h>
   
   static PerlInterpreter *my_perl;
   
    int main(int argc, char **argv, char **env)
   {
       STRLEN n_a;
       char *embedding[] = { "", "-e", "0" };
   
       my_perl = perl_alloc();
       perl_construct(my_perl);
   
       perl_parse(my_perl, NULL, 3, embedding, NULL);
       perl_run(my_perl);
   
       /** Treat $a as an integer **/
       perl_eval_pv("$a = 3; $a **= 2", TRUE);
       printf("a = %d\n", SvIV(perl_get_sv("a", FALSE)));
   
       /** Treat $a as a float **/
        perl_eval_pv("$a = 3; $a **= 2", TRUE);
       printf("a = %f\n", SvNV(perl_get_sv("a", FALSE)));
   
        /** Treat $a as a float **/
       perl_eval_pv("$a = 'rekcaH lreP rehtonA tsuJ'; $a = reverse($a);", TRUE);
       printf("a = %s\n", SvPV(perl_get_sv("a", FALSE), n_a));
   
       perl_destruct(my_perl);
       perl_free(my_perl);
   }

所有这些名字中含有 sv 的这些怪函数都帮助把Perl的标量返回成C的类型。它们在 perlguts手册里有描述。

如果你编译并运行string.c,你将看到 SvIV()创建整数,SvNV()创建浮点数,以及SvPV()创建一个字符串:

   a = 9
   a = 9
   a = Just Another Perl Hacker

在上例中,我们创建了一个全局的变量来保存我们表达式执行的结果。在很多情形下,通过perl_eval_pv()的执行结果来取回返回值,也不错。如:

   ...
   STRLEN n_a;
   SV *val = perl_eval_pv("reverse 'rekcaH lreP rehtonA tsuJ'", TRUE);
   printf("%s\n", SvPV(val,n_a));
   ...

这种方法,我们通过不创建全局变量以及简化我们的化码,避免了名字空间污染。

在你的C程序中,提升正则匹配和替换的效率

函数perl_eval_sv() 让我们可以从Perl代码取得字符串,所以我们可以定义一些函数用来指定匹配或者替换: 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]), substitute()根据表达式,改变AV中的值,返回改变了的子串个数。

   int matches(SV *string, char *pattern, AV **matches);

给定一个字符串SV,一个正则表达式和一个指向为空的AV的指针, matches()在一个列表上下文中执行$string =~ $pattern,用列表元素填充matches,返回匹配的元素个数。

这有一个程序示例,match.c,使用了这三种操作 (过长的行被换行了):

#include <EXTERN.h>
#include <perl.h>

/** my_perl_eval_sv(code, error_check)
** kinda like perl_eval_sv(),
** but we pop the return value off the stack
**/
SV* my_perl_eval_sv(SV *sv, I32 croak_on_error)
{
     dSP;
     SV* retval;
     STRLEN n_a;

     PUSHMARK(SP);
     perl_eval_sv(sv, G_SCALAR);

     SPAGAIN;
     retval = POPs;
     PUTBACK;

     if (croak_on_error && SvTRUE(ERRSV))
        croak(SvPVx(ERRSV, n_a));

     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(1099, 0), *retval;
     STRLEN n_a;

     sv_setpvf(command, "my $string = '%s'; $string =~ %s",
              SvPV(string,n_a), pattern);

     retval = my_perl_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.
**/

  int substitute(SV **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_perl_eval_sv(command, TRUE);
     SvREFCNT_dec(command);

     *string = perl_get_sv("string", FALSE);
     return SvIV(retval);
}

/** matches(string, pattern, matches)
**
** Used for matches in an array 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(1099, 0);
     I32 num_matches;
     STRLEN n_a;

      sv_setpvf(command, "my $string = '%s'; $string =~ %s",
              SvPV(string,n_a), pattern);

     my_perl_eval_sv(command, TRUE);
     SvREFCNT_dec(command);

     *match_list = perl_get_av("array", FALSE);
     num_matches = av_len(*match_list) + 1; /** assume $[ is 0 **/

     return num_matches;
}

  int main(int argc, char **argv, char **env)
{
     PerlInterpreter *my_perl = perl_alloc();
     char *embedding[] = { "", "-e", "0" };
     AV *match_list;
     I32 num_matches, i;
     SV *text = NEWSV(1099,0);
     STRLEN n_a;

     perl_construct(my_perl);
     perl_parse(my_perl, NULL, 3, embedding, NULL);

     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 contains the word 'quarter'.\n\n");

     if (match(text, "m/eighth/")) /** Does text contain 'eighth'?**/
         printf("match: Text contains the word 'quarter'.\n\n");
     else
          printf("match: Text contains the word 'quarter'.\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(*av_fetch(match_list, i, FALSE),n_a));
     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(text,n_a));
     }

     /** 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);
}

程序输出

   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.

[ 本帖最后由 naihe2010 于 2009-12-15 09:01 编辑 ]

论坛徽章:
0
发表于 2009-12-15 09:01 |显示全部楼层
在C程序中处理Perl的栈

当解释栈的时候,大部分计算机科学著作都会举叠放着的咖啡盘子的例子:最后放进栈的最先被取出来。这符合我们的目的:你的C程序将会放一些参数到Perl的栈里去,闭上眼睛,发生了一些神奇的事情,然后取回结果-你的Perl函数的执行结果。当然,它们从栈上取回来。

首先,你得知道如何用newSViv()、sv_setnv()和newAV()以及它们一族函数来在C类型与Perl类型之间转化。它们在perlguts手册里有描述。

其次,你得知道如何操作Perl的栈。它们在perlcall手册中有描述。

一旦你理解了这些,C中嵌入Perl就很容易了。

因为C没有内置函数进行整数的指数运算,让我们用Perl的**操作来实现它(这听起来不太实用,因为Perl的**操作就是用C的pow()函数实现的)。首先我将在power.pl中创建一个指数运算函数:

    sub expo {
        my ($a, $b) = @_;
        return $a ** $b;
    }

现在我创建一个C程序power.c,其中<el>perlPower函数包括必要的把两个参数压栈传递给expo()以及退栈返回结果的操作。深吸一口气……

    #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 */
      perl_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" };

      my_perl = perl_alloc();
      perl_construct(my_perl);

      perl_parse(my_perl, NULL, 2, my_argv, (char **)NULL);
      perl_run(my_perl);

      PerlPower(3, 4);                      /*** Compute 3 ** 4 ***/

      perl_destruct(my_perl);
      perl_free(my_perl);
    }

编译运行:

     % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

    % power
    3 to the 4th power is 81.

维护一个永久的Perl解释器

当开发长时间运行或者交互式的应用程序时,维护一个永久的解释器而不是多次创建新解释器是一个好主意。主要原因是速度:因为Perl只被往内存中装载一次。

但是,当你使用永久的解释器时,你不得不对于名字空间和变量作用域更加小心一些才行。在前一例子中,我们在默认的包main中使用了全局变量。我们确切地知道什么代码将运行,以及我们可以避免变量和符号表越界。

比如说,你的应用程序是一个执行指定的文件中的Perl代码的服务器。你的服务器没有办法知道什么代码将会执行。相当危险。

如果文件被放入perl_parse(),编译进一个新创建的解释器然后用perl_destruct()释放,你避免了遇到许多名字空间的麻烦。

一个避免名字空间污染的方法是把文件名翻译成一个单一的包,然后把代码用eval编译。在下例中,每一个文件将只执行编译一次。或者,应用程序可以选择在确定文件不再被使用后,清空对应文件的符号表。使用perl_call_argv,我们可以在persistent.pl中调用内置函数Embed:ersistent::eval_file,传递文件名和是否清空符号表的布尔值做为参数。

注意这个进程会随着被使用的文件,持续增长。并且, 自动加载的函数和其它处理也会导致Perl的符号表增长。你也许想加入一些逻辑来监视进程的大小,或者当处理一些次数以后重启它,来保证程序内存使用最小。你也将希望尽可能地用my限制变量。

package Embed:ersistent;
#persistent.pl

use strict;
use vars '%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/([^A-Za-z0-9\/])/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

static PerlInterpreter *perl = NULL;

int
   int main(int argc, char **argv, char **env)
{
     char *embedding[] = { "", "persistent.pl" };
     char *args[] = { "", DO_CLEAN, NULL };
     char filename [1024];
     int exitstatus = 0;
     STRLEN n_a;

     if((perl = perl_alloc()) == NULL) {
        fprintf(stderr, "no memory!";
        exit(1);
     }
     perl_construct(perl);

     exitstatus = perl_parse(perl, NULL, 2, embedding, NULL);

     if(!exitstatus) {
        exitstatus = perl_run(perl);

        while(printf("Enter file name: " && gets(filename)) {

            /* call the subroutine, passing it the filename as an argument */
            args[0] = filename;
            perl_call_argv("Embed:ersistent::eval_file",
                           G_DISCARD | G_EVAL, args);

            /* check $@ */
            if(SvTRUE(ERRSV))
                fprintf(stderr, "eval error: %s\n", SvPV(ERRSV,n_a));
        }
     }

      PL_perl_destruct_level = 1;
     perl_destruct(perl);
     perl_free(perl);
     exit(exitstatus);
}

现在编译:

  % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

这是一个示例脚本文件:

#test.pl
my $string = "hello";
foo($string);

sub foo {
     print "foo says: @_\n";
}

Now run:

% 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

维护多个解释器例程

一些奇怪的应用程序可能需要在一个执行期间创建多于一个的Perl解释器。这样的应用程序可能希望释放掉所有的解释器使用过的资源。

程序必须在创建下一个新的解释器之前小心地确保这一点。默认地,全局变量PL_perl_destruct_level设置为0,因为在只有一个解释器的情况下,没有必要进行其它的清理工作。

设置PL_perl_destruct_level为1使所有的被清理:

PL_perl_destruct_level = 1;

while(1) {
     ...
     /* reset global variables here with PL_perl_destruct_level = 1 */
     perl_construct(my_perl);
     ...
     /* clean and reset _everything_ during perl_destruct */
     perl_destruct(my_perl);
     perl_free(my_perl);
     ...
     /* let's go do it again!*/
}

当perl_destruct()被调用时,这个解释器的语法分析树和符号表被全部释放,并且全局变量被重置。

现在设想我们在同一时刻有多个解释器实例在运行。这是不可能的,除非你编译Perl的时候定义了-DMULTIPLICITY宏。默认地,它设置PL_perl_destruct_level为1。

让我们试试:

#include <EXTERN.h>
#include <perl.h>

/* we're going to embed two interpreters */
/* 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 = perl_alloc(),
         *two_perl = perl_alloc();
     char *one_args[] = { "one_perl", SAY_HELLO };
     char *two_args[] = { "two_perl", SAY_HELLO };

     perl_construct(one_perl);
     perl_construct(two_perl);

     perl_parse(one_perl, NULL, 3, one_args, (char **)NULL);
     perl_parse(two_perl, NULL, 3, two_args, (char **)NULL);

     perl_run(one_perl);
     perl_run(two_perl);

     perl_destruct(one_perl);
     perl_destruct(two_perl);

     perl_free(one_perl);
     perl_free(two_perl);
}

编译如前:

  % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`

执行它,执行它:

% multiplicity
Hi, I'm one_perl
Hi, I'm two_perl

在你的C程序中,使用调用了C库的Perl模块

如果你想测试上面的例子并且试一试嵌入一个使用了C或者C++的库的模块的Perl脚本:

不能加载Socket模块,本版本的perl没有提供实时加载。  你也许可需要编译一个新的perl执行文件来支持这个。
  支持动态加载或者在它里面静态编译进Socket模块。

怎么了?

你的解释器不知道如何与这些扩展进行通讯。一些窍门可能有用。直到现在你一直在调用perl_parse()时,第二个参数传递NULL值。

perl_parse(my_perl, NULL, argc, argv, (char **)NULL);

那正是有关创建Perl加载C/C++库的关键的代码。让我们看一看perlmain.c中的一块代码是如何实现这个的:

#ifdef __cplusplus
#  define EXTERN_C extern "C"
  else
#  define EXTERN_C extern
#endif

static void xs_init _((void));

EXTERN_C void boot_DynaLoader _((CV* cv));
EXTERN_C void boot_Socket _((CV* cv));

EXTERN_C void
xs_init()
{
        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()并且它在你说<el>use Module</el>时被调用。轮过来,这给XSUB实现了一个回调,boot_Module,它为每一个扩展地XSUB创建了一个Perl计数器。不要担心这一块:留给xsubpp和扩展的作者吧。如果你的扩展是动态加载的,想着动态创建Module::bootstrap()。事实上,如果你有已有一个动态加载器就完全没有必要静态加载其它任何扩展了。

一旦你有了这块代码,把它传递给perl_parse()的第二个参数:

   perl_parse(my_perl, NULL, argc, argv, (char **)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 can also automate writing the xs_init glue code.

% 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`

Consult the perlxs manpage and the perlguts manpage for more details.

在Win32环境下嵌入Perl

在写本文的时候,有两个版本的Perl运行在Win32环境下。(这两个版本号都是5.005。)ActiveState's的Perl库接口与示例中的代码非常不同,标志在Perl的内部API不同。尽管如此,也能实时地嵌入ActiveState的Perl。具体地,请看Win32下的Perl 的FAT在http://www.perl.com/CPAN/doc/FAQs/win32/perlwin32faq.html

而官方的Perl,版本5.004或者更高,本文档中的所有的示例代码都可以不加修改地编译、运行。尽管在Unix与Win32上的编译处理有很大的不同。

对初学者来说,backticks不能在Win32的原生命令行下工作。CPAN上的ExtUtils::Embed包,带有一个叫做genmake的脚本,可以创建一个简单的makefile文件来从一个单独的C源文件进行编译。它可以这样用:

C:\ExtUtils-Embed\eg> perl genmake interp.c
C:\ExtUtils-Embed\eg> nmake
C:\ExtUtils-Embed\eg> interp -e "print qq{I'm embedded in Win32!\n}"

你可能希望使用一个开发环境,比如Microsoft Developper Studio。如果这样,这样运行来创建perlxsi.c:

perl -MExtUtils::Embed -e xsinit

创建一个新的工程,通过Insert -> Files填加perlxsi.c、perl.lib以及你的源文件,比如interp.c。你可以从C:\perl\lib\CORE找到perl.lib。如果没有,你应该通过perl -V:archlib来查找CORE目录。studio也需要这个路径好让它知道从哪找到perl的头文件。这个目录可以通过Tools -> Options -> Directories菜单填加。最后,选择Build -> Build interp.exe就可以了。

约束

你有时可以用C写代码更快,但是你可以一直用Perl写得更快。因为你你可以互相调用,如你所愿地混合使用。

作者

Jon Orwant <orwant@tpj.com> and Doug MacEachern <dougm@osf.org>, with small contributions from Tim Bunce, Tom Christiansen, Guy Decoux, Hallvard Furuseth, Dov Grobgeld, and Ilya Zakharevich.

Doug MacEachern has an article on embedding in Volume 1, Issue 4 of The Perl Journal (http://tpj.com). Doug is also the developer of the most widely-used Perl embedding: the mod_perl system (perl.apache.org), which embeds Perl in the Apache web server. Oracle, Binary Evolution, ActiveState, and Ben Sugars's nsapi_perl have used this model for Oracle, Netscape and Internet Information Server Perl plugins.

July 22, 1998

译者

Alf naihe2010@gmail.com

版权

Copyright (C) 1995, 1996, 1997, 1998 Doug MacEachern and Jon Orwant. All Rights Reserved.

Permission is granted to make and distribute verbatim copies of this documentation provided the copyright notice and this permission notice are preserved on all copies.

Permission is granted to copy and distribute modified versions of this documentation under the conditions for verbatim copying, provided also that they are marked clearly as modified versions, that the authors' names and title are unchanged (though subtitles and additional authors' names may be added), and that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this documentation into another language, under the above conditions for modified versions.

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2009-12-15 10:05 |显示全部楼层

论坛徽章:
0
发表于 2009-12-15 10:41 |显示全部楼层

回复 #3 flw 的帖子

thanks flw提醒~
我已联系了原署名翻译者YeWenbin,希望跟lz是同一人~

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2009-12-15 10:46 |显示全部楼层
原帖由 兰花仙子 于 2009-12-15 10:41 发表
thanks flw提醒~
我已联系了原署名翻译者YeWenbin,希望跟lz是同一人~

我对比了部分文字,应该不是同一人。应该是分别独立翻译的。
翻译质量也要较之前版本稍微好一些。

论坛徽章:
0
发表于 2009-12-15 10:53 |显示全部楼层
我没有见过原来的翻译版本,如果见过,也就不会费劲地翻译这个了。
我是工作中要用到这块,网上找了找资料,没什么权威的,就自己翻译了perlembed手册页。

论坛徽章:
0
发表于 2009-12-15 10:54 |显示全部楼层

回复 #6 naihe2010 的帖子

噢~~那还是赞一个

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2009-12-15 10:57 |显示全部楼层
原帖由 naihe2010 于 2009-12-15 10:53 发表
我没有见过原来的翻译版本,如果见过,也就不会费劲地翻译这个了。
我是工作中要用到这块,网上找了找资料,没什么权威的,就自己翻译了perlembed手册页。

能分享一下需求吗?

把 Perl 用到这种程度的单位似乎不是很多啊。
说出来交流交流,让更多的人也感受一下 -lperl

论坛徽章:
0
发表于 2009-12-15 11:08 |显示全部楼层
其实需求很简单,也有点儿无奈。

我给公司写了个WEB控制服务系统的中间程序,就是说WEB只要掉我这一个脚本就可以对服务程序实现管理、配置、排错、调试等操作。开始时我觉得用Perl实现挺好,就用了。后来给客户布署的时候,“相关单位”不让透露源代码,所以我就想用perlcc直接编一下。看perlcc相关的手册,读到perlembed,发现很重要。对于我这样的C程序员,可以使内功大进哪!就想可能不少人会有我这样的需求,干脆学雷锋,把手册翻译了发出来得了。

论坛徽章:
0
发表于 2009-12-15 12:11 |显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

【架构革新 高效可控】2020年12月21日-23日第十一届中国数据库技术大会将在北京隆重召开。

大会设置2大主会场,20+技术专场,将邀请超百位行业专家,重点围绕数据架构、AI与大数据、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

http://dtcc.it168.com


大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP