view release on metacpan or search on metacpan
- `size()`: Returns the total allocated size for managed pointers.
- `cast($type)`: Reinterprets the pointer.
- `attach_destructor($destructor, [$lib])`: Attaches a custom C cleanup routine to the pointer.
- Added `attach_destructor( $pin, $destructor, [$lib] )` to allow attaching custom C cleanup routines to managed pointers.
- Improved `VarArgs` support to automatically promote Perl strings to `char*`.
- Experimental Zero-Copy 'Live' Aggregates:
- `LiveStruct`: A new helper to return zero-copy, live views of C structs. Modifications to the returned blessed hash reflect immediately in C memory.
- `LiveArray`: A new helper to return live `Affix::Pointer` objects instead of deeply copied array references.
- Implemented the `TIEHASH` interface for `Affix::Live` so perl can treat them as standard Perl hashes (`keys %$live`, `each %$live`, etc.).
- Fully implemented marshalling for `Int128` and `UInt128` (sint128/uint128) primitive types.
- Added `Affix::Wrap->generate( $lib, $pkg, $file )` for static binding generation. This emits standalone Perl modules that depend only on `Affix`, eliminating the need for `Clang` or header files at runtime.
- Recursive macro resolution support in `Affix::Wrap` for bitwise OR expressions like `(FLAG_A | FLAG_B)`.
- Support for passing string names of enum constants directly to functions.
- Added `params()` method to `Affix::Type::Callback` to allow inspecting and modifying callback parameters.
- Added string-to-integer conversion when passing Perl strings to C functions expecting enums.
### Fixed
- Optimized `Pointer` returns in the XSUB dispatcher for performance by inlining the marshalling path and caching the stash.
- Fixed several issues in `CLONE` where metadata, managed memory, and enum registries were not correctly duplicated across perl's ithreads.
- Improved `_get_pin_from_sv` and `is_pin` to safely handle both references to pins and direct magical scalars like those found in Unions.
- SIMD Vector Improvements:
- Added M512, M512d, and M512i type helpers.
- Ensured compatibility with infix's refined vector alignment and passing rules.
- [infix] Added support for half-precision floating-point (`float16`).
- [infix] Implemented C++ exception propagation through JIT frames on Linux (x86-64 and ARM64) using manual DWARF `.eh_frame` generation and `__register_frame`.
- [infix] Implemented Structured Exception Handling (SEH) for Windows x64 and ARM64 for C++ exception propagation through trampolines.
- [infix] Added `infix_forward_create_safe` API to establish an exception boundary that catches native exceptions and returns a dedicated error code (`INFIX_CODE_NATIVE_EXCEPTION`).
- [infix] Added support for 256-bit (AVX) and 512-bit (AVX-512) vectors in the System V ABI.
- [infix] Added support for receiving bitfield structs in reverse call trampolines.
- [infix] Added trampoline caching. Identical signatures and targets now share the same JIT-compiled code and metadata via internal reference counting, significantly reducing memory overhead and initialization time.
- [infix] Added a new opt-in build mode (`--sanity`) that emits extra JIT instructions to verify stack pointer consistency around user-provided marshaller calls, making it easier to debug corrupting language bindings.
### Changed
- Pull infix v0.1.6.
- [infix] Explicitly enabled 16-byte stack alignment in Windows x64 trampolines to ensure SIMD compatibility.
- [infix] Updated `infix_type_create_vector` to use the vector's full size for its natural alignment (e.g., 32-byte alignment for `__m256`).
- [infix] Refined the Windows x64 ABI to pass all vector types by reference (pointer in GPR). This ensures compatibility with MSVC which expects even 128-bit vectors to be passed via pointer in many scenarios, while still returning them by value in `...
- [infix] Move to a pre-calculated hash field in `_infix_registry_entry_t`. Lookups and rehashing now use this stored hash, significantly reducing string hashing overhead during type resolution and registry scaling.
- [infix] Optimized Type Registry memory management: Internal hash table buckets are now heap-allocated and freed during rehashes, preventing memory "leaks" within the registry's arena.
Based on infix v0.1.3
### Added
- Support for Variadic Functions (varargs):
- Implemented dynamic JIT compilation for C functions with variable arguments (e.g., `printf`).
- Added `variadic_cache` to cache trampolines for repeated calls, ensuring high performance.
- Implemented runtime type inference: Perl integers promote to `sint64`, floats to `double`, and strings to `*char`.
- Added `Affix::coerce($type, $value)` to explicitly hint types for variadic arguments. This allows passing structs by value or forcing specific integer widths where inference is insufficient.
- Cookbook: I'm putting together chapters on a wide range of topics at https://github.com/sanko/Affix.pm/discussions/categories/recipes
- `affix` and `wrap` functions now accept an address to bind to. This expects the library to be `undef` and jumps past the lib location and loading steps.
- Added `File` and `PerlIO` types.
- Allows passing Perl filehandles to C functions expecting standard C streams (`PerlIO*` => `Pointer[PerlIO]`).
- Allows receiving `FILE*` from C and using them as standard Perl filehandles (`FILE*` => `Pointer[File]`).
- A few new specialized pointer types:
- `StringList`: Automatically marshals an array ref of strings to a null-terminated `char**` array (and back). This is useful in instances where `argv` or a similar list is expected.
- `Buffer`: Allows passing a pre-allocated scalar as a mutable `char*` buffer to C (zero-copy write).
- `SockAddr`: Safe marshalling of Perl packed socket addresses to `struct sockaddr*`.
- Affix::Build: A polyglot shared library builder. Currently supports Ada, Assembly, C, C#, C++, Cobol, Crystal, Dlang, Eiffel, F#, Fortran, Futhark, Go, Haskell, Nim, OCaml, Odin, Pascal, Rust, Swift, Vlang, and Zig.
- Affix::Wrap: An experimental tool to introspect C header files and generate Affix bindings and documentation.
- Dual-Driver Architecture:
- `Affix::Wrap::Driver::Clang`: Uses the system `clang` executable to parse the AST for high-fidelity extraction of types, macros, and comments.
- `Affix::Wrap::Driver::Regex`: A zero-dependency fallback driver that parses headers using heuristics.
### Changed
- `Array[Char]` function arguments now accept Perl strings directly, copying the string data into the temporary C array.
- `Affix::errno()` now returns a dualvar containing both the numeric error code (`errno`/`GetLastError`) and the system error string (`strerror`/`FormatMessage`).
# EXPORTS
Affix exports standard types (`Int`, `Double`, etc.) and core functions (`affix`, `wrap`, `load_library`) by
default. You can control imports using tags:
```perl
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...
use Affix qw[:pin]; # Variable binding (pin, unpin)
use Affix qw[:types]; # Types only (Int, Struct, Pointer...)
```
# CORE API
These functions are the primary entry points for interacting with foreign libraries.
## `affix( $lib, $symbol, $params, $return )`
Attaches a symbol from a library to a named Perl subroutine in the current namespace.
- **`$lib`**: A library handle returned by `load_library`, a string name, or `undef` to search the currently running process/executable.
- **`$symbol`**: The name of the C function. To install it under a different name in Perl, pass an array reference: `['c_name', 'perl_alias']`. To bind a raw memory address, pass it directly: `[$ptr, 'perl_alias']`.
- **`$params`**: An `ArrayRef` of Affix Type objects representing the function's arguments.
- **`$return`**: A single Affix Type object representing the return value.
```perl
# 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;
compiled library.
- 1. **Prevent Mangling:** Wrap your exported functions in `extern "C"` to ensure they have predictable names.
```
extern "C" {
int add(int a, int b) { return a + b; }
}
```
- 2. **Or Use Mangled Names:** If you cannot change the C++ source, you must look up the exact mangled name (e.g., `_Z3addii`) using tools like `nm` or `objdump`, and bind to that.
- 3. **Object Methods:** Calling an object's method requires passing the object instance pointer (the `this` pointer) as the first argument. Use the `ThisCall( ... )` wrapper around your callback/signature to automatically insert `Pointer[Void]` at t...
## Rust
Rust does not use the C ABI by default. You must explicitly instruct the compiler to format the function correctly.
- 1. **Exporting:** Use `#[no_mangle]` and `pub extern "C"`.
```rust
#[no_mangle]
```
# Call this at the start of your script when running under Valgrind
set_destruct_level(2);
```
# COMPANION MODULES
Affix ships with two powerful companion modules to streamline your FFI development:
- [**Affix::Wrap**](https://metacpan.org/pod/Affix%3A%3AWrap): Parses C/C++ headers using the Clang AST to automatically generate Affix bindings for entire libraries.
- [**Affix::Build**](https://metacpan.org/pod/Affix%3A%3ABuild): A polyglot builder that compiles inline C, C++, Rust, Zig, Go, and 15+ other languages into dynamic libraries you can bind instantly.
# 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.
## 1. Initialization Phase vs. Execution Phase
Functions that modify Affix's global state are **not thread-safe**. You must perform all definitions in the main thread
before starting any background threads or loops in the library.
infix/include/infix/infix.h view on Meta::CPAN
* C function is "bound" into the JIT-compiled code, offering higher performance.
*
* @param return_value_ptr A pointer to a buffer to receive the return value. Can be `nullptr` for `void` returns.
* @param args_array An array of pointers, where each element points to an argument's value.
*/
typedef void (*infix_cif_func)(void *, void **);
/**
* @brief A function pointer type for a generic closure handler.
*
* This handler is used with `infix_reverse_create_closure` and is ideal for language
* bindings or stateful callbacks where the handler needs access to user-provided data.
*
* @param context The reverse trampoline context, from which `user_data` can be retrieved via
* `infix_reverse_get_user_data`.
* @param return_value_ptr A pointer to a buffer where the handler must write the function's return value.
* @param args_array An array of pointers to the argument values passed by the native C caller.
*/
typedef void (*infix_closure_handler_fn)(infix_context_t *, void *, void **);
/**
* @brief Enumerates the possible status codes returned by `infix` API functions.
*/
infix/include/infix/infix.h view on Meta::CPAN
* infix_reverse_destroy(ctx);
* @endcode
*/
INFIX_API INFIX_NODISCARD infix_status infix_reverse_create_callback(infix_reverse_t **,
const char *,
void *,
infix_registry_t *);
/**
* @brief Creates a generic reverse trampoline (closure) for stateful callbacks.
*
* @details This is the low-level API for reverse calls, designed for language bindings
* and advanced use cases. The handler function has a generic signature
* (`infix_closure_handler_fn`) and receives arguments as a `void**` array.
* A `user_data` pointer can be provided to maintain state between calls, making it
* a "closure".
*
* This is the most flexible way to handle callbacks, as it allows the handler
* to be implemented in another language and to access state associated with the
* callback object.
*
* @param[out] out_context A pointer to an `infix_reverse_t*` that will receive the
infix/include/infix/infix.h view on Meta::CPAN
* @param[in] registry An optional registry for resolving named types in the signature.
* @return `INFIX_SUCCESS` on success, or an error code on failure.
*/
INFIX_API INFIX_NODISCARD infix_status
infix_write_global(infix_library_t *, const char *, const char *, void *, infix_registry_t *);
/** @} */ // end of exports_api group
/**
* @defgroup manual_api Manual API
* @brief A lower-level, programmatic API for creating trampolines from `infix_type` objects.
*
* This API is intended for performance-critical applications or language bindings that
* need to construct type information dynamically without the overhead of string parsing.
* All `infix_type` objects passed to these functions must be allocated from an `infix_arena_t`.
* @{
*/
/**
* @brief Creates a bound forward trampoline from `infix_type` objects.
* @param[out] out_trampoline Receives the created trampoline handle.
* @param[in] return_type The `infix_type` for the function's return value.
* @param[in] arg_types An array of `infix_type*` for the function's arguments.
* @param[in] num_args The number of arguments.
infix/include/infix/infix.h view on Meta::CPAN
* @param[in] size The size of each element.
* @param[in] alignment The required alignment. Must be a power of two.
* @return A pointer to the zero-initialized memory, or `nullptr` on failure.
*/
INFIX_API INFIX_NODISCARD void * infix_arena_calloc(infix_arena_t *, size_t, size_t, size_t);
/** @} */ // end of memory_management group (continued)
/**
* @defgroup introspection_api Introspection API
* @brief Functions for inspecting the properties of trampolines and `infix_type` objects at runtime.
*
* This API is essential for building dynamic language bindings, serializers, or any
* tool that needs to understand the memory layout and signature of C data structures
* and functions.
* @{
*/
/**
* @brief Specifies the output format for printing types and function signatures.
*/
typedef enum {
INFIX_DIALECT_SIGNATURE, /**< The standard, human-readable `infix` signature format. */
INFIX_DIALECT_ITANIUM_MANGLING, /**< (Not yet implemented) Itanium C++ ABI name mangling. */
infix/include/infix/infix.h view on Meta::CPAN
*/
} infix_error_details_t;
/**
* @brief Retrieves detailed information about the last error that occurred on the current thread.
* @return A copy of the last error details structure. This function is thread-safe.
*/
INFIX_API infix_error_details_t infix_get_last_error(void);
/** @} */ // end of error_api group
/**
* @defgroup direct_marshalling_api Direct Marshalling API
* @brief An advanced, high-performance API for language bindings.
* @ingroup high_level_api
*
* This API provides a way to create highly optimized forward trampolines that
* bypass the standard `void**` argument array. Instead, the JIT-compiled code
* directly calls user-provided "marshaller" functions to convert language-specific
* objects into native C arguments just-in-time. This reduces memory indirection
* and copying, yielding significant performance gains for FFI calls in tight loops.
* @{
*/
infix/include/infix/infix.h view on Meta::CPAN
typedef union {
uint64_t u64; ///< Used for all unsigned integer types up to 64 bits.
int64_t i64; ///< Used for all signed integer types up to 64 bits.
double f64; ///< Used for `float` and `double`.
void * ptr; ///< Used for all pointer types.
} infix_direct_value_t;
/**
* @brief A function pointer for a custom marshaller for scalar types.
*
* A language binding provides a function of this type to convert a language object
* into a native C primitive value (integer, float, pointer, etc.).
*
* @param source_object A generic `void*` pointer to the language's native object.
* @return An `infix_direct_value_t` union containing the converted C value.
*/
typedef infix_direct_value_t (*infix_marshaller_fn)(void * source_object);
/**
* @brief A function pointer for a custom marshaller for aggregate types (structs/unions).
*
* A language binding provides a function of this type to populate a C struct/union
* from a language object (e.g., a hash or dictionary).
*
* @param source_object A `void*` pointer to the language's native object.
* @param dest_buffer A pointer to a block of memory, allocated by the JIT trampoline,
* that is the exact size of the C aggregate. The function must
* fill this buffer with the native C data.
* @param type A pointer to the `infix_type` object describing the C aggregate. The
* marshaller can introspect this type to determine field names, offsets,
* and member types.
*/
infix/include/infix/infix.h view on Meta::CPAN
* @param source_object A `void*` pointer to the original language object that was passed in.
* @param c_data_ptr A pointer to the (potentially modified) C data buffer that was passed
* to the C function.
* @param type A pointer to the `infix_type` object describing the C data.
*/
typedef void (*infix_writeback_fn)(void * source_object, void * c_data_ptr, const infix_type * type);
/**
* @brief A struct containing all the necessary handlers for a single function argument.
*
* For each argument, a language binding provides an instance of this struct. Based on
* the argument's type, one or more of the function pointers will be non-NULL.
*/
typedef struct {
/** @brief For "in" parameters of a scalar type (int, float, pointer). */
infix_marshaller_fn scalar_marshaller;
/** @brief For "in" parameters of an aggregate type (struct, union). */
infix_aggregate_marshaller_fn aggregate_marshaller;
/** @brief For "out" or "in-out" parameters. Called after the C function returns. */
infix_writeback_fn writeback_handler;
} infix_direct_arg_handler_t;
infix/src/jit/executor.c view on Meta::CPAN
}
if (context->cached_forward_trampoline != nullptr) {
// Path 1: Type-safe "callback". Use the pre-generated forward trampoline to
// call the user's C function with the correct signature. This is efficient
// and provides a clean interface for the C developer.
infix_cif_func cif_func = infix_forward_get_code(context->cached_forward_trampoline);
cif_func(return_value_ptr, args_array);
}
else {
// Path 2: Generic "closure". Directly call the user's generic handler.
// This path is more flexible and is intended for language bindings where the
// handler needs access to the context and raw argument pointers.
infix_closure_handler_fn handler = (infix_closure_handler_fn)context->user_callback_fn;
handler(context, return_value_ptr, args_array);
}
INFIX_DEBUG_PRINTF("Exiting reverse call dispatcher.");
}
infix/src/jit/trampoline.c view on Meta::CPAN
if (status != INFIX_SUCCESS && handle != nullptr)
infix_forward_destroy(handle);
infix_arena_destroy(temp_arena);
return status;
}
/**
* @brief Creates a bound forward trampoline from `infix_type` objects (Manual API).
*
* @details This is the lower-level, programmatic way to create a bound forward trampoline.
* It bypasses the signature string parser, making it suitable for performance-critical
* applications or language bindings that construct type information dynamically.
*
* All `infix_type` objects passed to this function must be fully resolved and have
* a valid layout. They should be allocated from a user-managed `infix_arena_t`.
*
* @param[out] out_trampoline Receives the created trampoline handle.
* @param[in] return_type The `infix_type` for the function's return value.
* @param[in] arg_types An array of `infix_type*` for the function's arguments.
* @param[in] num_args The number of arguments.
* @param[in] num_fixed_args The number of non-variadic arguments.
* @param[in] target_function The address of the C function to call.
lib/Affix.pod view on Meta::CPAN
and exceptionally fast.
=head1 EXPORTS
Affix exports standard types (C<Int>, C<Double>, etc.) and core functions (C<affix>, C<wrap>, C<load_library>) by
default. You can control imports using tags:
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...
use Affix qw[:pin]; # Variable binding (pin, unpin)
use Affix qw[:types]; # Types only (Int, Struct, Pointer...)
=head1 CORE API
These functions 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;
lib/Affix.pod view on Meta::CPAN
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
lib/Affix.pod view on Meta::CPAN
# 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
lib/Affix/Build.pod view on Meta::CPAN
C<Affix::Build> is a cross-platform compilation utility designed to generate shared libraries (C<.dll>, C<.so>,
C<.dylib>) from source code in over 20 different programming languages.
While originally developed to compile test fixtures for the Affix suite, it has evolved into a robust tool for creating
polyglot extensions. It abstracts away the complexity of invoking various compilers, normalizing object file
extensions, handling platform-specific linker flags (such as MinGW vs. MSVC on Windows), and ensuring that language
runtimes (like the Go GC or .NET Runtime) are correctly initialized.
It serves as a powerful, polyglot alternative to C<Inline::*> modulesâallowing you to write native code in your
language of choice and instantly bind to it from Perl using L<Affix>.
=head2 Build Strategies
The builder automatically selects the optimal strategy based on the input sources:
=over
=item 1. B<Native Toolchain Strategy> (Single Language)
If you provide source files for only B<one> language, C<Affix::Build> delegates the entire build process to that
lib/Affix/Wrap.pm view on Meta::CPAN
} class Affix::Wrap::Variable : isa(Affix::Wrap::Entity) {
field $type : reader : param;
method affix_type { sprintf 'pin my $%s, $lib, \'%s\' => %s', $self->name, $self->name, $type->affix_type }
method affix ( $lib, $pkg //= () ) {
if ($lib) {
my $t = $type->affix;
if ($pkg) {
no strict 'refs';
# Vivify package variable and bind it
Affix::pin( ${ "${pkg}::" . $self->name }, $lib, $self->name, $t );
}
else {
my $var;
Affix::pin( $var, $lib, $self->name, $t );
return $var;
}
}
$type->affix;
}
lib/Affix/Wrap.pod view on Meta::CPAN
# Option 1: Instantly wrap and inject into the current namespace
# You may call functions exported by the lib immediately
$wrapper->wrap('libsqlite3.so', __PACKAGE__);
# Option 2: Generate a standalone Perl module file to disk
# This should get you started on a library wrapper you'll eventually put on CPAN
$wrapper->generate('libsqlite3.so', 'My::SQLite', 'lib/My/SQLite.pm');
=head1 DESCRIPTION
C<Affix::Wrap> is a frictionless binding generator that bridges C/C++ header files and L<Affix>. It parses headers to
extract functions, structs, enums, typedefs, macros, and global variables, automatically converting this information
into L<Affix> definitions.
It is designed to facilitate two primary developer workflows:
=over
=item 1. B<Rapid Prototyping (Runtime Wrapping)>
Parse headers on the fly and inject bindings directly into your running Perl environment via C<wrap()>. This is perfect
for private tooling, experimental scripts, or whenever you want to avoid the boilerplate of a dedicated FFI module.
=item 2. B<Distribution (Static Generation)>
Generate standalone F<.pm> files for CPAN via C<generate()>. This produces a pure-Perl module that depends only on
L<Affix>, ensuring fast load times and zero development dependencies (like C<Clang> or C<Affix::Wrap> itself) for your
end users.
=back
lib/Affix/Wrap.pod view on Meta::CPAN
=item * Function signatures (including pointer-to-function arguments)
=item * Nested Structs and Unions
=item * Enums (maps them to Affix Dualvar Enums)
=item * Macros (numeric and string constants)
=item * Typedefs (follows deep typedef chains)
=item * Extern Global Variables (binds them via C<Affix::pin>)
=item * Doxygen/Markdown Comments (extracts to POD when generating modules)
=back
=head1 CONSTRUCTOR
=head2 C<new( ... )>
my $binder = Affix::Wrap->new(
project_files => [ 'lib.h' ],
include_dirs => [ '/usr/include' ],
types => {
'my_opaque_t' => Pointer[Void],
'my_int_t' => Int64
},
driver => 'Clang'
);
=over
lib/Affix/Wrap.pod view on Meta::CPAN
Optional. Explicitly select the parser driver. Values are C<'Clang'> or C<'Regex'>. If omitted, C<Affix::Wrap> attempts
to find the C<clang> executable and falls back to Regex if unavailable.
=back
=head1 METHODS
=head2 wrap( $lib, [$target_package] )
$binder->wrap( $lib );
$binder->wrap( $lib, 'My::Package' );
Parses the project files and immediately binds all found entities (functions, variables, constants, types) to the
target package.
=over
=item C<$lib>
The shared library to link the functions against.
=item C<$target_package>
Optional. The namespace to inject symbols and types into. Defaults to the caller package.
=back
=head2 generate( $lib, $pkg, $output_file )
$binder->generate( 'mylib', 'My::Lib', 'lib/My/Lib.pm' );
Parses the headers and writes a fully functioning, standalone Perl .pm module to disk. This is highly recommended for
production modules to avoid the overhead of parsing headers at runtime.
=head2 parse( [$entry_point] )
my @nodes = $binder->parse;
Parses the project files and returns a list of Node objects (see B<Data Model> below). Use this if you want to inspect
the C header structure or generate code strings for a static Perl module.
The nodes are sorted by file name and line number to ensure deterministic output order.
=over
=item C<$entry_point>
lib/Affix/Wrap.pod view on Meta::CPAN
=head1 Data Model
The C<parse()> method returns a list of objects inheriting from C<Affix::Wrap::Entity>.
All nodes provide at least two key methods:
=over
=item * C<affix_type>: Returns a B<string> of Perl code representing the type or declaration (e.g., C<"Int">, C<"typedef Foo => Int">). Used for code generation.
=item * C<affix( $lib, $pkg )>: Performs the actual binding at runtime. Installs symbols into C<$pkg> using C<$lib>.
=back
=head2 Affix::Wrap::Type
Represents a generic C type (e.g., C<int>, C<void>, C<size_t>).
=head2 Affix::Wrap::Type::Pointer
Represents C<T*> types. Wraps another type object.
lib/Affix/Wrap.pod view on Meta::CPAN
=item * C<affix_type>: Returns the signature of the type OR the nested definition.
=back
=head2 Affix::Wrap::Function
A C function declaration.
=over
=item * C<affix_type>: Returns a complete Perl string to bind this function (e.g., C<affix $lib, name =E<gt> ...>).
=item * C<affix( $lib, $pkg )>: Installs the function into C<$pkg>.
=back
=head2 Affix::Wrap::Struct
A C struct or union definition.
=over
lib/Affix/Wrap.pod view on Meta::CPAN
Affix::Wrap->new( project_files => ['demo.h'] )->wrap($lib);
# Now you can use them:
my $obj = Demo_CreateStruct();
=head3 Manual Control
If you need to filter which functions are bound or rename them, you can iterate over the AST manually instead of
calling C<wrap>:
my $binder = Affix::Wrap->new( project_files => ['demo.h'] );
for my $node ( $binder->parse ) {
next if $node->name =~ m[^Internal_]; # Skip internal functions
# Manually bind
if ( $node->can('affix') ) {
$node->affix($lib);
}
}
=head2 Generating Affix Modules for CPAN
To create a distributable module (e.g., C<My::Lib.pm>) without requiring your users to have C<Clang> or C<Affix::Wrap>
installed at runtime, use the C<generate> method:
use Affix::Wrap;
my $binder = Affix::Wrap->new( project_files => ['mylib.h'] );
# Creates 'lib/My/Lib.pm' which depends only on 'Affix'
$binder->generate( 'mylib', 'My::Lib', 'lib/My/Lib.pm' );
If you need custom behaviors (like filtering functions or adding custom POD), you can iterate over the AST manually as
described in L<parse()>.
=head1 AUTHOR
Sanko Robinson E<lt>sanko@cpan.orgE<gt>
=head1 COPYRIGHT
t/007_pointers.t view on Meta::CPAN
is strnlen( $dup, 5 ), 5, 'strnlen capped at max';
is strnlen( $dup, 100 ), 11, 'strnlen found true length';
# Ensure it's managed memory that we can free
ok free($dup), 'free(dup) worked';
};
};
subtest 'return malloc\'d pointer' => sub {
ok affix( $lib_path, 'test', [] => Pointer [Void] ), 'affix test()';
# We MUST bind C's free, because Affix::free uses Perl's allocator.
# Mixing them causes crashes on Windows.
ok affix( $lib_path, 'c_free', [ Pointer [Void] ] => Void ), 'affix c_free()';
ok my $string = test(), 'test()';
is Affix::cast( $string, String ), 'Testing', 'read C string';
# Correct cleanup: Use the allocator that created it.
c_free($string);
pass('freed via c_free');
};
subtest 'deep pointers' => sub {
t/017_affix_build.t view on Meta::CPAN
extern(C) int add_d(int a, int b) { return a + b; }
run_test( 'odin', 'Odin', <<~'', 'add_odin', 'odin' );
package main
@(export)
add_odin :: proc "c" (a, b: i32) -> i32 {
return a + b
}
run_test( 'fortran', 'Fortran', <<~'', 'add_f', 'gfortran' );
function add_f(a, b) bind(c, name='add_f')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: add_f
add_f = a + b
end function
run_test( 'nim', 'Nim', <<~'', 'add_nim', 'nim' );
proc add_nim(a, b: cint): cint {.exportc, dynlib.} =
return a + b
run_test( 'v', 'V', <<~'', 'add_v', 'v' );
t/017_affix_build.t view on Meta::CPAN
#include <stdio.h>
#ifdef _WIN32
__declspec(dllexport)
#endif
int core_version() { return 1; }
C
# Fortran does the math
my $f_src = $TMP_DIR->child('math_algos.f90');
$f_src->spew_utf8(<<~'F90');
function fortran_add(a, b) bind(c, name='fortran_add')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: fortran_add
fortran_add = a + b
end function
F90
# Assembly for optimization
my $asm_bin;
my $asm_src;
my $asm_file_name;
t/017_affix_build.t view on Meta::CPAN
import core.sys.windows.dll;
mixin SimpleDllMain;
export
extern(C) int func_d() { return 5; }
$c->add($f5);
#
my $f6 = $TMP_DIR->child('f6.f90');
$f6->spew_utf8(<<~'');
function func_f() bind(c, name='func_f')
use iso_c_binding
integer(c_int) :: func_f
func_f=6
end function
$c->add($f6);
#
my $asm_ext = ( $Config{archname} =~ /arm64/ ) ? 's' : 'asm';
my $f7 = $TMP_DIR->child("f7.$asm_ext");
$f7->spew_utf8( ( $^O eq 'MSWin32' || $Config{archname} !~ /arm64/ ) ? <<~'' : <<~'' );
; x86/x64
t/025_affix_wrap.t view on Meta::CPAN
#
my $src = <<~'';
//ext: .c
int return_six() { return 6; }
my $dir = Path::Tiny->tempdir;
spew_files( $dir, 'main.c' => $src );
my $lib = compile_ok($src);
my $pkg = $driver_class eq 'Affix::Wrap::Driver::Clang' ? 'Testing_clang' : 'Testing_regex';
#
my $binder = Affix::Wrap->new(
driver => $driver_class->new( project_files => [ $dir->child('main.c')->stringify ] ),
include_dirs => [ './t/src', 'src', 'C:\Users\S\Documents\GitHub\Affix.pm\t\src' ]
);
$binder->wrap( $lib, $pkg );
#
is $pkg->can('return_six')->(), 6, 'returned 6';
};
subtest 'Static Generation' => sub {
my $dir = Path::Tiny->tempdir;
spew_files(
$dir,
'static.h' => <<'EOF',
#define STATIC_VAL 42
typedef struct { int x; } StaticStruct;
int static_func(int i);
EOF
'main.c' => '#include "static.h"'
);
my $parser = $driver_class->new( project_files => [ $dir->child('static.h')->stringify ] );
my $binder = Affix::Wrap->new( driver => $parser );
my $pm_file = $dir->child('StaticLib.pm');
$binder->generate( 'dummy_lib', 'StaticLib', $pm_file->stringify );
ok -e $pm_file, 'Generated .pm file';
my $content = $pm_file->slurp_utf8;
like $content, qr/package\s+StaticLib\s*{/, 'Package decl';
like $content, qr/use constant STATIC_VAL => 42;/, 'Constant generated';
like $content, qr/typedef 'StaticStruct' => Struct\[ x => Int \];/, 'Struct typedef generated';
like $content, qr/affix \$lib, ('static_func'|\[_static_func => 'static_func'\]) => \[Int\], Int;/, 'Function affix generated';
# Syntax check
my ( undef, undef, $exit ) = capture { system $^X, '-Ilib', '-c', $pm_file->stringify };
is $exit >> 8, 0, 'Generated code syntax check OK';
t/050_affix_build.t view on Meta::CPAN
extern(C) int add_d(int a, int b) { return a + b; }
run_test( 'odin', 'Odin', <<~'', 'add_odin', 'odin' );
package main
@(export)
add_odin :: proc "c" (a, b: i32) -> i32 {
return a + b
}
run_test( 'fortran', 'Fortran', <<~'', 'add_f', 'gfortran' );
function add_f(a, b) bind(c, name='add_f')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: add_f
add_f = a + b
end function
run_test( 'nim', 'Nim', <<~'', 'add_nim', 'nim' );
proc add_nim(a, b: cint): cint {.exportc, dynlib.} =
return a + b
run_test( 'v', 'V', <<~'', 'add_v', 'v' );
t/050_affix_build.t view on Meta::CPAN
#include <stdio.h>
#ifdef _WIN32
__declspec(dllexport)
#endif
int core_version() { return 1; }
C
# Fortran does the math
my $f_src = $TMP_DIR->child('math_algos.f90');
$f_src->spew_utf8(<<~'F90');
function fortran_add(a, b) bind(c, name='fortran_add')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: fortran_add
fortran_add = a + b
end function
F90
# Assembly for optimization
my $asm_bin;
my $asm_src;
my $asm_file_name;
t/050_affix_build.t view on Meta::CPAN
import core.sys.windows.dll;
mixin SimpleDllMain;
export
extern(C) int func_d() { return 5; }
$c->add($f5);
#
my $f6 = $TMP_DIR->child('f6.f90');
$f6->spew_utf8(<<~'');
function func_f() bind(c, name='func_f')
use iso_c_binding
integer(c_int) :: func_f
func_f=6
end function
$c->add($f6);
#
my $asm_ext = ( $Config{archname} =~ /arm64/ ) ? 's' : 'asm';
my $f7 = $TMP_DIR->child("f7.$asm_ext");
$f7->spew_utf8( ( $^O eq 'MSWin32' || $Config{archname} !~ /arm64/ ) ? <<~'' : <<~'' );
; x86/x64
t/900_leak.t view on Meta::CPAN
void * test() {
void * ret = malloc(8);
if ( ret == NULL ) { }
else { strcpy(ret, "Testing"); }
return ret;
}
void c_free(void* p) { free(p); }
ok affix( $lib, 'test', [] => Pointer [Void] ), 'affix test()';
# We MUST bind C's free, because Affix::free uses Perl's allocator.
# Mixing them causes crashes on Windows.
ok affix( $lib, 'c_free', [ Pointer [Void] ] => Void ), 'affix c_free()';
ok my $string = test(), 'test()';
is Affix::cast( $string, String ), 'Testing', 'read C string';
# Correct cleanup: Use the allocator that created it.
c_free($string);
pass('freed via c_free');
};
done_testing;