Affix
view release on metacpan or search on metacpan
lib/Affix.pod view on Meta::CPAN
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 managed C<Pointer[Void]> pin.
my $ptr = malloc(1024); # Allocates 1KB
# $ptr is automatically freed when it goes out of scope
=head3 C<calloc( $count, $type )>
Allocates memory for an array of C<$count> elements of the given C<$type>, and zero-initializes the memory. Returns a
managed pin typed as an Array.
my $arr = calloc( 10, Int );
$arr->[0] = 42; # Write directly to the first element
=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);
=head2 Lifecycle & Ownership
=head3 C<own( $ptr, [$bool] )>
Gets or sets the lifecycle management status of a pointer.
=over
=item * C<own($ptr, 1)>: Perl takes ownership. C<free()> will be called automatically when C<$ptr> is garbage collected.
=item * C<own($ptr, 0)>: Perl releases ownership. You (or the C library) are now responsible for freeing the memory.
=back
# Take ownership of a pointer returned from C
my $c_string = get_string_from_c();
own($c_string, 1);
=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);
=head2 Type Casting
=head3 C<cast( $ptr, $type )>
Reinterprets a memory address as a new type. The behavior depends on the requested C<$type>:
=over
=item * B<Casting to a Value (Primitives/Strings):> Reads the memory immediately and returns a Perl scalar copy.
=item * B<Casting to a Reference (Pointers/Live Views):> Returns a B<new unmanaged Pin> that aliases the same memory address, allowing you to interact with it using the new type's rules.
=back
my $void_ptr = malloc(4);
# 1. Alias the memory as an Integer pointer
my $int_ptr = cast($void_ptr, Pointer[Int]);
$int_ptr->[0] = 99;
# 2. Read the memory immediately as an integer value
my $val = cast($void_ptr, Int); # Returns 99
=head1 POINTER UTILITIES
=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.
=head1 RAW MEMORY OPERATIONS
Affix exposes standard C memory operations for high-performance, raw byte manipulation. These functions accept either
Pins or raw integer addresses.
=over
lib/Affix.pod view on Meta::CPAN
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
Affix ships with two powerful companion modules to streamline your FFI development:
=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
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
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 E<lt>sanko@cpan.orgE<gt>
=head1 COPYRIGHT
( run in 0.326 second using v1.01-cache-2.11-cpan-e1769b4cff6 )