Sometimes, you don't have a pre-existing .dll or .so file. Sometimes, you just have an idea, a heavy mathematical loop, or a snippet of C code you found on Stack Overflow, and you want to run it right now inside your Perl script.
This is where Affix::Build shines. It turns your Perl script into a build system, a linker, and a loader, all in about five lines of code.
Let's look at a classic "Hello World" of pointer manipulation: the Integer Swap.
The Recipe
use v5.40;
use Affix;
use Affix::Build;
# 1. Spin up the compiler
my $c = Affix::Build->new();
# 2. Add some C code directly into the script
$c->add( \<<~'', lang => 'c' );
void swap(int *a, int *b) {
int tmp = *b;
*b = *a;
*a = tmp;
}
# 3. Compile, Link, and Bind
affix $c->link, swap => [ Pointer [Int], Pointer [Int] ] => Void;
# 4. Profit
my $a = 1;
my $b = 2;
say "Before: [a,b] = [$a, $b]";
# Pass references so C can write back to Perl
swap( \$a, \$b );
say "After: [a,b] = [$a, $b]";
How It Works
This creates a seamless bridge between the two languages. Here is what is happening under the hood:
- The Builder
Affix::Build->new() creates a temporary workspace. It detects your operating system (Windows, Linux, macOS, BSD) and hunts for a viable compiler chain (GCC, Clang, MSVC). You don't need to configure Makefiles or worry about linker flags; the class handles the heavy lifting.
- The Source
We pass the source code as a reference to a string (\<<~''>). This tells the compiler, "I'm not giving you a filename; I'm giving you the raw code." We specify lang => 'c', but Affix::Build is a polyglot—it could just as easily have been cpp, rust, or fortran.
- The Link
Calling $c->link triggers the build process. It writes your source to a temp file, compiles it into an object file, and links it into a dynamic library. It returns the absolute path to that library (e.g., /tmp/affix_lib.so).
We pass that path immediately to affix( ... ), binding the symbol swap.
- The Types
This is the most critical part of this recipe:
[ Pointer [Int], Pointer [Int] ]
If we had defined this as [ Int, Int ], Affix would have passed the values 1 and 2 to C. The C code would have swapped those values in its own local stack and returned, leaving our Perl variables untouched.
By specifying that these are Pointers, we tell Affix we expect a memory address.
- The Execution
Because we defined the types as Pointers, we must pass B
swap( \$a, \$b );
Affix takes the memory address of the value held in the scalar $a, passes it to C, and allows the C code to overwrite the memory directly. When swap returns, $a sees the data that was previously in $b and vice versa. Their values are swapped!
Kitchen Reminders
The chef would like to remind you of things you should have noted or learned from this recipe.
- Compiler Required
While Affix works on any machine, Affix::Build obviously requires a C compiler (like gcc, clang, or Visual Studio) to be in your system's PATH.
- Automatic Cleanup
By default, Affix::Build creates a temporary directory for the build artifacts. When your script ends, that directory is wiped clean. If you want to inspect the generated C files or the .so library for debugging, pass clean => 0 to the constructor:
my $c = Affix::Build->new( clean => 0, debug => 1 );
- Type Safety
C is unforgiving. If you tell Affix the argument is a Pointer[Int], but you pass a string like "Hello", Affix will try to make it work, but the C code effectively interprets the bytes of that string as an integer. Always match your Perl data types to your Affix definitions.