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.