Affix

 view release on metacpan or  search on metacpan

lib/Affix.pod  view on Meta::CPAN

=pod

=encoding utf-8

=head1 NAME

Affix - A Foreign Function Interface eXtension

=head1 SYNOPSIS

    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 } );
    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);

=head1 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.
The result is near-native call speed with a rich type system covering primitives, structs, unions, enums, SIMD vectors,
and pointers.

Powered by L<infix|https://github.com/sanko/infix/>, which has been tested on Linux, Windows, macOS, Solaris, BSD, and
across C<x86_64> and C<AArch64> (ARM64).

=head1 EXPORTS

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

Control what gets imported:

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

=head1 CORE API

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

lib/Affix.pod  view on Meta::CPAN


=item * C<Int16> / C<SInt16> / C<UInt16>: 16-bit integers (C<int16_t>, C<uint16_t>).

=item * C<Int32> / C<SInt32> / C<UInt32>: 32-bit integers (C<int32_t>, C<uint32_t>).

=item * C<Int64> / C<SInt64> / C<UInt64>: 64-bit integers (C<int64_t>, C<uint64_t>).

=item * C<Int128> / C<SInt128> / C<UInt128>: 128-bit integers. I<Note: Because standard Perl scalars cannot hold 128-bit numbers natively, these must be passed to/from Affix as decimal strings.>

=back

=head3 Floating Point

=over

=item * C<Float16>: Half-precision 16-bit float (IEEE 754).

=item * C<Float> / C<Float32>: Standard 32-bit C<float>.

=item * C<Double> / C<Float64>: Standard 64-bit C<double>.

=item * C<LongDouble>: Platform-specific extended precision (typically 80-bit on x86 or 128-bit).

=back

=head3 Complex Numbers

=over

=item * C<Complex[ $type ]>: C99 complex numbers (e.g., C<Complex[Double]>). In Perl, these map to an C<ArrayRef> of two numbers: C<[ $real, $imaginary ]>.

=back

=head2 String Types

=over

=item * B<C<String>>: Maps to C<const char*>. Affix handles UTF-8 encoding (Perl to C) and decoding (C to Perl) automatically.

=item * B<C<WString>>: Maps to C<const wchar_t*>. Affix automatically handles UTF-16/UTF-32 conversions, including Windows Surrogate Pairs.

=item * B<C<StringList>>: Maps a Perl C<ArrayRef> of strings to a null-terminated C<char**> array (common in C APIs like C<execve> or C<main(argc, argv)>).

=item * B<C<Buffer>>: Maps a mutable C<char*> to the raw memory buffer of a Perl scalar. B<Zero-copy>. The scalar must have pre-allocated capacity (e.g., C<"\0" x 1024>).

=back

=head2 Pointer & Reference Types

=head3 C<Pointer[ $type ]>

A pointer to another type. Affix wraps pointers in magical Perl scalar references that read and write C memory directly
via the C<$$> dereference operator.

=head4 Reading

Dereferencing reads the value from C memory:

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

=head4 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:

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

=head4 Passing Scalars as Pointers

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

    # 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

=head4 Array Indexing

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

    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:

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

=head4 Void Pointers

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

=head3 Specialized Pointers

=over

=item * B<C<File>> / B<C<PerlIO>>: Maps Perl filehandles (Globs or IO objects) to C<FILE*> or C<PerlIO*>. B<Must> be wrapped in a pointer: C<Pointer[File]>.

=item * B<C<SockAddr>>: Specialized marshalling for packed socket strings (e.g., from C<Socket::pack_sockaddr_in>) to C<struct sockaddr*>.

=item * B<C<SV>>: Direct, low-level access to Perl's internal Interpreter Object (C<SV*>). B<Must> be wrapped in a pointer: C<Pointer[SV]>.

=back

=head2 Aggregate Types

=head3 C<Struct[ @members ]>

A C struct, mapped to a Perl C<HashRef>.

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

=head3 C<Union[ @members ]>

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

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

=head3 C<Packed[ $aggregate ]> / C<Packed[ $align, $aggregate ]>

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

    # 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 ] );

