Affix

 view release on metacpan or  search on metacpan

Changes.md  view on Meta::CPAN

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

Changes.md  view on Meta::CPAN

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

Changes.md  view on Meta::CPAN

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

README.md  view on Meta::CPAN


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

README.md  view on Meta::CPAN

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]

README.md  view on Meta::CPAN


```
# 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;



( run in 2.459 seconds using v1.01-cache-2.11-cpan-2398b32b56e )