Affix

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

# 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 } );
affix $lib, 'get_pos', [] => Point();
my $pt = get_pos();
say sprintf 'x: %d, y: %d', $pt->{x}, $pt->{y};

# 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

Call native code from Perl without XS, compilers, or runtime overhead.

Affix is a high-performance Foreign Function Interface (FFI) for Perl. It bridges Perl to C, Rust, Zig, C++, Go,
Fortran, and more via JIT-compiled trampolines that handle argument marshalling at runtime. No generic dispatch loops
here. The result is near-native call speed with a rich type system covering primitives, structs, unions, enums, SIMD
vectors, and pointers.

Powered by [infix](https://github.com/sanko/infix/), which has been tested on Linux, Windows, macOS, Solaris, BSD, and
across x86\_64, ARM64, and RISC-V.

# EXPORTS

Import types and functions with built-in tags. By default, Affix exports standard types (`Int`, `Double`, etc.) and
core functions (`affix`, `wrap`, `load_library`).

Control what gets imported:

```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, raw, snapshot, pin, unpin...
use Affix qw[:types];  # Types only (Int, Struct, Pointer...)
```

# CORE API

Bind functions to Perl subroutines and define custom types. These 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 );
```

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

Bind Perl scalars directly to C global variables for real-time, two-way access to C memory.

## `pin( ... )`

Binds a scalar to a C variable. Reading the scalar reads C memory; writing to it updates C memory immediately. Three
calling conventions are supported:

- **pin( $var, $lib, $symbol, $type )**

    Binds to an exported symbol. This is the most common form.

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

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

README.md  view on Meta::CPAN

```perl
affix $lib, 'get_ptr', [] => Pointer[Int];
my $ptr = get_ptr();
say $$ptr;  # Reads the int value from C memory
```

#### Writing

Assigning through the dereference writes directly to C memory:

```
$$ptr = 42;  # Writes 42 to the C memory address
```

This works for deep pointer chains as well:

```perl
# int*** ptr; ***ptr = 5;
my $ppp = cast( $mem, Pointer[ Pointer[ Pointer[Int] ] ] );
$$ppp = $pp_val;  # Writes the pointer address through the chain
```

#### Passing Scalars as Pointers

When a function expects a `Pointer[$type]` argument, pass a **scalar reference** (`\$var`) to send the address of a
Perl scalar. Affix automatically handles the marshalling:

```perl
# C: int deref_and_add(int* p);
affix $lib, 'deref_and_add', [ Pointer[Int] ] => Int;

my $val = 50;
is deref_and_add( \$val ), 60;  # Passes address of $val as int*

# C: void modify_int_ptr(int* p, int new_val);
affix $lib, 'modify_int_ptr', [ Pointer[Int], Int ] => Void;
modify_int_ptr( \$val, 999 );
say $val;  # 1000; C function wrote through the pointer
```

#### Array Indexing

Pointers to arrays support direct element access via array subscript syntax. Reads and writes go directly to C memory:

```perl
affix $lib, 'get_array_ptr', [] => Pointer[ Array[ Int, 4 ] ];
my $arr = get_array_ptr();
say $arr->[0];   # Read first element from C memory
$arr->[2] = 99;  # Write third element in C memory
```

To take a deep copy (snapshot), dereference into an anonymous array ref:

```perl
my $snapshot = [@$arr];
$snapshot->[0] = 100;  # Modifying snapshot does NOT affect C memory
```

#### Void Pointers

If `$type` is `Void`, the pointer is "terminal." Dereferencing it will return `undef`. In this case, use `cast()`
or `address()` to work with the raw memory address.

### Specialized Pointers

- **`File`** / **`PerlIO`**: Maps Perl filehandles (Globs or IO objects) to `FILE*` or `PerlIO*`. **Must** be wrapped in a pointer: `Pointer[File]`.
- **`SockAddr`**: Specialized marshalling for packed socket strings (e.g., from `Socket::pack_sockaddr_in`) to `struct sockaddr*`.
- **`SV`**: Direct, low-level access to Perl's internal Interpreter Object (`SV*`). **Must** be wrapped in a pointer: `Pointer[SV]`.

## Aggregate Types

### `Struct[ @members ]`

A C struct, mapped to a Perl `HashRef`.

```perl
# C: typedef struct { int x; int y; } Point;
typedef Point => Struct[ x => Int, y => Int ];
```

### `Union[ @members ]`

A C union, mapped to a Perl `HashRef` with exactly one key.

```perl
# C: union { int key_code; float pressure; };
typedef Event => Union[ key_code => Int, pressure => Float ];
```

### `Packed[ $aggregate ]` / `Packed[ $align, $aggregate ]`

Forces specific byte alignment on a Struct or Union (e.g., `#pragma pack(1)`).

```perl
# Without explicit alignment (default):
Packed[ Struct[ flag => Char, data => Int ] ];

# With explicit alignment (e.g., #pragma pack(push, 1)):
Packed( 1, Struct[ flag => Char, data => Int ] );
```

### `Array[ $type, $count ]`

A fixed-size C array. Maps to a Perl `ArrayRef`.

```perl
# C: double Vector3[3];
typedef Vector3 => Array[ Double, 3 ];
```

### Bitfields

Specify bit widths using the pipe (`|`) operator within Structs/Unions. Affix handles all masking and shifting.

```perl
# C: typedef struct { uint32_t a : 1; uint32_t b : 3; } Config;
typedef Config => Struct[ a => UInt32 | 1, b => UInt32 | 3 ];
```

