Perl and

SWIG

Olly Betts

Me and SWIG

SWIG

Overview

Basics

Example

namespace Xapian {
  int major_version();
  const char * version_string();
}
use Search::Xapian;
say Search::Xapian::major_version();
say Search::Xapian::version_string();
1
1.2.5

Ambiguous Cases

Ambiguous Cases

Same syntax, different semantics:

void set_entry(const char * name, unsigned value);
set_entry('answer', 42);

Ambiguous Cases

Same syntax, different semantics:

void set_entry(const char * name, unsigned value);
set_entry('answer', 42);
void write_data(const char * ptr, unsigned len);

Ambiguous Cases

Same syntax, different semantics:

void set_entry(const char * name, unsigned value);
set_entry('answer', 42);
void write_data(const char * ptr, unsigned len);
write_data("hello\x00world", 11);

Ambiguous Cases

Same syntax, different semantics:

void set_entry(const char * name, unsigned value);
set_entry('answer', 42);
void write_data(const char * ptr, unsigned len);
my $data = "hello\x00world";
write_data($data, length($data));

Ambiguous Cases

Same syntax, different semantics:

void set_entry(const char * name, unsigned value);
set_entry('answer', 42);
void write_data(const char * ptr, unsigned len);

%typemap(in) (const char * ptr, unsigned len) {
  STRLEN len_tmp;
  $1 = SvPV($input, len);
  $2 = len_tmp;
}
write_data("hello\x00world");

Renaming



namespace Xapian {
  class PostingSource {
    // [...]
    void next(double min_wt);
  };
}
xapian/postingsource.h:4: Warning 314: 'next' is a perl keyword

Renaming

%rename(inc) Xapian::PostingSource::next;

namespace Xapian {
  class PostingSource {
    // [...]
    void next(double min_wt);
  };
}
$postingsource->inc();

Renaming

%rename(inc) next;

namespace Xapian {
  class PostingSource {
    // [...]
    void next(double min_wt);
  };
}
$postingsource->inc();

Pattern renames

Remove a pseudo-namespace prefix

%module MyLib

int mylib_init();
int mylib_crunch_numbers(int a, int b);
use MyLib;

MyLib::mylib_init() or die $!;
say MyLib::mylib_crunch_numbers(6, 28);

Pattern renames

Remove a pseudo-namespace prefix

%module MyLib
%rename("%(regex/^mylib_//)s") "";
int mylib_init();
int mylib_crunch_numbers(int a, int b);
use MyLib;

MyLib::init() or die $!;
say MyLib::crunch_numbers(6, 28);

Style renames

Convert to match preferred naming conventions

%rename("%(undercase)s",%$isfunction) "";

%rename("%(camelcase)s",%$isclass) "";

Ignoring features

%ignore max_size;

Exceptions

%exception {
  [...]
  $action
  [...]
}

Translating C++ Exceptions

%exception {
  try {
    $action
  } catch (std::range_error & e) {
    SWIG_exception(SWIG_IndexError, e.what());
  }
}

Throwing wrapped objects in Perl

%exception {
  try {
    $action
  } catch (const Xapian::Error & e) {
    SV * sv = sv_newmortal();
    sv_setref_pv(sv, "Search::Xapian::Error", (void *) new Xapian::Error(e));
    croak_sv(sv);
    SWIG_fail;
  }
}

Refactored for smaller glue code

static void set_perl_exception() {
  try {
    throw;
  } catch (const Xapian::Error & e) {
    // Call croak_sv(), etc as before.
  }
}

%exception {
  try {
    $action
  } catch (...) {
    set_perl_exception();
    SWIG_fail;
  }
}

Error codes to exceptions

%exception {
  $action
  if (result < 0) {
    SWIG_exception(SWIG_IndexError, strerror(errno));
  }
}

Subclassing

Dynamic typing example

Lego Superman

Dynamic typing example

%typemap(in) const vector<Xapian::Query> &
    (vector<Xapian::Query> v) {
  AV * array = (AV*) SvRV($1);
  int num_items = av_len(q) + 1;
  v.reserve(num_items);
  for (int i = 0; i < num_items; ++i) {
    // Loop body on next slide...
  }
  $1 = &v;
}

Dynamic typing example continued

SV **svp = av_fetch(array, i, 0);
if( svp == NULL )
  croak("Unexpected NULL returned by av_fetch()");
SV *sv = *svp;

if (sv_isa(sv, "Search::Xapian::Query")) {
  Xapian::Query *q;
  SWIG_ConvertPtr(sv, (void **)&q, SWIGTYPE_p_Xapian__Query, 0);
  v.push_back(*q);
} else if (SvOK(sv)) {
  STRLEN len;
  const char * ptr = SvPV(sv, len);
  v.push_back(Xapian::Query(std::string(ptr, len)));
} else {
  croak("USAGE: Search::Xapian::Query->new(OP, @TERMS_OR_QUERIES)");
}

Adding extra methods to wrap

%extend Xapian::Query {
  Query(Query::op op, const vector<Query> & v) {
    return new Xapian::Query(op, v.begin(), v.end());
  }
}

Multiple return values

int decode(const char *s, int & width, int & height);
my @result = decode($input);

Iterators

(Mostly) Automatically Wrapped C++ Iterator

my $i = $matches->begin();
while ($i != $matches->end()) {
  say $i->get_docid();
  $i++;
}

With a little work

foreach my $m ($matches->items) {
  say $m->get_docid();
}

Summary

The End

 Questions welcome

Links

 

Image credits: