While runtime wrapping is convenient, it has a startup cost (parsing headers every time). For a distributable CPAN module, you want the parsing to happen once (on your machine), generating a pure Perl module that users can load instantly.

Affix::Wrap objects have an affix_type method that returns the Perl code required to bind that entity. We can use this to write a code generator.

The Recipe

This recipe will generate a file named MyLib.pm from a C header, complete with POD documentation extracted from C comments.

use v5.40;
use Affix::Wrap;
use Path::Tiny;

# 1. The Source Header
my $header = Path::Tiny->tempfile( SUFFIX => '.h' );
$header->spew_utf8(<<~'H');
    /**
     * @brief A user profile.
     */
    typedef struct {
        int id;
        char name[32];
    } User;

    /**
     * @brief Fetch a user from the database.
     * @param id The user ID to lookup
     * @return A User struct
     */
    User get_user(int id);
    H

# 2. Parse
my $binder = Affix::Wrap->new( project_files => ["$header"] );
my @nodes  = $binder->parse;

# 3. Generate Perl Code
my $code = <<~'PERL';
    package MyLib;
    use v5.40;
    use Affix;

    # In a real module, you might locate the lib via Alien::Base or File::ShareDir
    my $lib = Affix::load('mylib');
    PERL
for my $node (@nodes) {

    # Spew POD
    if ( my $doc = $node->pod ) {
        $code .= "\n$doc=cut\n\n";
    }

    # Generate Affix calls
    if ( $node isa Affix::Wrap::Function ) {
        $code .= $node->affix_type . "\n";
    }
    elsif ( $node isa Affix::Wrap::Typedef ) {
        $code .= $node->affix_type . ";\n";
    }
    else {
        ...;    # etc.
    }
}
$code .= "\n1;";

# 4. Save
path('MyLib.pm')->spew_utf8($code);
say $code;

Output (The generated file):

package MyLib;
use v5.40;
use Affix;

# In a real module, you might locate the lib via Alien::Base or File::ShareDir
my $lib = Affix::load('mylib');

=head2 User

A user profile.

=cut

typedef User => Struct[ id => Int, name => Array[Char, 32] ];

=head2 get_user

Fetch a user from the database.

=over

=item C<id>

The user ID to lookup

=back

B<Returns:> A User struct

=cut

affix $lib, get_user => [Int], User;

1;

How It Works

Kitchen Reminders