Affix
view release on metacpan or search on metacpan
# SYNOPSIS
```perl
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 } );
# We can also allocate and manage raw memory and write data to it
my $ptr = Affix::malloc(1024);
### `Pointer[ $type ]`
A pointer to another type. When used as an argument, you can pass a **reference to a scalar** for automatic temporary
allocation and write-back.
```perl
# C: void get_val(int *val);
affix $lib, 'get_val', [ Pointer[Int] ] => Void;
my $val = 0;
get_val(\$val);
say $val; # Updated by C
```
### Specialized Pointers
- **`File`** / **`PerlIO`**: Maps Perl filehandles (Globs or IO objects) to `FILE*` or `PerlIO*`. **Must** be wrapped in a pointer: `Pointer[File]`.
- **`SockAddr`**: Specialized marshalling for packed socket strings (e.g., from `Socket::pack_sockaddr_in`) to `struct sockaddr*`.
- **`SV`**: Direct, low-level access to Perl's internal Interpreter Object (`SV*`). **Must** be wrapped in a pointer: `Pointer[SV]`.
## Aggregate Types
$live->{x} = 42; # Updates C memory
```
### Unified Access
`Affix::Pointer` objects for aggregates allow direct field access (`$p->{field}`) without explicit casting.
```perl
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.
```
## Callbacks & Functions
- **`Callback[ [$params] => $ret ]`**: Defines the signature of a C function pointer. Allows you to pass Perl subroutines into C functions.
```perl
# C: void set_handler( void (*cb)(int) );
affix $lib, 'set_handler', [ Callback[ [Int] => Void ] ] => Void;
```
# POINTER UTILITIES
### `address( $ptr )`
Returns the virtual memory address of the pointer as a Perl Unsigned Integer (`UInt64`). Useful for passing addresses
to other FFI libraries or debugging.
```
say sprintf("Address: 0x%X", address($ptr));
```
### `ptr_add( $ptr, $offset_bytes )`
Returns a new **unmanaged alias Pin** offset by `$offset_bytes`.
```perl
my $int_arr = calloc(10, Int);
my $next_elem = ptr_add($int_arr, sizeof(Int));
```
### `locate_lib( $name, [$version] )`
Searches for a library using Affix's discovery engine and returns its absolute file path as a string. It **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.
```perl
# Find libssl.so.1.1 or libssl.1.1.dylib
my $path = locate_lib('ssl', '1.1');
say "Found SSL at: $path" if $path;
```
### `find_symbol( $lib_handle, $symbol_name )`
Looks up an exported symbol (function or global variable) inside an already-loaded `Affix::Lib` handle. Returns an
unmanaged `Affix::Pointer` (Pin) of type `Pointer[Void]` pointing to the memory address of the symbol.
```perl
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 `undef` if the symbol cannot be found.
### `libc()` and `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.
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. Affix provides compiler-grade type introspection.
### `sizeof( $type )`
Returns the size, in bytes, of any Affix Type object or registered `typedef` name.
```
# C: sizeof(int);
say sizeof( Int ); # 4 (usually)
# C: sizeof(Point);
say sizeof( Point() ); # 8
```
### `alignof( $type )`
Returns the alignment boundary (in bytes) required by the C ABI for the given type.
```perl
say alignof( Int64 ); # 8 (usually)
# Struct alignment is dictated by its largest member
typedef Mixed => Struct[ a => Char, b => Double ];
say alignof( Mixed() ); # 8
```
### `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.
```perl
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)
```
### `types()`
Returns a list of all custom type names currently registered in Affix's global type registry via `typedef`. In scalar
context, returns the total number of registered types.
```perl
my @known_types = types();
say "Registered types: " . join(', ', @known_types);
```
# INTERFACING WITH OTHER LANGUAGES
Because Affix dynamically loads symbols according to the C Application Binary Interface (C ABI), it can interact with
libraries written in almost any language, provided they expose their functions correctly. Companion modules like
[Affix::Build](https://metacpan.org/pod/Affix%3A%3ABuild) make compiling these languages seamless.
Here are the requirements and quirks for interfacing with non-C languages.
This function returns a **dualvar**. It behaves as an integer in numeric context, and magically resolves to the
human-readable system error message (via `strerror` or `FormatMessage`) in string context.
```perl
# 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.";
}
}
```
**Note:** You must call `errno()` immediately after the C function invokes, as subsequent Perl operations (like
printing to STDOUT) might overwrite the system's error register.
## Memory Inspection
### `dump( $pin, $length_in_bytes )`
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);
```
## Interacting with C++ Classes (vtable)
```perl
# 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
builder/Affix/Builder.pm view on Meta::CPAN
}
method Build(@args) {
my $method = $self->can( 'step_' . $action );
$method // die "No such action '$action'\n";
exit $method->($self);
}
method Build_PL() {
die "Pure perl Affix? Ha! You wish.\n" if $pureperl;
say sprintf 'Creating new Build script for %s %s', $meta->name, $meta->version;
$self->write_file( 'Build', sprintf <<'', $^X, __PACKAGE__, __PACKAGE__ );
#!%s
use lib 'builder';
use %s;
%s->new( @ARGV && $ARGV[0] =~ /\A\w+\z/ ? ( action => shift @ARGV ) : (),
map { /^--/ ? ( shift(@ARGV) =~ s[^--][]r => 1 ) : /^-/ ? ( shift(@ARGV) =~ s[^-][]r => shift @ARGV ) : () } @ARGV )->Build();
make_executable('Build');
my @env = defined $ENV{PERL_MB_OPT} ? split_like_shell( $ENV{PERL_MB_OPT} ) : ();
$self->write_file( '_build_params', encode_json( [ \@env, \@ARGV ] ) );
lib/Affix.pod view on Meta::CPAN
=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 } );
# We can also allocate and manage raw memory and write data to it
my $ptr = Affix::malloc(1024);
lib/Affix.pod view on Meta::CPAN
=head3 C<Pointer[ $type ]>
A pointer to another type. When used as an argument, you can pass a B<reference to a scalar> for automatic temporary
allocation and write-back.
# C: void get_val(int *val);
affix $lib, 'get_val', [ Pointer[Int] ] => Void;
my $val = 0;
get_val(\$val);
say $val; # Updated by C
=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]>.
lib/Affix.pod view on Meta::CPAN
# 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;
lib/Affix.pod view on Meta::CPAN
# 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.>
lib/Affix.pod view on Meta::CPAN
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.
lib/Affix.pod view on Meta::CPAN
=head1 INTROSPECTION
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. Affix provides compiler-grade type introspection.
=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
Because Affix dynamically loads symbols according to the C Application Binary Interface (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++
lib/Affix.pod view on Meta::CPAN
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 )>
lib/Affix.pod view on Meta::CPAN
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;
( run in 0.902 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )