Affix

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

# NAME

Affix - A Foreign Function Interface eXtension

# SYNOPSIS

```perl
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);
```

# 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 [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 [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 `x86_64` and `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.

# EXPORTS

Affix exports standard types (`Int`, `Double`, etc.) and core functions (`affix`, `wrap`, `load_library`) by
default. You can control imports using tags:

```perl
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...)
```

# CORE API

These functions are the primary entry points for interacting with foreign libraries.

## `affix( $lib, $symbol, $params, $return )`

Attaches a symbol from a library to a named Perl subroutine in the current namespace.

- **`$lib`**: A library handle returned by `load_library`, a string name, or `undef` to search the currently running process/executable.
- **`$symbol`**: The name of the C function. To install it under a different name in Perl, pass an array reference: `['c_name', 'perl_alias']`. To bind a raw memory address, pass it directly: `[$ptr, 'perl_alias']`.
- **`$params`**: An `ArrayRef` of Affix Type objects representing the function's arguments.
- **`$return`**: A single Affix Type object representing the return value.

```perl
# 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.

## `wrap( $lib, $symbol, $params, $return )`

Creates a wrapper around a given symbol and returns it as an anonymous `CODE` reference. Arguments are identical to
`affix` except you cannot provide an alias.

```perl
my $pow = wrap $lib, 'pow', [ Double, Double ] => Double;
my $result = $pow->( 2, 5 );
```

## `direct_affix( ... )` / `direct_wrap( ... )`

**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.

## `typedef( $name => $type )`

Registers a named type alias. This makes signatures more readable and is required for recursive types and smart Enums.

```perl
# 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: `Point()`.

## `coerce( $type, $value )`

