NAME

Affix::Wrap - Frictionless C/C++ Binding Generator for Affix

SYNOPSIS

use Affix::Wrap;

my $wrapper = Affix::Wrap->new(
    project_files => ['/usr/include/sqlite3.h'],
    include_dirs  => ['/usr/include'],
    types         => {
        'sqlite3' => 'Pointer[Void]' # Custom override
    }
);

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

DESCRIPTION

Affix::Wrap is a frictionless binding generator that bridges C/C++ header files and Affix. It parses headers to extract functions, structs, enums, typedefs, macros, and global variables, automatically converting this information into Affix definitions.

It is designed to facilitate two primary developer workflows:

1. Rapid Prototyping (Runtime Wrapping)

Parse headers on the fly and inject bindings directly into your running Perl environment via wrap(). This is perfect for private tooling, experimental scripts, or whenever you want to avoid the boilerplate of a dedicated FFI module.

2. Distribution (Static Generation)

Generate standalone .pm files for CPAN via generate(). This produces a pure-Perl module that depends only on Affix, ensuring fast load times and zero development dependencies (like Clang or Affix::Wrap itself) for your end users.

Parsing Drivers

Affix::Wrap employs a dual-driver strategy:

  • Clang Driver (Recommended)

    The primary driver. It leverages clang -Xclang -ast-dump=json to perform compiler-grade analysis of your headers. It is highly accurate and handles complex C++ templates, macros, and deep inclusion chains with ease.

  • Regex Driver (Fallback)

    A pure-Perl heuristic parser. While less capable than the Clang driver (it may struggle with complex macros or nested C++ constructs), it requires no external dependencies and is sufficient for many straightforward C headers.

Supported Features

Affix::Wrap extracts and bridges:

  • Function signatures (including pointer-to-function arguments)

  • Nested Structs and Unions

  • Enums (maps them to Affix Dualvar Enums)

  • Macros (numeric and string constants)

  • Typedefs (follows deep typedef chains)

  • Extern Global Variables (binds them via Affix::pin)

  • Doxygen/Markdown Comments (extracts to POD when generating modules)

CONSTRUCTOR

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'
);
project_files

Required. An array reference of paths to the C header files (.h, .hpp, .hxx) you wish to parse.

include_dirs

Optional. An array reference of paths to search for #include "..." directives. The directory of every file listed in project_files is automatically added to this list.

types

Optional. A hash reference for manually mapping type names to Affix type objects or definition strings. Very useful for masking complex internal library structures behind Pointer[Void] handles.

driver

Optional. Explicitly select the parser driver. Values are 'Clang' or 'Regex'. If omitted, Affix::Wrap attempts to find the clang executable and falls back to Regex if unavailable.

METHODS

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.

$lib

The shared library to link the functions against.

$target_package

Optional. The namespace to inject symbols and types into. Defaults to the caller package.

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.

parse( [$entry_point] )

my @nodes = $binder->parse;

Parses the project files and returns a list of Node objects (see 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.

$entry_point

Optional. The specific file to start parsing from. Defaults to the first file in project_files.

Data Model

The parse() method returns a list of objects inheriting from Affix::Wrap::Entity.

All nodes provide at least two key methods:

  • affix_type: Returns a string of Perl code representing the type or declaration (e.g., "Int", "typedef Foo = Int">). Used for code generation.

  • affix( $lib, $pkg ): Performs the actual binding at runtime. Installs symbols into $pkg using $lib.

Affix::Wrap::Type

Represents a generic C type (e.g., int, void, size_t).

Affix::Wrap::Type::Pointer

Represents T* types. Wraps another type object.

Affix::Wrap::Type::Array

Represents T[N] fixed-size arrays. Wraps a type object and a count.

Affix::Wrap::Type::CodeRef

Represents function pointers (callbacks), e.g., void (*)(int).

  • ret: Return type object.

  • params: ArrayRef of argument type objects.

  • affix_type: Returns string Callback[[Args] => Ret].

Affix::Wrap::Argument

Function arguments. Stringifies to "Type Name".

Affix::Wrap::Member

Struct/Union members.

  • definition: If the member defines a nested struct/union inline, this holds that definition object.

  • affix_type: Returns the signature of the type OR the nested definition.

Affix::Wrap::Function

A C function declaration.

  • affix_type: Returns a complete Perl string to bind this function (e.g., affix $lib, name => ...).

  • affix( $lib, $pkg ): Installs the function into $pkg.

Affix::Wrap::Struct

A C struct or union definition.

  • tag: Either 'struct' or 'union'.

  • affix_type: Returns signature string Struct[ ... ] or Union[ ... ].

Affix::Wrap::Typedef

A name alias for another type.

  • underlying: The type object being aliased.

  • affix_type: Returns string typedef Name => UnderlyingType.

Note: In C, typedef struct { ... } Name; results in a Typedef object where underlying is the Struct object.

Affix::Wrap::Enum

An enumeration.

  • affix_type: Returns signature string Enum[ Name => Val, ... ]. String values/expressions in enums are quoted automatically to prevent eval errors.

Affix::Wrap::Variable

A global extern variable.

  • affix_type: Returns string pin my $var, $lib, name => Type.

  • affix( $lib, $pkg ): Installs the variable accessor into $pkg.

Affix::Wrap::Macro

A preprocessor #define. Only simple value macros are captured.

  • affix_type: Returns string use constant Name => Value. Expressions (e.g., A + B) are quoted as strings, while literals are preserved.

  • affix( undef, $pkg ): Installs the constant into $pkg.

Tutorials

Runtime Library Wrappers

If you want to use a C library immediately without creating a separate Perl module file, use the wrap method.

use Affix;
use Affix::Wrap;

my $lib = load_library('demo');

# This parses demo.h and installs subroutines, constants,
# and types directly into the calling package.
Affix::Wrap->new( project_files => ['demo.h'] )->wrap($lib);

# Now you can use them:
my $obj = Demo_CreateStruct();

Manual Control

If you need to filter which functions are bound or rename them, you can iterate over the AST manually instead of calling 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);
    }
}

Generating Affix Modules for CPAN

To create a distributable module (e.g., My::Lib.pm) without requiring your users to have Clang or Affix::Wrap installed at runtime, use the 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 parse().

AUTHOR

Sanko Robinson <sanko@cpan.org>

COPYRIGHT

Copyright (C) 2026 by Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.