You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
C99 introduced Flexible Array Members (FAM), which allow a structure to end with an array of unspecified size. This is a common pattern for "Packets" or "Messages" where a header is followed by a variable amount of payload data.
typedefstruct {
intcount;
doublesamples[]; // The size isn't known until runtime
} Signal;
Interfacing with these from a high-level language can be tricky because the sizeof the struct only includes the header, not the array.
The Recipe
We will build a "Dynamic Signal" processor. We will use a single malloc to create a structure large enough to hold our header AND our variable data, and then use Affix to treat it as a single object.
use v5.40;
use Affix qw[:all];
# 1. Define the Structure# We use '?' to indicate a flexible array member.
typedef Signal=> Struct[
count=> Int,
samples=> Array[ Double, '?' ]
];
# 2. Allocation# We want a Signal header + 5 doubles.my$num_samples = 5;
my$total_size = sizeof( Int ) + ( sizeof( Double ) * $num_samples );
say"Allocating $total_size bytes for FAM struct...";
my$ptr = malloc( $total_size );
# 3. Mapping# We cast the raw pointer to our Signal type.# Affix understands that 'samples' is flexible and will # allow array access based on the memory we allocated.my$sig = cast( $ptr, Pointer[ Signal() ] );
# 4. Initialization$sig->{count} = $num_samples;
for (0 .. $num_samples - 1) {
# Accessing the flexible array member naturally$sig->{samples}[$_] = $_ * 1.5;
}
# 5. Usagesay"Signal Header Count: " . $sig->{count};
say"Sample 3: " . $sig->{samples}[2];
# 6. Passing to C# If we had a C function: void process_signal(Signal *s);# we can just pass $sig!# process_signal($sig);# 7. Cleanup
free($ptr);
How It Works
1. The ? Marker
When you define an Array[ Type, '?' ] at the end of a Struct, Affix marks that field as a Flexible Array Member.
Importantly, sizeof(Signal()) will only return the size of the count integer. It effectively treats the array as having size zero for layout purposes.
2. Manual Size Calculation
Since the compiler (and Affix) doesn't know how many elements you want, you must manually calculate the memory requirement: HeaderSize + (ElementSize * Count)
3. Unified Access
Even though the array size was dynamic, Affix's Unified Access allows you to use $sig->{samples}[$i] as if it were a fixed-size array. Affix simply calculates the pointer offset: AddressOf(sig) + offsetof(samples) + (i * sizeof(Double))
Kitchen Reminders
Boundary Safety
Perl usually protects you from "Array Out of Bounds" errors. FAMs do not. If you allocate space for 5 elements but try to write to index 10, you will overwrite other parts of the system heap. Always use the count field in your struct to keep track of your boundaries!
Alignment
The C standard requires that the flexible array member be aligned correctly for its type. Affix handles this automatically by inserting padding between the last fixed member and the start of the flexible array if necessary.
Only at the End
By definition, a flexible array member B be the last member of a structure. Affix will throw an error if you try to define a field after a FAM.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
C99 introduced Flexible Array Members (FAM), which allow a structure to end with an array of unspecified size. This is a common pattern for "Packets" or "Messages" where a header is followed by a variable amount of payload data.
Interfacing with these from a high-level language can be tricky because the
sizeofthe struct only includes the header, not the array.The Recipe
We will build a "Dynamic Signal" processor. We will use a single
mallocto create a structure large enough to hold our header AND our variable data, and then use Affix to treat it as a single object.How It Works
1. The
?MarkerWhen you define an
Array[ Type, '?' ]at the end of aStruct, Affix marks that field as a Flexible Array Member.Importantly,
sizeof(Signal())will only return the size of thecountinteger. It effectively treats the array as having size zero for layout purposes.2. Manual Size Calculation
Since the compiler (and Affix) doesn't know how many elements you want, you must manually calculate the memory requirement:
HeaderSize + (ElementSize * Count)3. Unified Access
Even though the array size was dynamic, Affix's Unified Access allows you to use
$sig->{samples}[$i]as if it were a fixed-size array. Affix simply calculates the pointer offset:AddressOf(sig) + offsetof(samples) + (i * sizeof(Double))Kitchen Reminders
Boundary Safety
Perl usually protects you from "Array Out of Bounds" errors. FAMs do not. If you allocate space for 5 elements but try to write to index 10, you will overwrite other parts of the system heap. Always use the
countfield in your struct to keep track of your boundaries!Alignment
The C standard requires that the flexible array member be aligned correctly for its type. Affix handles this automatically by inserting padding between the last fixed member and the start of the flexible array if necessary.
Only at the End
By definition, a flexible array member B be the last member of a structure. Affix will throw an error if you try to define a field after a FAM.
All reactions