Auto-FFI (Peta::FFI)#

PetaPerl includes a built-in Foreign Function Interface that allows calling C library functions directly from Perl — without writing XS, without a C compiler, without any build step. This is a PetaPerl-exclusive feature with no perl5 equivalent.

Architecture#

The FFI system has three layers:

Layer

Module

Purpose

0

Peta::FFI

Raw FFI: dlopen + call with type signatures

1

Peta::FFI::Libc, ::UUID, ::GMP

Pre-baked bindings for specific libraries

2

Peta::FFI::scan()

Discovery: enumerate available system libraries

Layer 0 uses libffi for dispatch — any C function signature works, no code generation required.

Layer 0: Raw FFI#

dlopen / call / dlclose#

use Peta::FFI qw(dlopen call dlclose);

my $lib = dlopen("libm.so.6");
my $result = call($lib, "sqrt", "(d)d", 2.0);   # √2 = 1.4142...
print "sqrt(2) = $result\n";
dlclose($lib);

Type Signature Format#

The signature string "(arg_types)return_type" uses single-character type codes:

Code

C Type

Perl Mapping

v

void

(no value)

i

int

IV

l

long

IV

L

unsigned long / size_t

UV

d

double

NV

f

float

NV

p

const char*

String (input)

P

void* (mutable buffer)

String (output)

Examples:

  • "(d)d" — one double argument, returns double (e.g. sqrt)

  • "(p)L" — one string argument, returns unsigned long (e.g. strlen)

  • "(ppi)i" — two strings + int, returns int

  • "()i" — no arguments, returns int (e.g. getpid)

Library Discovery#

use Peta::FFI qw(scan);

my $libs = scan();
# Returns hashref: { "libz.so.1" => "/usr/lib/libz.so.1", ... }

for my $soname (sort keys %$libs) {
    print "$soname => $libs->{$soname}\n";
}

scan() calls ldconfig -p to enumerate all shared libraries available on the system.

Layer 1: Pre-baked Bindings#

Layer 1 modules provide Perl-native APIs for specific C libraries, with proper argument validation, return value conversion, and error handling. No type signatures needed — just call functions.

Peta::FFI::Libc#

Direct bindings to libc functions, grouped by category:

Process: getpid, getppid, getuid, getgid, geteuid, getegid

Strings: strlen, strerror

Environment: getenv, setenv, unsetenv

Math: abs, labs

System: sleep, usleep, gethostname, uname

File operations: access, unlink, rmdir, mkdir, chmod, chown

use Peta::FFI::Libc qw(getpid gethostname uname);

print "PID: ", getpid(), "\n";
print "Host: ", gethostname(), "\n";

my @info = uname();
print "OS: $info[0] $info[2] ($info[4])\n";

Peta::FFI::UUID#

Bindings to libuuid (must be installed on the system):

use Peta::FFI::UUID qw(uuid_generate uuid_generate_random);

my $uuid = uuid_generate();           # e.g. "550e8400-e29b-41d4-a716-446655440000"
my $rand = uuid_generate_random();    # random-based UUID

Functions: uuid_generate, uuid_generate_random, uuid_generate_time, uuid_parse, uuid_is_null, uuid_unparse.

If libuuid is not installed, use Peta::FFI::UUID dies with a diagnostic message.

Peta::FFI::GMP (Math::GMP)#

Arbitrary-precision integer arithmetic via libgmp. Integrates with the Math::GMP class name for compatibility with CPAN’s Math::GMP:

use Math::GMP;

my $big = Math::GMP->new("123456789012345678901234567890");
my $sum = $big + 42;
print "$sum\n";

GMP integers are heap-allocated mpz_t values stored as blessed references. Operator overloading (+, -, *, /, %, **, <=>, "") works identically to the XS module.

If libgmp is not installed, use Math::GMP dies with a diagnostic message.

When to Use FFI vs Native Modules#

Scenario

Recommendation

Common CPAN module (List::Util, etc.)

Native module (already built in)

System library not yet wrapped

Layer 0 raw FFI

Frequent calls to same library

Request Layer 1 pre-baked binding

One-off experiment

Layer 0 raw FFI

Layer 0 FFI has per-call overhead from argument marshalling and libffi dispatch. Layer 1 pre-baked bindings call Rust/C directly and are faster. Native modules are fastest — same speed as built-in operators.