You’re here because…
• You know some Perl
• You recognise some C– But fear it a little
• You’re an evil brigand
• You know it’s warmer in here
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack Functions
dSP; Declare stack varsPUSHMARK(SP) start of argumentsEXTEND make spaceXPUSHstuff push stuff, making spacePUSHstuff push stuff, need spacePUTBACK done adding arguments . . . Make a call . . .SPAGAIN refresh our stackret = POPstuff grab stuff from stackPUTBACK we’re done grabbing stuff
Scope•Start and end a scope
•Tell Perl we’re creating temporary stuff
•Tell Perl when we’re done with it
Scope Functions
ENTER Opening brace {SAVETMPS start making temps . . . Call function . . .FREETMPS done with my tempsLEAVE Closing brace }
Scalars
$thing = 12 12.3 “harry” \ @array \ %hash \ $thing bless(\$object) *FILE qr{.*} {code}
Scalar Functions ISV* get_sv(char* name, I32 create)SV* sv = get_sv(“bar”, TRUE)
SV* newSVx sv = newSViv(12) newSVnv(12.3) newSVpv(“honk”, 0) -> “honk” newSVpv(“honk”, 2) -> “ho”
IV intv = SvIV(sv)NV fltv = SvNV(sv)char* foo = SvPV(sv)
sv_setpv(sv, “honk”); sv_setiv(sv, 12)
Scalar Functions ISV* get_sv(char* name, I32 create)SV* sv = get_sv(“bar”, TRUE)
SV* newSVx sv = newSViv(12) newSVnv(12.3) newSVpv(“honk”, 0) -> “honk” newSVpv(“honk”, 2) -> “ho”
IV intv = SvIV(sv)NV fltv = SvNV(sv)char* foo = SvPV(sv)
sv_setpv(sv, “honk”); sv_setiv(sv, 12)
Scalar Functions ISV* get_sv(char* name, I32 create)SV* sv = get_sv(“bar”, TRUE)
SV* newSVx sv = newSViv(12) newSVnv(12.3) newSVpv(“honk”, 0) -> “honk” newSVpv(“honk”, 2) -> “ho”
IV intv = SvIV(sv)NV fltv = SvNV(sv)char* foo = SvPV(sv)
sv_setpv(sv, “honk”); sv_setiv(sv, 12)
Scalar Functions ISV* get_sv(char* name, I32 create)SV* sv = get_sv(“bar”, TRUE)
SV* newSVx sv = newSViv(12) newSVnv(12.3) newSVpv(“honk”, 0) -> “honk” newSVpv(“honk”, 2) -> “ho”
IV intv = SvIV(sv)NV fltv = SvNV(sv)char* foo = SvPV(sv)
sv_setpv(sv, “honk”); sv_setiv(sv, 12)
Scalar Functions IISV* refsv = newRV_inc(sv) “refsv = \ sv”SV* sv = SvRV(refsv) “sv = $ { refsv }”
I32 cnt = REFCNT(sv) REFCNT_inc(sv) REFCNT_dec(sv)
SV *sv = sv_2mortal(newSViv(12))
sv_free(sv)
sv_{catpv catsv cmp dec eq inc isa len . . . }
Scalar Functions IISV* refsv = newRV_inc(sv) “refsv = \ sv”SV* sv = SvRV(refsv) “sv = $ { refsv }”
I32 cnt = REFCNT(sv) REFCNT_inc(sv) REFCNT_dec(sv)
SV *sv = sv_2mortal(newSViv(12))
sv_free(sv)
sv_{catpv catsv cmp dec eq inc isa len . . . }
Scalar Functions IISV* refsv = newRV_inc(sv) “refsv = \ sv”SV* sv = SvRV(refsv) “sv = $ { refsv }”
I32 cnt = REFCNT(sv) REFCNT_inc(sv) REFCNT_dec(sv)
SV *sv = sv_2mortal(newSViv(12))
sv_free(sv)
sv_{catpv catsv cmp dec eq inc isa len . . . }
Scalar Functions IISV* refsv = newRV_inc(sv) “refsv = \ sv”SV* sv = SvRV(refsv) “sv = $ { refsv }”
I32 cnt = REFCNT(sv) REFCNT_inc(sv) REFCNT_dec(sv)
SV *sv = sv_2mortal(newSViv(12))
sv_free(sv)
sv_{catpv catsv cmp dec eq inc isa len . . . }
A Puzzle
• Find the longest set of three words that when listed form three letter words down all columns.
padsareatend
Array FunctionsAV *av = get_av(“ARGV”, TRUE)
SV** ret = av_store(av, 12, sv)
SV** ret = av_fetch(av, 12, sv, lval)
SV* sv = av_pop(av) av_shift(av)
void av_push(av, sv) av_unshift(av, 3) [3 undefs]
I32 len = av_len(av)
Hash FunctionsHV *hv = get_hv(“hash”, TRUE)
BOOL hv_exists(hv, “key”, 3)
SV** sv = hv_fetch(hv, “key”, 3, lval)
SV** sv = hv_store(hv, “key”, 3, sv, 0)
Or you can play with HE (Hash Entries)
Function FunctionsCV* cv = get_cv(“subname”, TRUE)
call_pv(“subname”, context) call_sv(sv, context)
Context: G_VOID } G_SCALAR } context G_ARRAY } G_NOARGS no arguments G_DISCARD “no return values” G_EVAL catch die in eval { } G_KEEPERR .. and be nice to $@
Objects, globs, whatever
• Maybe you’ll need them
• Maybe you won’t
• See perlapi, perlcall, perlguts
Embedding
• You have some C
• You want to add a bit of Perl– Config file dabbling– Scripting engine– Hook into Efficient / Legacy code– Prototype features
Config Parsing
• Flexible configuration framework• C side factored into:
void get_config(char* key, char* dst, int len);
• Let Perl provide the intelligence
• (We provide the glue)
Perl
• A big C program
• A really tiny main()– Constructs an intepreter– Tells it to parse your script– Tells it to run your script
• Jumps between C and Perl a lot
– Cleans up (sort of)– Exits
Config Parsinguse strict;
my %config = ();
sub init {my($file) = @_;… # fill in %config
}
sub get_value {my($key) = @_;return $config{$key} || “”;
}
#include "EXTERN.h"#include "Perl.h"
static PerlInterpreter *g_perl_interp;
int main(int argc, char** argv, char** env) {char *config_pl[] = {“config.pl”, 0};
PERL_SYS_INIT3(&argc,&argv,&env); g_perl_interp = perl_alloc();perl_construct(g_perl_interp);PL_exit_flags |= PERL_EXIT_DESTRUCT_END;perl_parse(g_perl_interp, NULL, 1, config_pl, env);perl_run(g_perl_interp);
do_program_stuff(. . .);
perl_destruct(g_perl_interp);perl_free(g_perl_interp);PERL_SYS_TERM()
}
#include "EXTERN.h"#include "Perl.h"
static PerlInterpreter *g_perl_interp;
int main(int argc, char** argv, char** env) {char *config_pl[] = {“config.pl”, 0};
PERL_SYS_INIT3(&argc,&argv,&env); g_perl_interp = perl_alloc();perl_construct(g_perl_interp);PL_exit_flags |= PERL_EXIT_DESTRUCT_END;perl_parse(g_perl_interp, NULL, 1, config_pl, env);perl_run(g_perl_interp);
do_program_stuff(. . .);
perl_destruct(g_perl_interp);perl_free(g_perl_interp);PERL_SYS_TERM()
}
#include "EXTERN.h"#include "Perl.h"
static PerlInterpreter *g_perl_interp;
int main(int argc, char** argv, char** env) {char *config_pl[] = {“config.pl”, 0};
PERL_SYS_INIT3(&argc,&argv,&env); g_perl_interp = perl_alloc();perl_construct(g_perl_interp);PL_exit_flags |= PERL_EXIT_DESTRUCT_END;perl_parse(g_perl_interp, NULL, 1, config_pl, env);perl_run(g_perl_interp);
do_program_stuff(. . .);
perl_destruct(g_perl_interp);perl_free(g_perl_interp);PERL_SYS_TERM()
}
#include "EXTERN.h"#include "Perl.h"
static PerlInterpreter *g_perl_interp;
int main(int argc, char** argv, char** env) {char *config_pl[] = {“config.pl”, 0};
PERL_SYS_INIT3(&argc,&argv,&env); g_perl_interp = perl_alloc();perl_construct(g_perl_interp);PL_exit_flags |= PERL_EXIT_DESTRUCT_END;perl_parse(g_perl_interp, NULL, 1, config_pl, env);perl_run(g_perl_interp);
do_program_stuff(. . .);
perl_destruct(g_perl_interp);perl_free(g_perl_interp);PERL_SYS_TERM()
}
#include "EXTERN.h"#include "Perl.h"
static PerlInterpreter *g_perl_interp;
int main(int argc, char** argv, char** env) {char *config_pl[] = {“config.pl”, 0};
PERL_SYS_INIT3(&argc,&argv,&env); g_perl_interp = perl_alloc();perl_construct(g_perl_interp);PL_exit_flags |= PERL_EXIT_DESTRUCT_END;perl_parse(g_perl_interp, NULL, 1, config_pl, env);perl_run(g_perl_interp);
do_program_stuff(. . .);
perl_destruct(g_perl_interp);perl_free(g_perl_interp);PERL_SYS_TERM()
}
#include "EXTERN.h"#include "Perl.h"
static PerlInterpreter *g_perl_interp;
int main(int argc, char** argv, char** env) {char *config_pl[] = {“config.pl”, 0};
PERL_SYS_INIT3(&argc,&argv,&env); g_perl_interp = perl_alloc();perl_construct(g_perl_interp);PL_exit_flags |= PERL_EXIT_DESTRUCT_END;perl_parse(g_perl_interp, NULL, 1, config_pl, env);perl_run(g_perl_interp);
do_program_stuff(. . .);
perl_destruct(g_perl_interp);perl_free(g_perl_interp);PERL_SYS_TERM()
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
void get_config(char* key, char* dst) {dSP;SV *ret = NULL;
ENTER;SAVETMPS;PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(key)));PUTBACK;call_pv("get_value", G_SCALAR); SPAGAIN;ret = POPs;strcpy(dst, SvPV);PUTBACK;FREETMPS;LEAVE;
}
Compiling
• Compile as normal, but add flags
cc -c foo.c \ `perl -MExtUtils::Embed -e ccopts`
cc -o interp perlxsi.o interp.o \ `perl -MExtUtils::Embed -e ldopts`
Compiling – Win32
• For Visual Studio & ActivePerlinclude: C:\Perl\lib\CORE
lib dir: C:\Perl\lib\CORE
libs: Perl58.lib
+ have C:\Perl\bin\ in path for dll when running.
Using Modules
• Pure Perl(eg. Acme::Barf)
• XS extensions(eg. Audio::File, Storable)
• Can do, but need to set up dynaloader from C first…
Using Modules
• Dynaloader loads and initialises C modules when you say“use Foo”
• Dynaloader must be setup first
• Then everything should Just Work
DynaloaderEXTERN_C void xs_init (pTHX);EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
EXTERN_C void xs_init(pTHX) {char *file = __FILE__;dXSUB_SYS;
newXS("DynaLoader::boot_DynaLoader",boot_DynaLoader, file);
}
And change your call to perl_parse to:perl_parse(g_perl_interp, xs_init, 1, config_pl, env);
Generate this with:perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c
DynaloaderEXTERN_C void xs_init (pTHX);EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
EXTERN_C void xs_init(pTHX) {char *file = __FILE__;dXSUB_SYS;
newXS("DynaLoader::boot_DynaLoader",boot_DynaLoader, file);
}
And change your call to perl_parse to:perl_parse(g_perl_interp, xs_init, 1, config_pl, env);
Generate this with:perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c
perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c
Manual Pages
• perlembed
• perlcall
• perlapi
• ExtUtils::Embed
XS
• C from Perl
• libraries
• Meddle
• Speed
Libraries• Steal work from others
• Compile & install separately
• XS provides glue to Perl
• Provide a Perl module to improve interface
Library’s Header File// green.h – Interface to green-turtle library
GREEN* new_green(int turtles, char *message);
int free_green(GREEN *green);
int more_turtles(GREEN *green, int eggs);
char** turtles(GREEN *green, char *match);
h2xs
• header 2 XS
• First install your library & headers(so that green.h is in the standard search path)
• Run: h2xs –Oxn Green::Turtle green.h
• Copy green.h to Green/Turtle
• Run: h2xs –Oxn Green::Turtle green.h
h2xs
• This just went and created
• Turtle.pm – Perl template
• Turtle.xs – XS functions
• typemap – maps C types to Perl types
• Makefile.PL
• Some test files and other stuff
• Nearly works… but we have to faff first
Turtle.xs
• Headers (#include <green.h>)
MODULE = Green::Turtle PACKAGE = Green::Turtle
• XS “functions”
Turtle.xsMODULE = Green::Turtle PACKAGE = Green::Turtle
doubleconstant(name,arg) char * name int arg
GREEN *new_green(turtles, message) int turtles char * message
intfree_green(green) GREEN * green
. . .
Turtle.xsGREEN *new_green(turtles, message) int turtles char * message
Becomes:
Green::Turtle::new_green()
typemap
GREEN* T_PTROBJ
Perl will “cast” between scalars & GREEN*
These scalars will be blessed into the GREENPtr package.
When destroyed, memory will not be free’d (but see later).
Turtle.pm
• Normal perl module + some imports
• Good idea to wrap the XS:sub new { my($class) = shift; my($turtles, $msg) = @_; my $self = {}; $self->{_green} = new_green($turtles, $msg); return bless $self, $class;}
sub add_turtles { my($self) = shift; return more_turtles($self->{green}, @_);}
GREEN*
• GREEN* blessed into GREENPtr
• To free, we need to add a DESTROY()
MODULE Green::Turtle PACKAGE GREENPtr
voidDESTROY(green)
GREEN* greenCODE:
free_green(green)
char **
• Perl doesn’t really know what to do so
• is not helpful…
• Can typemap or marshall ourselves, as we’ll want a list of strings, we’ll need to marshall.
char **turtles(green, message) GREEN * green char * message
voidturtles(green, message) GREEN * green char * message PPCODE: { char **ret = turtles(green, message); while (*ret != NULL) { PUSHs(sv_2mortal(newSVpv(*ret, 0))); ret++; } }
XS blocksintcount_colours(turtle)
GREEN* turtlePREINIT:char *name = “george”;CODE:RETVAL = reds(turtle, name)
+ greens(turtle, name);OUTPUT:RETVAL
sub try_words { my($ar,$l,@all,$j) = $_[0];
push(@all, [split "", $_]) foreach @$ar; foreach my $a1 (@all) { foreach my $a2 (@all) { INNA: foreach my $a3 (@all) { for ($j=0;$j<$l;$j++) {
my $w = $a1->[$j].$a2->[$j].$a3->[$j];
unless (exists($w3{$w})){ next INNA; } }
print(join("", @$_)) foreach ($a1, $a2, $a3); print "\n"; } } }
}
sub try_words { my($ar,$l,@all,$j) = $_[0];
push(@all, [split "", $_]) foreach @$ar; foreach my $a1 (@all) { foreach my $a2 (@all) { INNA: foreach my $a3 (@all) { for ($j=0;$j<$l;$j++) {
my $w = $a1->[$j].$a2->[$j].$a3->[$j];
unless (exists($w3{$w})){ next INNA; } }
print(join("", @$_)) foreach ($a1, $a2, $a3); print "\n"; } } }
}
sub try_words { my($ar,$l,@all,$j) = $_[0];
# push(@all, [split "", $_]) foreach @$ar; foreach my $a1 (@$ar) { foreach my $a2 (@$ar) { INNA: foreach my $a3 (@$ar) { if (try_cwords($a1,$a2,$a3,$l,\%w3) ) { print “($a1, $a2, $a3)\n”; } } }
}
inttry_cwords(one,two,thr,len,ref_w3) char* one char* two char* thr int len SV* ref_w3 PREINIT: int i; char three[] = {0,0,0}; HV* w3;
char three[3]; char *one, *two, *thr; SV* ref_w3; HV* w3; int len;
CODE: if (! SvROK(ref_w3)) croak("ref_w3 is not a reference");
w3 = (HV*)SvRV(ref_w3);
for (i=0;i<len; i++) { three[0] = one[i]; three[1] = two[i]; three[2] = thr[i]; /* Check if it's in the hash */ if (!hv_exists(w3, three, 3)) XSRETURN_UNDEF; } RETVAL = 1;
sub try_words { my($ar,$l,@all,$j) = $_[0];
# push(@all, [split "", $_]) foreach @$ar; foreach my $a1 (@$ar) { foreach my $a2 (@$ar) { INNA: foreach my $a3 (@$ar) { if (try_cwords($a1,$a2,$a3,$l,\%w3) ) { print “($a1, $a2, $a3)\n”; } } }
}
C speed hotness
anticoagulativeneuroanatomicaldeterminateness
aeromechanicalaminoguanidineheterophylesis
General XS info
• Lovely man pages– perlxs– perlxstut– perlcall, perlapi, perlwhatever
• Bother someone you know
• Inline::C!
• Inline::C!
Lear on Hybrids
Our mother was the Pussy-cat, our father was the Owl, And so we're partly little beasts and partly little fowl,
The brothers of our familyhave feathers and they hoot, While all the sisters dress in fur and have long tails to boot.