NAME
Affix::Wrap - Frictionless C Header Introspection and FFI Wrapper Generation
SYNOPSIS
use v5.40;
use Affix;
use Affix::Wrap;
# Parse headers and install symbols into the current package
Affix::Wrap->new(
project_files => [ 'include/mylib.h' ],
include_dirs => [ '/usr/local/include' ],
types => {
'git_repository' => Pointer[Void], # Treat opaque struct as void pointer
'git_off_t' => Int64, # Force specific integer width
}
)->wrap( load_library('demo') );
# Use the C functions immediately
my $val = mylib_function(10);
DESCRIPTION
Affix::Wrap acts as a bridge between C/C++ header files and Affix. It parses C header files to produce a structured object model (AST) representing functions, structs, enums, typedefs, macros, and variables.
This module is designed to facilitate two primary workflows:
- 1. Runtime Wrapping
-
Parsing headers on the fly to wrap libraries dynamically via
wrap(). This is ideal for rapid prototyping, private tooling, or when you don't want to maintain a separate XS/FFI module file. - 2. Static Generation
-
Creating .pm files to be distributed on CPAN via
parse(). This provides the fastest load times for end users, as the parsing happens only once during development.
Please be aware that this is experimental software!
Drivers
Affix::Wrap utilizes a dual-driver approach to parsing:
Clang driver
The preferred driver. It uses the system's
clangexecutable to dump the AST in JSON format. It is extremely accurate, handles C preprocessor logic (macros, includes), and resolves complex typedef chains correctly.Regex driver
A fallback driver implemented in pure Perl. It scans headers using regular expressions. While zero-dependency, it may struggle with heavily macro-laden code or complex C++ templates.
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 inproject_filesis automatically added to this list. types-
Optional. A hash reference mapping type names to Affix type objects or definition strings.
These are registered via
typedefbefore parsing begins. This is useful for:Defining opaque types (e.g. mapping a complex C struct to
Pointer[Void]).Fixing integer widths (e.g. forcing
off_ttoInt64across platforms).Overriding definitions that the parser might misinterpret.
If the parser encounters a struct, enum, or typedef in the C header with the same name as an entry in this hash, the C definition is skipped to prevent redefinition warnings and ensure your override takes precedence.
driver-
Optional. Explicitly select the parser driver. Values are
'Clang'or'Regex'. If omitted,Affix::Wrapattempts to find theclangexecutable and falls back to Regex if unavailable.
METHODS
wrap( $lib, [$target] )
$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-
An instance of an Affix library object (created via
load_library). $target-
Optional. The package name to install symbols into. Defaults to the calling package.
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$pkgusing$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 stringCallback[[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 stringStruct[ ... ]orUnion[ ... ].
Affix::Wrap::Typedef
A name alias for another type.
underlying: The type object being aliased.affix_type: Returns stringtypedef 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 stringEnum[ Name => Val, ... ]. String values/expressions in enums are quoted automatically to prevent eval errors.
Affix::Wrap::Variable
A global extern variable.
affix_type: Returns stringpin 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 stringuse 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), use the affix_type method. This returns Perl source code strings.
use Affix::Wrap;
use Path::Tiny;
my $binder = Affix::Wrap->new( project_files => ['mylib.h'] );
my $ast = $binder->parse;
my $code = <<~'PERL';
package My::Lib;
use v5.40;
use Affix;
# Load the library (user must ensure this exists)
my $lib = load_library('mylib');
PERL
for my $node (@$ast) {
# Add POD documentation derived from C comments
if ( defined $node->doc ) {
$code .= "\n=head2 " . $node->name . "\n\n";
$code .= $node->doc . "\n\n=cut\n";
}
# Generate Perl code
if ( $node isa Affix::Wrap::Function ) {
# e.g. "affix $lib, 'my_func', [Int], Void;"
$code .= $node->affix_type . "\n";
}
elsif ( $node isa Affix::Wrap::Typedef ) {
# e.g. "typedef MyStruct => Struct[ ... ];"
$code .= $node->affix_type . ";\n";
}
elsif ( $node isa Affix::Wrap::Macro ) {
# e.g. "use constant MAX_VAL => 100;"
$code .= $node->affix_type . ";\n";
}
elsif ( $node isa Affix::Wrap::Variable ) {
# e.g. "pin my $var, $lib, ..."
$code .= $node->affix_type . ";\n";
}
}
$code .= "\n1;\n";
path('lib/My/Lib.pm')->spew_utf8($code);
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.