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
here. 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 x86_64, ARM64, and RISC-V.

=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

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

=head2 C<wrap( $lib, $symbol, $params, $return )>

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

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

=head2 C<< typedef( $name => $type ) >>

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

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

lib/Affix.pod  view on Meta::CPAN

    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.
    $rect->{top}{x} = 5; # FATAL ERROR

=head2 Casting with Const

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

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

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

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



( run in 0.585 second using v1.01-cache-2.11-cpan-4e7a2411597 )