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
-
affix_typeThis method returns the string representation of the binding.- For
User:Struct[ id => Int, name => Array[Char, 32] ]. - For
get_user:affix $lib, get_user => [Int], User;.
- For
-
podAffix::Wrapparses Doxygen-style comments (@brief,@param) and converts them into standard Perl POD, meaning your generated module is automatically documented!
Kitchen Reminders
- Dependencies: The generated
.pmfile does not depend onAffix::Wraporclang. It only depends onAffix. This means your end-users do not need a compiler installed to use your module.