## Live Views (Zero-Copy Aggregates)

README.md  view on Meta::CPAN

my $point = cast($mem, Struct[ x => Int, y => Int ]);
$point->{x} = 10;
```

# POINTER UTILITIES

Navigate and inspect raw pointers with helper functions for address arithmetic, null checks, and more.

### `address( $ptr )`

Returns the virtual memory address of the pointer as a Perl Unsigned Integer (`UInt64`). Useful for passing addresses
to other FFI libraries or debugging.

```
say sprintf("Address: 0x%X", address($ptr));
```

### `ptr_add( $ptr, $offset_bytes )`

Returns a new **unmanaged alias Pin** offset by `$offset_bytes`.

```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( $ptr, $length_in_bytes )`

Returns a Perl string containing the raw, un-decoded binary data extracted directly from the memory address. This is
the programmatic, binary equivalent of `dump()`.

### `snapshot( $pin )`

Deeply reads the C memory backing a Pin and returns a pure, non-magical native Perl data structure (ArrayRef, HashRef,
or Scalar). Because it does not apply VTable magic to the returned values, reading elements from the returned structure
in a bulk operation (like summing a 10,000 element array) is exceptionally fast.

# Raw Memory Operations

Classic C memory functions (memcpy, memset, etc.) available directly from Perl for high-performance 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`.

# `Const` & Readonly Memory

Enforce C's const contract at the Perl level. Affix intercepts writes to read-only memory and throws a fatal exception:
`Modification of a read-only C value attempted`.

## Declarative Const: `Const[ $type ]`

You can wrap any type in `Const[ ... ]` within a signature.

```perl
# C: void process(const char* name, const int* values);
affix $lib, 'process', [ Const[String], Pointer[ Const[Int] ] ] => Void;
```

## Imperative Const: `readonly( $pin, [$bool] )`

The `readonly()` function allows you to inspect or toggle the const status of a Pin or Aggregate at runtime. This acts
as an _FFI Escape Hatch_ (similar to `const_cast` in C++).

```perl
my $point = cast($addr, Struct[ x => Int, y => Int ]);

readonly($point, 1); # Lock the entire struct
$point->{x} = 10;    # FATAL ERROR
```

## Recursive Protection

When an aggregate (Struct or Array) is marked as read-only, Affix automatically propagates that protection to all of
its members.

```perl
my $rect = cast($addr, Const[Struct[top => Struct[ x => Int, y => Int ], bottom => Struct[ x => Int, y => Int ] ]]);

# Even though 'x' wasn't explicitly marked Const, it inherited protection
# from the parent struct.
$rect->{top}{x} = 5; # FATAL ERROR
```

## Casting with Const

When using `cast( ... )`, you can prepend a `+` to the type signature to create an immutable view of a raw memory
address.

```perl
my $view = cast($raw_addr, Const[MyStruct]);
# $view is now a read-only HashRef mapping to C memory.
```

# Zero-copy Aggregates

Structs, unions, and arrays map directly to C memory—no deep copies required. When C returns a pointer to an
aggregate, Affix wraps it in a magical Perl reference that reads and writes C memory in real time.

### Native Array Indexing

C Arrays are traversed using standard Perl array syntax.

```perl
typedef Task => Struct[ id => Int, name => String ];
affix $lib, 'get_tasks', [] => Pointer[ Array[ Task(), 10 ] ];

my $tasks = get_tasks();
$tasks->[5]{id} = 404; # Writes directly to C memory!
```

### Deep Null Safety

Traversing a \`NULL\` pointer in C causes a segfault. Affix wraps C memory in Perl safety rails. If you try to traverse a
\`NULL\` pointer inside a struct, Affix intercepts it and throws a standard Perl exception (`Can't use an undefined
value as a HASH reference`).

# LIBRARIES & SYMBOLS

Load and inspect dynamic libraries across platforms. Affix's smart discovery engine handles varying extensions,
prefixes, and search paths automatically.

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

Query type sizes, alignments, and field offsets like a compiler would. When working with C APIs, you often need to know
exactly how much memory a structure consumes or where a specific field is located within a block of memory.

### `sizeof( $type )`

Returns the size, in bytes, of any Affix Type object or registered `typedef` name.

```
# C: sizeof(int);
say sizeof( Int ); # 4 (usually)

# C: sizeof(Point);
say sizeof( Point() ); # 8
```

### `alignof( $type )`

Returns the alignment boundary (in bytes) required by the C ABI for the given type.

```perl
say alignof( Int64 ); # 8 (usually)

# Struct alignment is dictated by its largest member
typedef Mixed => Struct[ a => Char, b => Double ];
say alignof( Mixed() ); # 8
```

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

README.md  view on Meta::CPAN

# COMPANION MODULES

Auto-generate bindings from C/C++ headers and compile polyglot source with two companion modules:

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

Understand the threading model: what's safe to do from callbacks, and what must happen in the main thread before
spawning any threads. 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

Real-world patterns including linked lists and C++ vtable calls. 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.

The Affix Cookbook: [https://github.com/sanko/Affix.pm/discussions/54](https://github.com/sanko/Affix.pm/discussions/54)

# AUTHOR

Sanko Robinson - [https://github.com/sanko](https://github.com/sanko)

# COPYRIGHT

Copyright (C) 2022-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 1.028 second using v1.01-cache-2.11-cpan-d80b1682f3f )