In C, arrays passed to functions are often used as output buffers. The function reads data from the array, modifies it in place, and expects the caller to see the changes. In XS, these are defined as IN_OUT parameters. Affix supports this functionality automatically.
The Recipe
We will define a C function that reverses an integer array in place.
use v5.40;
use Affix qw[:all];
use Affix::Build;
# 1. Compile C
my $c = Affix::Build->new();
$c->add( \<<~'END', lang => 'c' );
// Reverses array 'a' in place
int array_reverse(int a[], int len) {
int tmp, i;
int ret = 0; // Sum of first half (arbitrary logic)
for (i = 0; i < len / 2; i++) {
ret += a[i];
tmp = a[i];
a[i] = a[len - i - 1];
a[len - i - 1] = tmp;
}
return ret;
}
// Wrapper for fixed-size 10-element array
int array_reverse10(int a[10]) {
return array_reverse(a, 10);
}
END
my $lib = $c->link;
# 2. Bind
# Dynamic length array
affix $lib, array_reverse => [ Array[Int], Int ], Int;
# Fixed length array
affix $lib, array_reverse10 => [ Array[Int, 10] ], Int;
# 3. Call (Fixed Size)
my @a = ( 1 .. 10 );
array_reverse10( \@a );
say "@a"; # Output: 10 9 8 7 6 5 4 3 2 1
# 4. Call (Dynamic Size)
my $b = [ 1 .. 20 ];
array_reverse( $b, 20 );
say "@$b"; # Output: 20 19 ... 2 1
How It Works
-
1. In-Place Modification
When you pass a Perl ArrayRef to an
Arrayargument:- Affix allocates a temporary C array.
- Copies your data into it.
- Calls the C function.
- Copies the data back from C to your Perl array.
This ensures that any changes made by the C function are reflected in your
SV*. -
2. Fixed size padding
affix ..., [ Array[Int, 10] ] ...If you define a fixed size (e.g. 10) but pass a shorter list (e.g.
[1, 2]), Affix allocates the full 10 integers and zero-fills the rest. This protects the C function from reading garbage memory if it assumes the array is 10 elements long.
Kitchen Reminders
-
Performance
Copying arrays back and forth (Perl -> C -> Perl) takes time. For very large datasets where you only need to modify a few bytes, consider using
mallocandPointertypes to keep the data in C memory (see Chapter 7). -
Reference requirement
You must pass a reference (
\@aor$array_ref). Passing a list directly (e.g.array_reverse([1,2], ...)) works, but the modifications will be lost because the anonymous array reference is discarded immediately after the call. This is a limitation of perl's handling of scalars.