📄 perlembed.pod
字号:
Given an C<SV>, a pattern, and a pointer to an empty C<AV>,matches() evaluates C<$string =~ $pattern> in a list context, andfills in I<matches> with the array elements, returning the number of matches found.Here's a sample program, I<match.c>, that uses all three (long lines havebeen wrapped here): #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; STRLEN n_a; PUSHMARK(SP); 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(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 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; STRLEN n_a; sv_setpvf(command, "$string = '%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 **matches with the matching substrings **/ I32 matches(SV *string, char *pattern, AV **match_list) { SV *command = newSV(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); *match_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; SV *text; STRLEN n_a; 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(*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); PERL_SYS_TERM(); }which produces the output (again, long lines have been wrapped here) 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.=head2 Fiddling with the Perl stack from your C programWhen trying to explain stacks, most computer science textbooks mumblesomething about spring-loaded columns of cafeteria plates: the lastthing you pushed on the stack is the first thing you pop off. That'lldo for our purposes: your C program will push some arguments onto "the Perlstack", shut its eyes while some magic happens, and then pop theresults--the return value of your Perl subroutine--off the stack.First you'll need to know how to convert between C types and Perltypes, with newSViv() and sv_setnv() and newAV() and all theirfriends. They're described in L<perlguts> and L<perlapi>.Then you'll need to know how to manipulate the Perl stack. That'sdescribed in L<perlcall>.Once you've understood those, embedding Perl in C is easy.Because C has no builtin function for integer exponentiation, let'smake Perl's ** operator available to it (this is less useful than itsounds, because Perl implements ** with C's I<pow()> function). FirstI'll create a stub exponentiation function in I<power.pl>: sub expo { my ($a, $b) = @_; return $a ** $b; }Now I'll create a C program, I<power.c>, with a functionI<PerlPower()> that contains all the perlguts necessary to push thetwo arguments into I<expo()> and to pop the return value out. Take adeep breath... #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(); }Compile and run: % cc -o power power.c `perl -MExtUtils::Embed -e ccopts -e ldopts` % power 3 to the 4th power is 81.=head2 Maintaining a persistent interpreterWhen developing interactive and/or potentially long-runningapplications, it's a good idea to maintain a persistent interpreterrather than allocating and constructing a new interpreter multipletimes. The major reason is speed: since Perl will only be loaded intomemory once.However, you have to be more cautious with namespace and variablescoping when using a persistent interpreter. In previous exampleswe've been using global variables in the default package C<main>. Weknew exactly what code would be run, and assumed we could avoidvariable collisions and outrageous symbol table growth.Let's say your application is a server that will occasionally run Perlcode from some arbitrary file. Your server has no way of knowing whatcode it's going to run. Very dangerous.If the file is pulled in by C<perl_parse()>, compiled into a newlyconstructed interpreter, and subsequently cleaned out withC<perl_destruct()> afterwards, you're shielded from most namespacetroubles.One way to avoid namespace collisions in this scenario is to translatethe filename into a guaranteed-unique package name, and then compilethe code into that package using L<perlfunc/eval>. In the examplebelow, each file will only be compiled once. Or, the applicationmight choose to clean out the symbol table associated with the fileafter it's no longer needed. Using L<perlapi/call_argv>, We'llcall the subroutine C<Embed::Persistent::eval_file> which lives in thefile C<persistent.pl> and pass the filename and boolean cleanup/cacheflag as arguments.Note that the process will continue to grow for each file that ituses. In addition, there might be C<AUTOLOAD>ed subroutines and otherconditions that cause Perl's symbol table to grow. You might want toadd some logic that keeps track of the process size, or restartsitself after a certain number of requests, to ensure that memoryconsumption is minimized. You'll also want to scope your variableswith L<perlfunc/my> whenever possible. 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -