Affix
view release on metacpan or search on metacpan
lib/Affix.pod view on Meta::CPAN
# C: union { int key_code; float pressure; };
typedef Event => Union[ key_code => Int, pressure => Float ];
=head3 C<Packed[ $align, $aggregate ]>
Forces specific byte alignment on a Struct or Union (e.g., C<#pragma pack(1)>).
# C: #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)
Standard structs and arrays copy data between Perl and C. Live views allow you to directly manipulate C memory through
Perl references.
C<Live[ $type ]> Returns a live, zero-copy view of the memory as defined by C<$type>.
If C<$type> is a C<Struct> or C<Union>, it returns an C<Affix::Live> tied hash. Modifying keys in this hash updates C
memory immediately.
If C<$type> is an C<Array>, it returns an C<Affix::Pointer> object. Modifying elements (e.g. C<< $arr->[0] = 5 >>)
writes directly to memory.
# Example: Live view of a struct
my $live = cast( $ptr, Live[ Struct[ x => Int, y => Int ] ] );
$live->{x} = 42; # Updates C memory
=head3 Unified Access
C<Affix::Pointer> objects for aggregates 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
];
=over
=item * B<Constants:> C<typedef> installs constants (e.g., C<OK() == 0>) into your package.
=item * B<Dualvars:> Values returned from C act as dualvars. They print as strings (C<"OK">) but evaluate mathematically as integers (C<0>).
=item * B<String Marshalling:> You can pass the string name of an element (C<"OK">) directly to functions that expect
that enum type.
=item * B<Aliases:> You can also use C<IntEnum[ ... ]>, C<CharEnum[ ... ]>, and C<UIntEnum[ ... ]> to force the underlying integer size.
=back
lib/Affix.pod view on Meta::CPAN
# 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
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>
( run in 0.648 second using v1.01-cache-2.11-cpan-39bf76dae61 )