Affix
view release on metacpan or search on metacpan
```
## 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();
}
if (int($err) == 2) {
say "Code 2 specifically triggered.";
}
}
```
**Note:** You must call `errno()` immediately after the C function invokes, as subsequent Perl operations (like
printing to STDOUT) might overwrite the system's error register.
## Memory Inspection
### `dump( $pin, $length_in_bytes )`
Prints a formatted hex dump of the memory pointed to by a Pin directly to `STDOUT`. This is an invaluable tool for
verifying that C structs or buffers contain the data you expect.
```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
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)
( run in 1.553 second using v1.01-cache-2.11-cpan-d01c6094234 )