Sometimes you need to peek inside the oven. If a function expects a C style struct to be passed by value or if you want to read struct members without writing C helper functions, you can define the struct layout in Perl and Affix handles the rest.

The Recipe

We will create a simple geometry library that operates on Points and Rectangles.

use v5.40;
use Affix qw[:all];
use Affix::Build;

# 1. Compile C Library
my $c = Affix::Build->new();
$c->add( \<<~'END', lang => 'c' );
    typedef struct {
        int x, y;
    } Point;

    typedef struct {
        Point top_left;
        Point bottom_right;
        int color;
    } Rect;

    // Pass by Value
    int area(Rect r) {
        int w = r.bottom_right.x - r.top_left.x;
        int h = r.bottom_right.y - r.top_left.y;
        return w * h;
    }

    // Pass by Reference (Modify in place)
    void move_rect(Rect *r, int dx, int dy) {
        r->top_left.x += dx;
        r->top_left.y += dy;
        r->bottom_right.x += dx;
        r->bottom_right.y += dy;
    }
END
my $lib = $c->link;

# 2. Define Types
# Order matters! Defined in the same order as the C struct.
typedef Point => Struct [ x => Int, y => Int ];

# We can nest types. Point() refers to the typedef above.
typedef Rect => Struct [ tl => Point(), br => Point(), color => Int ];

# 3. Bind
affix $lib, 'area',      [ Rect() ]                       => Int;
affix $lib, 'move_rect', [ Pointer [ Rect() ], Int, Int ] => Void;

# 4. Use (Pass by Value)
# We represent the struct as a hash reference.
my $r = { tl => { x => 0, y => 0 }, br => { x => 10, y => 20 }, color => 0xFF00FF };
say 'Area: ' . area($r);    # 200

# 5. Use (Pass by Reference)
# We pass a reference to the hash.
# Affix updates the hash members after the call.
move_rect( $r, 5, 5 );
say "New TL: $r->{tl}{x}, $r->{tl}{y}";    # 5, 5

How It Works

Kitchen Reminders