C might be the lingua franca of system programming, but Affix::Build is a true polyglot. We've covered building shared libraries in C and C++ but Affix::Build supports over 15 languages out of the box.
This chapter is a quick-reference gallery. Each recipe demonstrates how to compile a single function that adds two integers, exported in a way that Affix can easily bind to it.
Assembly
For raw speed, you can write directly in Assembly.
Affix::Build supports both x86_64 (via NASM) and ARM64 (via the system assembler). Calling conventions differ by architecture and operating system, so we check %Config to determine which assembly code to use.
use v5.40;
use Affix qw[:all];
use Affix::Build;
use Config qw[%Config];
$|++;
my $c = Affix::Build->new( name => 'asm_lib' );
my $arm = $Config{archname} =~ /arm64|aarch64/;
$c->add( \( $arm ? <<~'' : $^O eq 'MSWin32' ? <<~'': <<~'' ), lang => $arm ? 's' : 'asm' );
; ARM64: add w0, w0, w1
.global add
.text
.align 2
add:
add w0, w0, w1
ret
; Win64 x86_64: RCX + RDX -> RAX
global add
section .text
add:
mov eax, ecx
add eax, edx
ret
; SysV x86_64: RDI + RSI -> RAX
global add
section .text
add:
mov eax, edi
add eax, esi
ret
affix $c->link, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
C# (.NET 8+)
With .NET 8's NativeAOT feature, you can compile C# directly to native machine code (no .NET runtime required on the target machine).
use v5.40;
use Affix qw[:all];
use Affix::Build;
$|++;
my $c = Affix::Build->new( name => 'cs_lib' );
$c->add( \<<~'CS', lang => 'cs' );
using System.Runtime.InteropServices;
namespace Demo {
public class Math {
[UnmanagedCallersOnly(EntryPoint="add")]
public static int Add(int a, int b) => a + b;
}
}
CS
affix $c->link, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
D (Dlang)
D is unique: on Windows, a specific initialization mixin is required to ensure its runtime starts correctly when loaded as a DLL.
use v5.40;
use Affix qw[:all];
use Affix::Build;
$|++;
my $c = Affix::Build->new( name => 'd_lib' );
my $src = ( $^O eq 'MSWin32' ? <<~'WIN32' : '' ) . <<~'D';
import core.sys.windows.dll;
mixin SimpleDllMain;
export
WIN32
extern(C) int add(int a, int b) { return a + b; }
D
$c->add( \$src, lang => 'd' );
affix $c->link, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
Fortran
Modern Fortran (2003+) offers the iso_c_binding module, which ensures your functions use standard C types and calling conventions, avoiding the historic "name mangling" issues (like appended underscores).
use v5.40;
use Affix qw[:all];
use Affix::Build;
$|++;
my $c = Affix::Build->new( name => 'fortran_lib' );
$c->add( \<<~'F90', lang => 'f90' );
function add(a, b) bind(c, name='add')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: add
add = a + b
end function
F90
affix $c->link, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
Go (Golang)
Go functions must be marked with the special //export comment to be visible.
Note: The Go runtime spins up background threads (for GC and scheduling) that do not shut down cleanly when a shared library is unloaded. This often causes access violations on Windows during program exit.
Affix attempts to detect Go libraries (by looking for the _cgo_dummy_export symbol) and pin them in memory to prevent this crash. However, if you still encounter segmentation faults during global destruction, you can force a safe exit using POSIX::_exit(0).
use v5.40;
use Affix qw[:all];
use Affix::Build;
$|++;
my $c = Affix::Build->new( name => 'go_lib' );
$c->add( \<<~'GO', lang => 'go' );
package main
import "C"
//export add
func add(a, b C.int) C.int {
return a + b
}
func main() { }
GO
affix $c->link, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
Rust
Rust is an excellent candidate for FFI because it has no garbage collector and strict ABI control. You must use #[no_mangle] and extern "C" to prevent the compiler from changing your function names.
use v5.40;
use Affix qw[:all];
use Affix::Build;
my $c = Affix::Build->new( name => 'rust_lib' );
$c->add( \<<~'RUST', lang => 'rust' );
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
RUST
my $lib = $c->link;
affix $lib, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
Zig
Zig is fully C-ABI compatible. Use the export keyword to make functions available.
use v5.40;
use Affix qw[:all];
use Affix::Build;
$|++;
my $c = Affix::Build->new( name => 'zig_lib' );
$c->add( \<<~'ZIG', lang => 'zig' );
export fn add(a: i32, b: i32) i32 {
return a + b;
}
ZIG
affix $c->link, 'add', [ Int, Int ] => Int;
say add( 4, 5 );
Chef's Notes
This gallery is just a sample but demonstrates that Perl's role as the ultimate glue language is stronger than ever. By abstracting away the build chain complexities... from the strict safety of Rust to the raw speed of Assembly, Affix::Build allows you to reach far outside the CPAN ecosystem. You can now leverage the unique strengths of almost any language, modern or ancient, without ever leaving the comfort of Perl.
In our next chapter, we'll discuss merging some of these languages into a single shared library.