Explicitly hints types for [Variadic Functions](#variadic-functions-varargs).

```
# Hint that we are passing a Float, not a Double
coerce( Float, 1.5 );
```

# VARIABLES & PINNING

Affix allows you to link Perl scalars directly to global or external variables exported by C libraries.

## `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.

```perl
# C: extern int errno;
my $errno;
pin $errno, libc(), 'errno', Int;

$errno = 0;   # Writes directly to C memory
```

## `unpin( $var )`

Removes the magic applied by `pin`. The variable retains its last value but is no longer linked to C memory.

# TYPE SYSTEM

Affix signatures are built using helper functions that map precisely to C types. These are exported by default, or can
be imported explicitly using the `:types` tag.

## Primitive Types

### Void & Booleans

- `Void`: Used for functions that return nothing (`void`).
- `Bool`: Mapped to Perl's true/false values (`stdbool.h` / `_Bool`).

README.md  view on Meta::CPAN

```perl
my $int_arr   = calloc(10, Int);
my $next_elem = ptr_add($int_arr, sizeof(Int));
```

_Note: If `$ptr` is an Array type, `ptr_add` correctly decays the returned pin into a Pointer to the element type._

### `ptr_diff( $ptr1, $ptr2 )`

Returns the byte difference (`$ptr1 - $ptr2`) between two pointers as an integer.

### `is_null( $ptr )`

Returns true if the address is `NULL` (`0x0`).

### `strnlen( $ptr, $max )`

Safe string length calculation. Checks the pointer for a `NULL` terminator, scanning at most `$max` bytes.

# RAW MEMORY OPERATIONS

Affix exposes standard C memory operations for high-performance, raw byte manipulation. These functions accept either
Pins or raw integer addresses.

- `memcpy( $dest, $src, $bytes )`: Copies exactly `$bytes` from `$src` to `$dest`.
- `memmove( $dest, $src, $bytes )`: Copies `$bytes` from `$src` to `$dest`. Safe to use if the memory regions overlap.
- `memset( $ptr, $byte_val, $bytes )`: Fills the first `$bytes` of the memory block with the value `$byte_val`.
- `memcmp( $ptr1, $ptr2, $bytes )`: Compares the first `$bytes` of two memory blocks. Returns an integer less than, equal to, or greater than zero.
- `memchr( $ptr, $byte_val, $bytes )`: Locates the first occurrence of `$byte_val` within the first `$bytes` of the memory block. Returns a new Pin pointing to the match, or `undef`.

# LIBRARIES & SYMBOLS

Loading dynamic libraries across different operating systems (Windows, macOS, Linux, BSD) can be a nightmare of varying
extensions, prefixes, and search paths. Affix abstracts this complexity away with a smart library discovery engine.

## Library Discovery

When you provide a bare library name (e.g., `'z'`, `'ssl'`, `'user32'`) rather than an absolute path, Affix
automatically formats the name for the current platform (e.g., `libz.so`, `libz.dylib`, `z.dll`) and searches the
following locations in order:

- 1. **Standard System Paths:** Windows `System32`/`SysWOW64`; Unix `/usr/local/lib`, `/usr/lib`, `/lib`, `/usr/lib/system`.
- 2. **Environment Variables:** Paths defined in `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`, `DYLD_FALLBACK_LIBRARY_PATH`, or `PATH`.
- 3. **Local Paths:** The current working directory (`.`) and its `lib/` subdirectory.

## Functions

### `load_library( $path_or_name )`

Locates and loads a dynamic library into memory, returning an opaque `Affix::Lib` handle.

```perl
my $lib = load_library('sqlite3');
```

**Lifecycle:** Library handles are thread-safe and internally reference-counted. The underlying OS library is only
closed (e.g., via `dlclose` or `FreeLibrary`) when all Affix wrappers and pins relying on it are destroyed.

_Note:_ When using `affix()` or `wrap()`, you can safely pass the string name directly (e.g., `affix('sqlite3',
...)`) and Affix will call `load_library` for you internally. If you pass `undef` instead of a library name, Affix
will search the currently running executable process.

### `locate_lib( $name, [$version] )`

Searches for a library using Affix's discovery engine and returns its absolute file path as a string. It **does not**
load the library into memory. This is useful if you need to pass the library path to another tool or check for its
existence.

```perl
# Find libssl.so.1.1 or libssl.1.1.dylib
my $path = locate_lib('ssl', '1.1');
say "Found SSL at: $path" if $path;
```

### `find_symbol( $lib_handle, $symbol_name )`

Looks up an exported symbol (function or global variable) inside an already-loaded `Affix::Lib` handle. Returns an
unmanaged `Affix::Pointer` (Pin) of type `Pointer[Void]` pointing to the memory address of the symbol.

```perl
my $lib = load_library('m');

# Get the raw memory address of the 'pow' function
my $pow_ptr = find_symbol($lib, 'pow');

if ($pow_ptr) {
    say sprintf("pow() is located at: 0x%X", address($pow_ptr));
}
```

Returns `undef` if the symbol cannot be found.

### `libc()` and `libm()`

Helper functions that locate and return the file paths to the standard C library and the standard math library for the
current platform. Because platform implementations differ wildly (e.g., MSVCRT on Windows, glibc on Linux, libSystem on
macOS), using these helpers guarantees you get the correct library.

```perl
# Bind 'puts' from the standard C library
affix libc(), 'puts', [String] => Int;

# Bind 'cos' from the math library
affix libm(), 'cos', [Double] => Double;
```

### `get_last_error_message()`

If `load_library`, `find_symbol`, or a signature parsing step fails, this function returns a string describing the
most recent internal or operating system error (via `dlerror` or `FormatMessage`).

```perl
my $lib = load_library('does_not_exist');
if (!$lib) {
    die "Failed to load library: " . get_last_error_message();
}
```

# INTROSPECTION

When working with C APIs, you often need to know exactly how much memory a structure consumes or where a specific field

README.md  view on Meta::CPAN

```perl
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.
```

### `sv_dump( $scalar )`

Dumps Perl's internal interpreter structure (SV) for a given scalar to `STDOUT`. This exposes the raw flags, reference
counts, and memory layout of the Perl variable itself.

```perl
my $val = 42;
sv_dump($val);
# Exposes IV flags, memory addresses of the SV head, etc.
```

## Advanced Debugging

### `set_destruct_level( $level )`

Sets the internal `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);
```

# COMPANION MODULES

Affix ships with two powerful companion modules to streamline your FFI development:

- [**Affix::Wrap**](https://metacpan.org/pod/Affix%3A%3AWrap): Parses C/C++ headers using the Clang AST to automatically generate Affix bindings for entire libraries.
- [**Affix::Build**](https://metacpan.org/pod/Affix%3A%3ABuild): A polyglot builder that compiles inline C, C++, Rust, Zig, Go, and 15+ other languages into dynamic libraries you can bind instantly.

# 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.

## 1. Initialization Phase vs. Execution Phase

Functions that modify Affix's global state are **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:

- `affix( ... )` - Binding new functions.
- `typedef( ... )` - Registering new types.

## 2. Callbacks

When passing a Perl subroutine as a `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.

# RECIPES & EXAMPLES

See [The Affix Cookbook](https://github.com/sanko/Affix.pm/discussions/categories/recipes) for comprehensive guides to
using Affix.

## Linked List Implementation

```perl
# 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
        }
    }
};

# Passing to a function that processes the head
affix $lib, 'sum_list', [ Pointer[Node()] ] => Int;
say sum_list($list);
```

## Interacting with C++ Classes (vtable)

```perl
# Manual call to a vtable entry
# Suppose $obj_ptr is a pointer to a C++ object
my $vtable = cast($obj_ptr, Pointer[ Pointer[Void] ]);
my $func_ptr = $vtable->[0]; # Get first method address

# Bind and call
my $method = wrap undef, $func_ptr, [Pointer[Void], Int] => Void;
$method->($obj_ptr, 42);
```

# SEE ALSO

[FFI::Platypus](https://metacpan.org/pod/FFI%3A%3APlatypus), [C::DynaLib](https://metacpan.org/pod/C%3A%3ADynaLib), [XS::TCC](https://metacpan.org/pod/XS%3A%3ATCC), [C::Blocks](https://metacpan.org/pod/C%3A%3ABlocks)

All the heavy lifting is done by [infix](https://github.com/sanko/infix), my JIT compiler and type introspection
engine.

# AUTHOR

Sanko Robinson <sanko@cpan.org>

# COPYRIGHT

Copyright (C) 2023-2026 by Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.



( run in 0.573 second using v1.01-cache-2.11-cpan-39bf76dae61 )