You've installed a native library with Alien::Xrepo. Now what? The whole point is to call a C function from Perl but there are three very different ways to do it. This article walks through the same task (calling zlibVersion() from Perl) using three different binding tools, shows the trade-offs, and helps you pick the right one.
All three examples assume:
my $repo = Alien::Xrepo->new;
my $zlib = $repo->install('zlib');
After this, $zlib->libpath points to zlib.dll / libz.so, $zlib->includedirs has zlib.h, and $zlib->links has the -lz flag.
Affix: one-liner FFI
Best for: Single functions, quick prototypes, CPAN-distributed bindings.
use v5.40;
use Alien::Xrepo;
use Affix;
my $zlib = Alien::Xrepo->new->install('zlib');
affix $zlib->libpath, 'zlibVersion', [], String;
say 'zlib ' . zlibVersion();
affix is a declarative one-liner: tell it the shared library, function name, argument types, and return type. No XS compilation, no compiler needed at runtime.
Pros
- Zero compilation — works with any compiler or without one.
- Single statement per function.
- The
libpathfromAlien::Xrepoplugs directly intoaffix.
Cons
- Each function needs a separate
affixline. - Whole-library wrapping requires
Affix::Wrap(parses C headers via a compiler), which can be very slow on some platforms.
FFI::Platypus: the classic
Best for: Old code you wrote before Affix existed?
use v5.40;
use Alien::Xrepo;
use FFI::Platypus;
my $zlib = Alien::Xrepo->new->install('zlib');
my $ffi = FFI::Platypus->new;
$ffi->lib( $zlib->libpath );
$ffi->attach( 'zlibVersion', [] => 'string' );
say 'zlib ' . zlibVersion();
FFI::Platypus is an established workhorse. It reads the same libpath and also accepts an explicit lib call, which is handy when you need to load several libraries.
Pros
- Mature, well-tested, wide platform coverage.
- Decent type support (callbacks, pointers, nested structs, records).
- Larger ecosystem; many CPAN modules already use it.
Cons
- More boilerplate than Affix (construct the object, call
lib, thenattach). - Requires
FFI::Platypusto be installed.
Inline::C: full C extensions
Best for: Performance-critical paths, code that needs headers and multiple libraries linked together, XS authorship.
use v5.40;
use Alien::Xrepo;
use Config;
my $repo = Alien::Xrepo->new;
my $zlib = $repo->install('zlib');
my $libpng = $repo->install('libpng');
# Runtime include/link flags
my $incs = join ' ', map { "-I$_" } (@{ $zlib->includedirs }, @{ $libpng->includedirs });
my $libs = join ' ',
map { "-L$_" } (@{ $zlib->linkdirs }, @{ $libpng->linkdirs }),
map { "-l$_" } (@{ $zlib->links }, @{ $libpng->links });
# PATH for DLLs
local $ENV{PATH} = join $Config{path_sep},
@{[$zlib->bin_dir]}, @{[$libpng->bin_dir]}, $ENV{PATH};
use Inline ();
Inline->bind(
'C',
<<'C',
#include <zlib.h>
#include <png.h>
const char* versions() {
static char buf[128];
snprintf(buf, sizeof buf, "zlib %s / libpng %s", zlibVersion(), PNG_LIBPNG_VER_STRING);
return buf;
}
C
INC => $incs,
LIBS => $libs
);
say versions();
Inline::C compiles a real C extension at build time. You get full access to the C preprocessor (macros, headers) and can call any function, define any struct, and chain any number of libraries in a single XS.
Pros
- True C code! Use any valid expression, any macro, any callback.
- Multi-library linking in one shot (here: zlib + libpng together).
- Compiled code runs at native speed.
Cons
- Requires a C compiler at install time.
- Slower startup (XS compilation on first run).
- Note: avoid bare
(void)in parameter lists; Inline::C's parser mishandles that signature.
Comparison
| Affix | FFI::Platypus | Inline::C | |
|---|---|---|---|
| Compiler needed | No | No | Yes |
| Boilerplate | 1 line per function | 3-4 lines per function | Full C code block |
| Multi-library | Manual (one affix per lib) |
Manual (lib calls) |
Merged into one INC/LIBS |
| Header macros | Not available | Not available | Full C preprocessor |
| Performance | FFI | FFI | Native XS |
| Best for | Quick one-offs | ??? | Performance-critical XS |
Which should you choose?
- Start with Affix if you just need to get up and running quickly and want the least boilerplate. It's the fastest way from install to output.
- Use FFI::Platypus if you prefer it? It's still the most widely used FFI.
- Use Inline::C if your binding code needs the C preprocessor (macro expansion, conditional compilation, header-only libraries) or needs to call multiple libraries in a single XS function.
In every case, Alien::Xrepo hands you libpath, includedirs, linkdirs, and links; the same four things each binding tool needs to find the library. Swap one binding for another without changing the install step.
Comments