=head3 C<Array[ $type, $count ]>

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

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

=head3 Bitfields

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

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

=head2 Live Views (Zero-Copy Aggregates)

In Affix, memory structures are live by design. When C returns a pointer to an aggregate (Struct, Union, or Array), or
when you use C<cast()> to overlay a type onto a memory address, Affix does not copy the data.

Instead, it returns a magical Perl reference (blessed into C<Affix::Pointer>) mapped directly to the C memory via Perl
VTables. This means zero-copy performance without the overhead of C<tie>.

Modifying keys or elements in these structures updates C memory immediately, and reading them reads directly from the C
heap.

    # Example: Live view of a struct
    my $live = cast( $ptr, Struct[ x => Int, y => Int ] );
    $live->{x} = 42; # Updates C memory

=head3 Unified Access

Magical C<Affix::Pointer> references allow direct field access (C<< $p->{field} >>) without explicit casting.

    affix $lib, 'get_ptr', [] => Pointer[Point];
    my $p = get_ptr();
    say $p->{x};  # Unified access! Reads directly from C memory.
    $p->{y} = 50; # Writes directly to C memory.

=head2 Callbacks & Functions

=over

=item * B<C<< Callback[ [$params] => $ret ] >>>: Defines the signature of a C function pointer. Allows you to pass Perl subroutines into C functions.

    # C: void set_handler( void (*cb)(int) );
    affix $lib, 'set_handler', [ Callback[ [Int] => Void ] ] => Void;

=item * B<C<ThisCall( $cb_or_sig )>>: Helper for C++-style C<__thiscall> callbacks. Prepends a C<Pointer[Void]> (the C<this> pointer) to the signature.

=back

=head2 Variadic Functions (VarArgs)

Affix supports C functions that take a variable number of arguments (e.g., C<printf>, C<ioctl>). When defining a
signature, use the C<VarArgs> token at the end of the argument list.

=head3 Basic Usage

    # C: int printf(const char* format, ...);
    affix libc(), ['printf' => 'my_printf'], [ String, VarArgs ] => Int;

    # Basic types are marshalled automatically based on Perl's internal state
    my_printf("Integer: %d, String: %s\n", 42, "Hello");

=head3 Explicit Type Control with C<coerce()>

In variadic functions, C relies on the caller to pass data in the exact format the function expects. While Affix
attempts to guess the correct C type for Perl scalars, these guesses might not always match the library's expectations
like passing a 64-bit integer where a 32-bit one is expected, or a float instead of a double.

Use C<coerce( $type, $value )> to explicitly tell Affix how to marshal a variadic argument.

    # Suppose we have a variadic log function that expects specific bit-widths
    # C: void custom_log(int level, ...);
    affix $lib, 'custom_log', [ Int, VarArgs ] => Void;

    custom_log(
        1,
        coerce(Short, 10),    # Explicitly pass as a 16-bit signed int
        coerce(Float, 1.5),    # Explicitly pass as a 32-bit float
        coerce(ULong, 1000)    # Explicitly pass as a platform-native unsigned long
    );

Note: Standard C default argument promotions still apply. For example, passing a C<Float> to a variadic function will
typically be promoted to a C<Double> by the C runtime unless the receiving function specifically handles raw floats.

=head2 Enumerations

    # C: enum Status { OK = 0, ERROR = 1, FLAG_A = 1<<0, FLAG_B = 1<<1 };
    typedef Status => Enum[
        [ OK => 0 ],
        'ERROR',                    # Auto-increments to 1
        [ FLAG_A => 1 << 0 ],       # Bit shifting
        [ FLAG_B => '1 << 1' ]      # String expression
    ];

lib/Affix.pod  view on Meta::CPAN


=head2 Managed vs. Unmanaged Memory

Memory in Affix is handled by life lines.

=over

=item * B<Affix::Memory:> Created via C<malloc()> or C<calloc()>. These are root objects. When the Perl variable is destroyed, C<safefree()> is called automatically.

=item * B<Pins:> Created via C<cast()> or pointer dereferencing. These variables do not own the memory, but they hold a reference to a life line to prevent the parent memory from being freed prematurely.

