NAME

FFI::Platypus::Type - Defining types for FFI::Platypus

VERSION

version 0.16

SYNOPSIS

OO Interface:

use FFI::Platypus;
my $ffi = FFI::Platypus->new;
$ffi->type('int' => 'my_int');

Declarative interface:

use FFI::Platypus::Declare
  qw( int void ),
  ['int' => 'my_int'];

DESCRIPTION

This document describes how to define types using FFI::Platypus. Types may be "defined" ahead of time, or simply used when defining or attaching functions.

# OO example of defining types
use FFI::Platypus;
my $ffi = FFI::Platypus->new;
$ffi->type('int');
$ffi->type('string');

# OO example of simply using types in function declaration or attachment
my $f = $ffi->function(puts => ['string'] => 'int');
$ffi->attach(puts => ['string'] => 'int');

If you are using the declarative interface, you can either pass the types you need to the FFI::Platypus::Declare use invocation, or you can use the FFI::Platypus::Declare#type function. The advantage of the former is that it creates a Perl constant for that type so that you do not need to use quotation marks when using the type.

# Declarative with use
use FFI::Platypus::Declare 'string', 'int';
attach puts => [string] => int;

# Declarative with type
use FFI::Platypus::Declare;
type 'string';
type 'int';
attach puts => ['string'] => 'int';

Unless you are using aliases the FFI::Platypus#type method or FFI::Platypus::Declare#type function are not necessary, but they will throw an exception if the type is incorrectly specified or not supported, which may be helpful.

meta information about types

You can get the size of a type using the FFI::Platypus#sizeof method.

# OO interface
my $intsize = $ffi->sizeof('int');
my intarraysize = $ffi->sizeof('int[64]');

# Declare interface
my $intsize = sizeof 'int';
my intarraysize = sizeof 'int[64]';

converting types

Sometimes it is necessary to convert types. In particular various pointer types often need to be converted for consumption in Perl. For this purpose the FFI::Platypus#cast method is provided. It needs to be used with care though, because not all type combinations are supported. Here are some useful ones:

# OO interface
my $address = $ffi->cast('string' => 'opaque', $string);
my $string  = $ffi->cast('opaque' => 'string', $pointer);

# Declare interface
use FFI::Platypus::Declare;
my $address = cast 'string' => 'opaque', $string;
my $string  = cast 'opaque' => 'string', $pointer;

aliases

Some times using alternate names is useful for documenting the purpose of an argument or return type. For this "aliases" can be helpful. The second argument to the FFI::Platypus#type method or FFI::Platypus::Declare#type function can be used to define a type alias that can later be used by function declaration and attachment.

# OO style
use FFI::Platypus;
my $ffi = FFI::Platypus->new;
$ffi->type('int'    => 'myint');
$ffi->type('string' => 'mystring');
my $f = $ffi->function( puts => ['mystring'] => 'myint' );
$ffi->attach( puts => ['mystring'] => 'myint' );

# Declarative style
use FFI::Platypus::Declare;
type 'int'    => 'myint';
type 'string' => 'mystring';
attach puts => ['mystring'] => 'myint';

# Declarative style with use (and with fewer quotes)
use FFI::Platypus::Declare
  [ int    => 'myint' ],
  [ string => 'mystring' ];
attach puts => [mystring] => myint;

Aliases are contained without the FFI::Platypus object, or the current package if you are using FFI::Platypus::Declare, so feel free to define your own crazy types without stepping on the toes of other CPAN Platypus developers.

TYPE CATEGORIES

Native types

So called native types are the types that the CPU understands that can be passed on the argument stack or returned by a function. It does not include more complicated types like arrays or structs, which can be passed via pointers (see the opaque type below). Generally native types include void, integers, floats and pointers.

the void type

This can be used as a return value to indicate a function does not return a value (or if you want the return value to be ignored).

integer types

The following native integer types are always available (parentheticals indicates the usual corresponding C type):

sint8

Signed 8 bit byte (signed char, int8_t).

uint8

Unsigned 8 bit byte (unsigned char, uint8_t).

sint16

Signed 16 bit integer (short, int16_t)

uint16

