Affix
view release on metacpan or search on metacpan
lib/Affix.pod view on Meta::CPAN
=head3 C<attach_destructor( $pin, $func_ptr, [$lib] )>
Attaches a custom C function to be called when the Pin is destroyed. This is incredibly useful for C libraries that
require specific cleanup routines (e.g., C<SDL_DestroyWindow>, C<sqlite3_free>).
# Find the address of the library's custom free function
my $free_func = find_symbol($my_lib, 'custom_free');
# When $ptr goes out of scope, Affix will call custom_free($ptr)
attach_destructor($ptr, $free_func, $my_lib);
=head3 C<readonly( $pin, [$bool] )>
Gets or sets the "const" status of a memory pin. If set to true, any attempt to write to that memory from Perl will
throw an exception.
readonly($point, 1);
$point->{x} = 20; # CRASH: Modification of a read-only C value
=head2 Type Casting
=head3 C<cast( $ptr, $type )>
The most powerful tool in the memory kit. It "overlays" a C type definition onto a raw memory address.
my $mem = malloc(16);
my $point = cast($mem, Struct[ x => Int, y => Int ]);
$point->{x} = 10;
=head1 POINTER UTILITIES
Navigate and inspect raw pointers with helper functions for address arithmetic, null checks, and more.
=head3 C<address( $ptr )>
Returns the virtual memory address of the pointer as a Perl Unsigned Integer (C<UInt64>). Useful for passing addresses
to other FFI libraries or debugging.
say sprintf("Address: 0x%X", address($ptr));
=head3 C<ptr_add( $ptr, $offset_bytes )>
Returns a new B<unmanaged alias Pin> offset by C<$offset_bytes>.
my $int_arr = calloc(10, Int);
my $next_elem = ptr_add($int_arr, sizeof(Int));
I<Note: If C<$ptr> is an Array type, C<ptr_add> correctly decays the returned pin into a Pointer to the element type.>
=head3 C<ptr_diff( $ptr1, $ptr2 )>
Returns the byte difference (C<$ptr1 - $ptr2>) between two pointers as an integer.
=head3 C<is_null( $ptr )>
Returns true if the address is C<NULL> (C<0x0>).
=head3 C<strnlen( $ptr, $max )>
Safe string length calculation. Checks the pointer for a C<NULL> terminator, scanning at most C<$max> bytes.
=head3 C<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 C<dump()>.
=head3 C<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.
=head1 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.
=over
=item * C<memcpy( $dest, $src, $bytes )>: Copies exactly C<$bytes> from C<$src> to C<$dest>.
=item * C<memmove( $dest, $src, $bytes )>: Copies C<$bytes> from C<$src> to C<$dest>. Safe to use if the memory regions overlap.
=item * C<memset( $ptr, $byte_val, $bytes )>: Fills the first C<$bytes> of the memory block with the value C<$byte_val>.
=item * C<memcmp( $ptr1, $ptr2, $bytes )>: Compares the first C<$bytes> of two memory blocks. Returns an integer less than, equal to, or greater than zero.
=item * C<memchr( $ptr, $byte_val, $bytes )>: Locates the first occurrence of C<$byte_val> within the first C<$bytes> of the memory block. Returns a new Pin pointing to the match, or C<undef>.
=back
=head1 C<Const> & Readonly Memory
Enforce C's const contract at the Perl level. Affix intercepts writes to read-only memory and throws a fatal exception:
C<Modification of a read-only C value attempted>.
=head2 Declarative Const: C<Const[ $type ]>
You can wrap any type in C<Const[ ... ]> within a signature.
# C: void process(const char* name, const int* values);
affix $lib, 'process', [ Const[String], Pointer[ Const[Int] ] ] => Void;
=head2 Imperative Const: C<readonly( $pin, [$bool] )>
The C<readonly()> function allows you to inspect or toggle the const status of a Pin or Aggregate at runtime. This acts
as an I<FFI Escape Hatch> (similar to C<const_cast> in C++).
my $point = cast($addr, Struct[ x => Int, y => Int ]);
readonly($point, 1); # Lock the entire struct
$point->{x} = 10; # FATAL ERROR
=head2 Recursive Protection
When an aggregate (Struct or Array) is marked as read-only, Affix automatically propagates that protection to all of
its members.
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.
lib/Affix.pod view on Meta::CPAN
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
Guidelines for calling into C++, Rust, Fortran, Go, and Assembly from Affix. Because Affix dynamically loads symbols
according to the 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
Diagnose FFI issues with built-in error reporting, memory inspection, and hex dumps. Bridging two entirely different
runtimes can lead to spectacular crashes if types or memory boundaries are mismatched.
=head2 Error Handling
=head3 C<errno()>
Accesses the system error code from the most recent FFI or standard library call (reads C<errno> on Unix and
C<GetLastError> on Windows).
This function returns a B<dualvar>. It behaves as an integer in numeric context, and magically resolves to the
human-readable system error message (via C<strerror> or C<FormatMessage>) in string context.
# 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
Auto-generate bindings from C/C++ headers and compile polyglot source with two companion modules:
=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
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.
=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
( run in 1.754 second using v1.01-cache-2.11-cpan-364913b4093 )