=back

=head2 Allocation & Deallocation

These functions allocate memory on the C heap. Memory allocated via these functions is B<managed by Perl> by default.

=head3 C<malloc( $size )>

Allocates C<$size> bytes of uninitialized memory. Returns a C<Pointer[Void]> pin.

    my $ptr = malloc(1024); # Allocates 1KB

=head3 C<calloc( $count, $size )>

Allocates zero-initialized memory for C<$count> elements of C<$size>. Returns a C<Pointer[Void]> pin.

    my $ptr = calloc( 10, sizeof(Int) );
    my $arr = cast( $ptr, Array[Int, 10] );

=head3 C<realloc( $ptr, $new_size )>

Resizes the memory area pointed to by C<$ptr> to C<$new_size> bytes. The original pin is updated automatically
in-place.

    $ptr = realloc( $ptr, 2048 );

=head3 C<strdup( $string )>

Allocates managed memory and copies the Perl string (along with a C<NULL> terminator) into it. Returns a managed
C<Pointer[Char]> pin.

    my $str_ptr = strdup("Hello C!");

=head3 C<free( $ptr )>

Manually releases memory.

B<Warning:> Only use this on memory that you exclusively own (e.g., allocated via C<malloc>). Do not call C<free> on
unmanaged pointers returned by C libraries unless the library explicitly transfers ownership to you, or you will cause
a segmentation fault.

    free($ptr);

=head3 C<own( $pin )>

Returns true if the given pin is an owned C<Affix::Memory> object (i.e., memory allocated via C<malloc> or C<calloc>
that Perl manages directly). Returns false for unmanaged pins or raw scalars.

    if (own($ptr)) {
        say "Perl owns this memory; it will be freed automatically.";
    }

=head3 C<is_pin( $var )>

Returns true if the variable is currently bound to C memory via C<pin>, C<cast>, or pointer dereferencing. Returns
false for ordinary Perl scalars.

    my $x = 42;
    say is_pin($x); # false

    pin $x, libc(), 'errno', Int;
    say is_pin($x); # true

=head2 Lifecycle & Ownership

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

lib/Affix.pod  view on Meta::CPAN

=head3 Native Array Indexing

C Arrays are traversed using standard Perl array syntax.

    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!

=head3 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 (C<Can't use an undefined
value as a HASH reference>).

=head1 LIBRARIES & SYMBOLS

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

=head2 Library Discovery

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

=over

=item 1. B<Standard System Paths:> Windows C<System32>/C<SysWOW64>; Unix C</usr/local/lib>, C</usr/lib>, C</lib>, C</usr/lib/system>.

=item 2. B<Environment Variables:> Paths defined in C<LD_LIBRARY_PATH>, C<DYLD_LIBRARY_PATH>, C<DYLD_FALLBACK_LIBRARY_PATH>, or C<PATH>.

=item 3. B<Local Paths:> The current working directory (C<.>) and its C<lib/> subdirectory.

=back

=head2 Functions

=head3 C<load_library( $path_or_name )>

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

    my $lib = load_library('sqlite3');

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

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

=head3 C<locate_lib( $name, [$version] )>

Searches for a library using Affix's discovery engine and returns its absolute file path as a string. It B<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.

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

=head3 C<find_symbol( $lib_handle, $symbol_name )>

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

    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 C<undef> if the symbol cannot be found.

=head3 C<libc()> and C<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.

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

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

=head3 C<get_last_error_message()>

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

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

=head1 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.

=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

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

=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

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

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

=head2 Interacting with C++ Classes (vtable)

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

=head1 SEE ALSO

L<FFI::Platypus>, L<C::DynaLib>, L<XS::TCC>, L<C::Blocks>

All the heavy lifting is done by L<infix|https://github.com/sanko/infix>, my JIT compiler and type introspection
engine.

=head1 AUTHOR

Sanko Robinson - L<https://github.com/sanko>

=head1 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.

=cut



( run in 3.094 seconds using v1.01-cache-2.11-cpan-2c0d6866c4f )