Unsigned 16 bit integer (unsigned short, uint16_t)

sint32

Signed 32 bit integer (int, int32_t)

uint32

Unsigned 32 bit integer (unsigned int, uint32_t)

sint64

Signed 64 bit integer (long or long long, int64_t)

uint64

Unsigned 64 bit integer (unsigned long or unsigned long long, uint64_t)

You may also use uchar, ushort, uint and ulong as short names for unsigned char, unsigned short, unsigned int and unsigned long.

These integer types are also available, but there actual size and sign may depend on the platform.

char

Somewhat confusingly, char is an integer type! This is really an alias for either sint8_t or uint8_t depending on your platform. If you want to pass a character (not integer) in to a C function that takes a character you want to use the perl ord function. Here is an example that uses the standard libc isalpha, isdigit type functions:

use FFI::Platypus::Declare
  'int',
  [int => 'character'];

lib undef;

my @list = qw( 
  alnum alpha ascii blank cntrl digit lower print punct 
  space upper xdigit
);

attach "is$_" => [character] => int for @list;

my $char = shift(@ARGV) || 'a';

no strict 'refs';
printf "'%s' is %s %s\n", $char, $_, &{'is'.$_}(ord $char) for @list;
size_t

This is usually an unsigned long, but it is up to the compiler to decide. The malloc function is defined in terms of size_t:

use FFI::Platypus::Declare qw( size_t opaque );
attach malloc => [size_t] => opaque;

(Note that you can get malloc from FFI::Platypus::Memory).

There are a number of other types that may or may not be available if they are detected when FFI::Platypus is installed. This includes things like wchar_t, off_t, wint_t. You can use this script to list all the integer types that FFI::Platypus knows about, plus how they are implemented.

use FFI::Platypus::Declare;

foreach my $type_name (sort FFI::Platypus->types)
{
  my $meta = type_meta $type_name;
  next unless $meta->{element_type} eq 'int';
  printf "%20s %s\n", $type_name, $meta->{ffi_type};
}

If you need a common system type that is not provided, please open a ticket in the Platypus project's GitHub issue tracker. Be sure to include the usual header file the type can be found in.

floating point types

The following native floating point types are always available (parentheticals indicates the usual corresponding C type):

float

Single precision floating point (float)

double

Double precision floating point (double)

The long double type may be supported in a future version.

opaque pointers

Opaque pointers are simply a pointer to a region of memory that you do not manage, and do not know the structure of. It is like a void * in C. These types are represented in Perl space as integers and get converted to and from pointers by FFI::Platypus. You may use pointer as an alias for opaque. (The Platypus documentation uses the convention of using "pointer" to refer to pointers to known types (see below) and "opaque" as short hand for opaque pointer).

As an example, libarchive defines struct archive type in its header files, but does not define its content. Internally it is defined as a struct type, but the caller does not see this. It is therefore opaque to its caller. There are archive_read_new and archive_write_new functions to create a new instance of this opaque object and archive_read_free and archive_write_free to destroy this objects when you are done.

use FFI::Platypus::Declare qw( opaque int );
attach archive_read_new   => []       => opaque;
attach archive_write_new  => []       => opaque;
attach archive_read_free  => [opaque] => int;
attach archive_write_free => [opaque] => int;

As a special case, when you pass undef into a function that takes an opaque type it will be translated into NULL for C. When a C function returns a NULL pointer, it will be translated back to undef.

Strings

From the CPU's perspective, strings are just pointers. From Perl and C's perspective, those pointers point to a series of characters. For C they are null terminates ("\0"). FFI::Platypus handles the details where they differ. Basically when you see char * or const char * used in a C header file you can expect to be able to use the string type.

use FFI::Platypus::Declare qw( string int );
attach puts => [string] => int;

Currently strings are only supported as simple argument and return types and as argument (but not return types) for closures. In the future pointers to strings or arrays of strings may be supported.

Pointer / References

In C you can pass a pointer to a variable to a function in order accomplish the task of pass by reference. In Perl the same is task is accomplished by passing a reference (although you can also modify the argument stack thus Perl supports proper pass by reference as well).

