Affix
view release on metacpan or search on metacpan
lib/Affix.pod view on Meta::CPAN
use v5.40;
use Affix qw[:all];
# Bind a function and call it natively.
# Here, we use libm which might be in libm.so, msvcrt.dll, etc.
# C: double pow(double x, double y);
affix libm(), 'pow', [ Double, Double ] => Double;
say pow( 2.0, 10.0 ); # 1024
# Working with C structs is easy
# C: typedef struct { int x; int y; } Point;
# void draw_point(Point p);
typedef Point => Struct[ x => Int, y => Int ];
affix $lib, 'draw_point', [ Point() ] => Void;
draw_point( { x => 10, y => 20 } );
# We can also allocate and manage raw memory and write data to it
my $ptr = Affix::malloc(1024);
$ptr->[0] = ord('t'); # Direct byte-level access
memcpy( $ptr, 'test', 4 );
# We can also do pointer arithmetic to create new references
my $offset_ptr = Affix::ptr_add( $ptr, 12 );
memcpy( $offset_ptr, 'test', 4 );
# Inspect memory with a hex dump to STDOUT
Affix::dump( $ptr, 32 );
# And release the memory. This is automatic when such a scalar falls out of scope
Affix::free($ptr);
=head1 DESCRIPTION
Affix is a high-performance, developer friendly Foreign Function Interface (FFI) extension for Perl. It serves as a
universal bridge to the vast ecosystem of native software including those written in C, Rust, Zig, C++, Go, Fortran,
and more without writing XS code, managing a compiler, or compromising on execution speed. Affix also comes with an
extensive type system including native support for primitives (including half-width floats and 128bit integers), nested
C style structs, union, fixed size arrays, smart handling of enums, SIMD vector types, and, of course, pointers.
At its core, Affix is powered by L<infix|https://github.com/sanko/infix/>, a lightweight JIT (Just-In-Time) compilation
engine designed with speed and portability as its primary objectives. Unlike traditional FFI solutions that rely on
generic, per-call dispatch loops, Affix generates optimized machine code trampolines at runtime. These trampolines
handle argument marshalling and return value processing directly, significantly reducing the overhead of crossing the
boundary between Perl and native code. The underlying infix engine is L<rigorously tested across a diverse range of
environments|https://github.com/sanko/infix/actions/workflows/ci.yml>, ensuring reliable performance on Linux, Windows,
macOS, Solaris, and various BSD flavors. It supports multiple CPU architectures including C<x86_64> and C<AArch64>
(ARM64).
Affix serves as a universal bridge to the vast ecosystem of native software. Whether you're tapping into a legacy
Fortran math routine, a modern Rust crate, or a system-level C library, Affix makes the integration safe, idiomatic,
and exceptionally fast.
=head1 EXPORTS
Affix exports standard types (C<Int>, C<Double>, etc.) and core functions (C<affix>, C<wrap>, C<load_library>) by
default. You can control imports using tags:
use Affix qw[:all]; # Import everything
use Affix qw[:lib]; # Library helpers (libc, libm, load_library...)
use Affix qw[:memory]; # malloc, free, memcpy, cast, dump...
use Affix qw[:pin]; # Variable binding (pin, unpin)
use Affix qw[:types]; # Types only (Int, Struct, Pointer...)
=head1 CORE API
These functions are the primary entry points for interacting with foreign libraries.
=head2 C<affix( $lib, $symbol, $params, $return )>
Attaches a symbol from a library to a named Perl subroutine in the current namespace.
=over
=item * B<C<$lib>>: A library handle returned by C<load_library>, a string name, or C<undef> to search the currently running process/executable.
=item * B<C<$symbol>>: The name of the C function. To install it under a different name in Perl, pass an array reference: C<['c_name', 'perl_alias']>. To bind a raw memory address, pass it directly: C<[$ptr, 'perl_alias']>.
=item * B<C<$params>>: An C<ArrayRef> of Affix Type objects representing the function's arguments.
=item * B<C<$return>>: A single Affix Type object representing the return value.
=back
# Standard: Load from library
affix $lib, 'pow', [ Double, Double ] => Double;
# Rename: Load 'pow', install as 'power' in Perl
affix $lib, [ pow => 'power' ], [ Double, Double ] => Double;
# Raw pointer: Bind a specific memory address (e.g., from dlsym or JIT)
affix undef,[ $ptr => 'my_func' ], [Int] => Void;
On success, installs the subroutine and returns the generated code reference.
=head2 C<wrap( $lib, $symbol, $params, $return )>
Creates a wrapper around a given symbol and returns it as an anonymous C<CODE> reference. Arguments are identical to
C<affix> except you cannot provide an alias.
my $pow = wrap $lib, 'pow', [ Double, Double ] => Double;
my $result = $pow->( 2, 5 );
=head2 C<direct_affix( ... )> / C<direct_wrap( ... )>
B<Experimental:> Bypasses standard safety checks and intermediate processing for maximum performance with simple
primitives. Generates highly specialized trampolines that read Perl SVs directly from the stack.
=head2 C<< typedef( $name => $type ) >>
Registers a named type alias. This makes signatures more readable and is required for recursive types and smart Enums.
# C: typedef struct { int x; int y; } Point;
typedef Point => Struct[ x => Int, y => Int ];
# C: typedef double Vector3[3];
typedef Vector3 => Array[ Double, 3 ];
# C: typedef int* IntPtr;
typedef IntPtr => Pointer[ Int ];
Once registered, use these types in signatures by calling them as functions: C<Point()>.
=head2 C<coerce( $type, $value )>
Explicitly hints types for L<Variadic Functions|/Variadic Functions (VarArgs)>.
# Hint that we are passing a Float, not a Double
coerce( Float, 1.5 );
=head1 VARIABLES & PINNING
Affix allows you to link Perl scalars directly to global or external variables exported by C libraries.
=head2 C<pin( $var, $lib, $symbol, $type )>
Binds a scalar to a C variable. Reading the scalar reads C memory; writing to it updates C memory immediately.
lib/Affix.pod view on Meta::CPAN
=head3 C<sizeof( $type )>
Returns the size, in bytes, of any Affix Type object or registered C<typedef> name.
# C: sizeof(int);
say sizeof( Int ); # 4 (usually)
# C: sizeof(Point);
say sizeof( Point() ); # 8
=head3 C<alignof( $type )>
Returns the alignment boundary (in bytes) required by the C ABI for the given type.
say alignof( Int64 ); # 8 (usually)
# Struct alignment is dictated by its largest member
typedef Mixed => Struct[ a => Char, b => Double ];
say alignof( Mixed() ); # 8
=head3 C<offsetof( $struct_or_union, $field_name )>
Returns the byte offset of a named field within an Aggregate type (Struct or Union). This is incredibly useful for
manual pointer arithmetic.
typedef Rect => Struct[ x => Int, y => Int, w => Int, h => Int ];
# C: offsetof(Rect, w);
say offsetof( Rect(), 'w' ); # 8 (skips x and y, 4 bytes each)
=head3 C<types()>
Returns a list of all custom type names currently registered in Affix's global type registry via C<typedef>. In scalar
context, returns the total number of registered types.
my @known_types = types();
say "Registered types: " . join(', ', @known_types);
=head1 INTERFACING WITH OTHER LANGUAGES
Because Affix dynamically loads symbols according to the C Application Binary Interface (C ABI), it can interact with
libraries written in almost any language, provided they expose their functions correctly. Companion modules like
L<Affix::Build> make compiling these languages seamless.
Here are the requirements and quirks for interfacing with non-C languages.
=head2 C++
C++ uses "name mangling" to support function overloading and namespaces, which alters the final symbol name inside the
compiled library.
=over
=item 1. B<Prevent Mangling:> Wrap your exported functions in C<extern "C"> to ensure they have predictable names.
extern "C" {
int add(int a, int b) { return a + b; }
}
=item 2. B<Or Use Mangled Names:> If you cannot change the C++ source, you must look up the exact mangled name (e.g., C<_Z3addii>) using tools like C<nm> or C<objdump>, and bind to that.
=item 3. B<Object Methods:> Calling an object's method requires passing the object instance pointer (the C<this> pointer) as the first argument. Use the C<ThisCall( ... )> wrapper around your callback/signature to automatically insert C<Pointer[Void]...
=back
=head2 Rust
Rust does not use the C ABI by default. You must explicitly instruct the compiler to format the function correctly.
=over
=item 1. B<Exporting:> Use C<#[no_mangle]> and C<pub extern "C">.
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 { a + b }
=item 2. B<Structs:> Rust structs must be annotated with C<#[repr(C)]> to guarantee their memory layout matches C (and thus Affix's C<Struct>).
=item 3. B<Strings:> Rust strings are not null-terminated. You must receive C<String> arguments as C<*const std::os::raw::c_char> and convert them using C<CStr::from_ptr>.
=back
=head2 Fortran
Fortran relies heavily on pass-by-reference.
=over
=item 1. B<Pointers Everywhere:> Unless a parameter uses the modern Fortran C<VALUE> attribute, you must pass everything as a pointer. If the function expects a Float, your Affix signature must be C<Pointer[Float]>.
=item 2. B<Name Mangling:> Most Fortran compilers convert subroutine names to lowercase and append an underscore. A Fortran subroutine named C<CALC_STRESS> will likely be exported as C<calc_stress_>.
=item 3. B<Strings:> Fortran does not use null-terminated strings. When passing character arrays, Fortran compilers silently append hidden "length" parameters at the B<end> of the argument list (passed by value as integers).
=back
=head2 Assembly
When writing raw Assembly (NASM/GAS), you must manually adhere to the calling convention of your target platform:
=over
=item * B<Linux/macOS (System V AMD64 ABI):> Arguments are passed in C<rdi, rsi, rdx, rcx, r8, r9>, with the rest on the stack.
=item * B<Windows (Microsoft x64):> Arguments are passed in C<rcx, rdx, r8, r9>, with "shadow space" reserved on the stack.
=back
=head2 Go
Go libraries can be loaded if they are compiled with C<-buildmode=c-shared>. Note that Go slices and strings contain
internal metadata (length/capacity) and do not map directly to C arrays or C<char*>. Use the C<C> package inside Go
(C<import "C">) and C<*C.char> to bridge the boundary.
=head1 ERROR HANDLING & DEBUGGING
Bridging two entirely different runtimes can lead to spectacular crashes if types or memory boundaries are mismatched.
Affix provides built-in tools to help you identify what went wrong.
=head2 Error Handling
lib/Affix.pod view on Meta::CPAN
# Suppose a C file-open function fails
my $fd = c_open("/does/not/exist");
if (!$fd) {
my $err = errno();
# String context
say "Failed to open: $err"; # "No such file or directory"
# Numeric context
if (int($err) == 2) {
say "Code 2 specifically triggered.";
}
}
B<Note:> You must call C<errno()> immediately after the C function invokes, as subsequent Perl operations (like
printing to STDOUT) might overwrite the system's error register.
=head2 Memory Inspection
=head3 C<dump( $pin, $length_in_bytes )>
Prints a formatted hex dump of the memory pointed to by a Pin directly to C<STDOUT>. This is an invaluable tool for
verifying that C structs or buffers contain the data you expect.
my $ptr = strdup("Affix Debugging");
dump($ptr, 16);
# Output:
# Dumping 16 bytes from 0x55E9A8A5 at script.pl line 42
# 000 41 66 66 69 78 20 44 65 62 75 67 67 69 6e 67 00 | Affix Debugging.
=head3 C<sv_dump( $scalar )>
Dumps Perl's internal interpreter structure (SV) for a given scalar to C<STDOUT>. This exposes the raw flags, reference
counts, and memory layout of the Perl variable itself.
my $val = 42;
sv_dump($val);
# Exposes IV flags, memory addresses of the SV head, etc.
=head2 Advanced Debugging
=head3 C<set_destruct_level( $level )>
Sets the internal C<PL_perl_destruct_level> variable.
When testing XS/FFI code for memory leaks using tools like Valgrind or AddressSanitizer, you often want Perl to
meticulously clean up all global memory during its destruction phase (otherwise the leak checker will be flooded with
false-positive "leaks" that are actually just memory Perl intentionally leaves to the OS to reclaim).
# Call this at the start of your script when running under Valgrind
set_destruct_level(2);
=head1 COMPANION MODULES
Affix ships with two powerful companion modules to streamline your FFI development:
=over
=item * L<B<Affix::Wrap>|Affix::Wrap>: Parses C/C++ headers using the Clang AST to automatically generate Affix bindings for entire libraries.
=item * L<B<Affix::Build>|Affix::Build>: A polyglot builder that compiles inline C, C++, Rust, Zig, Go, and 15+ other languages into dynamic libraries you can bind instantly.
=back
=head1 THREAD SAFETY & CONCURRENCY
Affix bridges Perl (a single-threaded interpreter, generally) with libraries that may be multi-threaded. This creates
potential hazards that you must manage.
=head2 1. Initialization Phase vs. Execution Phase
Functions that modify Affix's global state are B<not thread-safe>. You must perform all definitions in the main thread
before starting any background threads or loops in the library.
Unsafe operations that you should never call from Callbacks or in a threaded context:
=over
=item * C<affix( ... )> - Binding new functions.
=item * C<typedef( ... )> - Registering new types.
=back
=head2 2. Callbacks
When passing a Perl subroutine as a C<Callback>, avoid performing complex Perl operations like loading modules or
defining subs inside callbacks triggered on a foreign thread. Such callbacks should remain simple: process data, update
a shared variable, and return.
If the library executes the callback from a background thread (e.g., window managers, audio callbacks), Affix attempts
to attach a temporary Perl context to that thread. This should be sufficient but Perl is gonna be Perl.
=head1 RECIPES & EXAMPLES
See L<The Affix Cookbook|https://github.com/sanko/Affix.pm/discussions/categories/recipes> for comprehensive guides to
using Affix.
=head2 Linked List Implementation
# C equivalent:
# typedef struct Node {
# int value;
# struct Node* next;
# } Node;
# int sum_list(Node* head);
typedef 'Node'; # Forward declaration for recursion
typedef Node => Struct[
value => Int,
next => Pointer[ Node() ]
];
# Create a list: 1 -> 2 -> 3
my $list = {
value => 1,
next => {
value => 2,
next => {
value => 3,
next => undef # NULL
( run in 1.066 second using v1.01-cache-2.11-cpan-2398b32b56e )