running away to c xs – more faff than fear london perl workshop – dec 04 alex gough...

Post on 26-Dec-2015

221 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Running Away to C

XS – More faff than fearLondon Perl Workshop – Dec 04

Alex Goughalex@earth.li

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.

top related