With FFI::Platypus you can define a pointer types to any of the native types described above (that is all the types we have covered so far except for strings). When using this you must make sure to pass in a reference to a scalar, or undef (undef will be translated into NULL).

If the C code makes a change to the value pointed to by the pointer, the scalar will be updated before returning to Perl space. Example, with C code.

/* foo.c */
void increment_int(int *value)
{
  if(value != NULL)
    (*value)++;
  else
    fprintf(stderr, "NULL pointer!\n");
}

# foo.pl
use FFI::Platypus::Declare 'void', ['int*' =>'int_p'];
lib 'libfoo.so'; # change to reflect the dynamic lib 
                 # that contains foo.c
attach increment_int => [int_p] => void;
my $i = 0;
increment_int(\$i);   # $i == 1
increment_int(\$i);   # $i == 2
increment_int(\$i);   # $i == 3
increment_int(undef); # prints "NULL pointer!\n"

Fixed length arrays

Fixed length arrays of native types are supported by FFI::Platypus. Like pointers, if the values contained in the array are updated by the C function these changes will be reflected when it returns to Perl space. An example of using this is the Unix pipe command which returns a list of two file descriptors as an array.

use FFI::Platypus::Declare qw( int );

lib undef;
attach [pipe=>'mypipe'] => ['int[2]'] => int;

my @fd = (0,0);
mypipe(\@fd);
my($fd1,$fd2) = @fd;

print "$fd1 $fd2\n";

Closures

A closure (called a "callback" by FFI::Raw, we use the libffi terminology) is a Perl subroutine that can be called from C. In order to be called from C it needs to be passed to a C function. To define the closure type you need to provide a list of argument types and a return type. As of this writing only native types and strings are supported as closure argument types and only native types are supported as closure return types. Here is an example, with C code:

/*
 * closure.c - on Linux compile with: gcc closure.c -shared -o closure.so -fPIC
 */

#include <stdio.h>

typedef int (*closure_t)(int);
closure_t my_closure = NULL;

void set_closure(closure_t value)
{
  my_closure = value;
}

int call_closure(int value)
{
  if(my_closure != NULL)
    return my_closure(value);
  else
    fprintf(stderr, "closure is NULL\n");
}

And the Perl code:

use FFI::Platypus::Declare
  'int', 'void', 'string',
  ['(int)->int' => 'closure_t'];

lib './closure.so';
lib undef; # for puts

attach set_closure => [closure_t] => void;
attach call_closure => [int] => int;
attach puts => [string] => int;

my $closure1 = closure { $_[0] * 2 };
set_closure($closure1);
puts(call_closure(2)); # prints "4"

my $closure2 = closure { $_[0] * 4 };
set_closure($closure2);
puts(call_closure(2)); # prints "8"

The syntax for specifying a closure type is a list of comma separated types in parentheticals followed by a narrow arrow ->, followed by the return type for the closure. For example a closure that takes a pointer, an integer and a string and returns an integer would look like this:

$ffi->type('(opaque, int, string) -> int' => 'my_closure_type');

Care needs to be taken with scoping and closures, because of the way Perl and C handle responsibility for allocating memory differently. Perl keeps reference counts and frees objects when nothing is referencing them. In C the code that allocates the memory is considered responsible for explicitly free'ing the memory for objects it has created when they are no longer needed. When you pass a closure into a C function, the C code has a pointer or reference to that object, but it has no way up letting Perl know when it is no longer using it. As a result, if you do not keep a reference to your closure around it will be free'd by Perl and if the C code ever tries to call the closure it will probably SIGSEGV. Thus supposing you have a C function set_closure that takes a Perl closure, this is almost always wrong:

set_closure(closure { $_[0] * 2 });  # BAD

In some cases, you may want to create a closure shouldn't ever be free'd. For example you are passing a closure into a C function that will retain it for the lifetime of your application. You can use the sticky keyword to indicate this, without the need to keep a reference of the closure:

set_closure(sticky closure { $_[0] * 2 }); # OKAY 

Custom Types

Custom Types in Perl

Platypus custom types are the rough analogue to typemaps in the XS world. They offer a method for converting Perl types into native types that the libffi can understand and pass on to the C code.

