Platypus Next Generation

Platypus is getting an update. It’s not backward compatible, so you have to opt-in when you create the platypus instance. That makes it backward compatible for all the old code you may or may not have written. Please spread the word.

# old code:
use FFI::Platypus;
my $ffi = FFI::Platypus->new;

# new code:
use FFI::Platypus 1.00;
my $ffi = FFI::Platypus->new( api => 1 );

You should generally write all new code using the new API so that you can take advantage of the newer features and design fixes. You may want to also consider upgrading your existing code to use the new API for the same reasons.

The main enhancement you get is the ability to pass and return records by value in addition to as pointers of by reference. This missing feature was a design bug in the original implementation of Platypus, and I am pleased to make this feature available. In the old interface records are assumed to be pointers. In the new interface they are assumed to be records, and you have to use the pointer decorator (*) to use pointers to records.

# both versions:
package Foo {
  use FFI::Platypus::Record;
  record_layout
    int bar
    int baz
  ;
}

# old code:
use FFI::Platypus;
my $ffi = FFI::Platypus->new;
# XXX can't use Foo pass-by-value         # pass-by-value
$ffi->type('record(Foo)' => 'foo_t');     # pass-by-pointer/reference

# new code:
use FFI::Platypus 1.00;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->type('record(Foo)'  => 'foo_t');    # pass-by-value
$ffi->type('record(Foo)*' => 'foo_ptr');  # pass-by-pointer/reference

A minor tweak on the internals mean that you can also decorate aliases as pointer or array types.

# old code:
use FFI::Platypus;
my $ffi = FFI::Platypus->new;
$ffi->type('opaque'      => 'o_t');
$ffi->type('opaque*'     => 'o_ptr');
$ffi->type('opaque[10]'  => 'o_array');
# XXX no records-by-value
$ffi->type('record(Foo)' => 'foo_ptr');

# new code:
use FFI::Platypus 1.00;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->type('opaque' => 'o_t');
$ffi->type('o_t*');     # pointer to o_t doesn't require an extra alias
$ffi->type('o_t[10]');  # same for arrays
$ffi->type('record(Foo)' => 'foo_t');
$fii->type('foo_t*');   # or pointers to records

Last, but not least, Platypus also now supports out of the box the common opaque pointer as an object pattern. TL;DR, given a C interface like this:

typedef struct foo_t;

foo_t*      foo_new();
void        foo_set_bar(foo_t*, const char *);
const char *foo_get_bar(foo_t*);
void        foo_free(foo_t*);

You can write Perl like this:

# new code:
package Foo {
  use FFI::Platypus 1.00;
  my $ffi = FFI::Platypus->new( api => 1 );
  $ffi->type('object(Foo)' => 'foo_t');
  $ffi->mangler(sub {
    my $name = shift;
    $name =~ s/^foo_//r;  # prefix all symbol lookups with foo_
  });
  $ffi->attach( new     => [ ] => 'foo_t*' );
  $ffi->attach( set_bar => [ 'foo_t*', 'string' ] );
  $ffi->attach( get_bar => [ 'foo_t*' ] => 'string' );
  $ffi->attach( free    => [ 'foo_t*' ] );
  sub DESTROY
  {
    my $self = shift;
    $self->free;
  }
}

my $foo = Foo->new;
$foo->set_bar("baz");
my $baz = $foo->get_bar;

If Platypus or the new API has piqued your interest or you have questions, we should definitely hang out! Join us on #native on irc.perl.org, check out the project on metacpan or github. If in person interaction is more your jam, then I will be at DCBPW in Baltimore next year along with TPCiH in Houston.

Leave a comment

About Graham Ollis

user-pic Perl programmer with an interest in Alien and FFI technologies. Primary developer of FFI::Platypus and Alien::Build.