Example 1: Integer constants

Say you have a C header file like this:

/* possible foo types: */
#define FOO_STATIC  1
#define FOO_DYNAMIC 2
#define FOO_OTHER   3

typedef int foo_t;

void foo(foo_t foo);
foo_t get_foo();

One common way of implementing this would be to create and export constants in your Perl module, like this:

package Foo;

use FFI::Platypus::Declare qw( void int );
use base qw( Exporter );

our @EXPORT_OK = qw( FOO_STATIC FOO_DYNAMIC FOO_OTHER foo get_foo );

ues constant FOO_STATIC  => 1;
ues constant FOO_DYNAMIC => 2;
ues constant FOO_OTHER   => 3;

attach foo => [int] => void;
attach get_foo => [] => int;

Then you could use the module thus:

use Foo qw( foo FOO_STATIC );
foo(FOO_STATIC);

If you didn't want to rely on integer constants or exports, you could also define a custom type, and allow strings to be passed into your function, like this:

package Foo;

use FFI::Platypus::Declare qw( void );
use base qw( Exporter );

our @EXPORT_OK = qw( foo get_foo );

my %foo_types = (
  static  => 1,
  dynamic => 2,
  other   => 3,
);
my %foo_types_reverse = reverse %foo_types;

custom_type foo_t => {
  native_type    => 'int',
  native_to_perl => sub {
    $foo_types{$_[0]};
  },
  perl_to_native => sub {
    $foo_types_reverse{$_[0]};
  },
};

attach foo => ['foo_t'] => void;
attach get_foo => [] => foo_t;

Now when an argument of type foo_t is called for it will be converted from an appropriate string representation, and any function that returns a foo_t type will return a string instead of the integer representation:

use Foo;
foo('static');

Example 2: Blessed references

Supposing you have a C library that uses an opaque pointer with a pseudo OO interface, like this:

typedef struct foo_t;

foo_t *foo_new();
void foo_method(foo_t *, int argument);
void foo_free(foo_t *);

One approach to adapting this to Perl would be to create a OO Perl interface like this:

package Foo;

use FFI::Platypus::Declare
  'void', 'int';
use FFI::Platypus::API qw( arguments_get_string );

custom_type foo_t => {
  native_type    => 'opaque',
  native_to_perl => sub {
    my $class = arguments_get_string(0);
    bless \$_[0], $class;
  }
  perl_to_native => sub { ${$_[0]} },
};

attach [ foo_new => 'new' ] => [ string ] => 'foo_t' );
attach [ foo_method => 'method' ] => [ 'foo_t', int ] => void;
attach [ foo_free => 'DESTROY' ] => [ 'foo_t' ] => void;

my $foo = Foo->new;

Here we are blessing a reference to the opaque pointer when we return the custom type for foo_t, and dereferencing that reference before we pass it back in. The function arguments_get_string queries the C arguments to get the class name to make sure the object is blessed into the correct class (for more details on the custom type API see FFI::Platypus::API), so you can inherit and extend this class like a normal Perl class. This works because the C "constructor" ignores the class name that we pass in as the first argument. If you have a C "constructor" like this that takes arguments you'd have to write a wrapper for new.

I good example of a C library that uses this pattern, including inheritance is libarchive. Platypus comes with a more extensive example in examples/archive.pl that demonstrates this.

Example 3: Pointers with pack / unpack

TODO

See example FFI::Platypus::Type::StringPointer.

Example 4: Custom Type modules and the Custom Type API

TODO

See example FFI::Platypus::Type::PointerSizeBuffer.

Custom Types in C/XS

Custom types written in C or XS are a future goal of the FFI::Platypus project. They should allow some of the flexibility of custom types written in Perl, with potential performance improvements of native code.

SEE ALSO

FFI::Platypus

Main platypus documentation.

FFI::Platypus::Declare

Declarative interface for FFI::Platypus.

FFI::Platypus::API

Custom types API.

FFI::Platypus::Type::StringPointer

String pointer type.

AUTHOR

Graham Ollis <plicease@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Graham Ollis.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.