Affix

 view release on metacpan or  search on metacpan

t/999_marshaller_2.t  view on Meta::CPAN

    int id;
    char name[16];
} Task;

typedef struct {
    char name[32];
    Task tasks[2]; // Array inside struct
} Employee;

typedef struct {
    Employee *manager; // Pointer inside struct
    int budget;
} Company;

// Verification function
DLLEXPORT bool verify_hierarchy(Company *c) {
    if (!c || !c->manager) return false;

    warn("# c: Company { budget: %d, manager: '%s' }", c->budget, c->manager->name);
    warn("# c: Manager Task 0: [%d] %s", c->manager->tasks[0].id, c->manager->tasks[0].name);

    return (c->budget == 50000 &&
            strcmp(c->manager->name, "Alice") == 0 &&
            c->manager->tasks[0].id == 101);
}

// Helper to return a NULL manager company
DLLEXPORT Company* get_null_company() {
    static Company c = { .manager = NULL, .budget = 100 };
    return &c;
}


// C++ Mock: Destructor Tracking
static int destructor_count = 0;
typedef struct { int id; } MockObj;
DLLEXPORT MockObj* mock_new(int id) {
    MockObj* m = (MockObj*)malloc(sizeof(MockObj));
    m->id = id;
    return m;
}
DLLEXPORT void mock_delete(MockObj* m) {
    destructor_count++;
    free(m);
}
DLLEXPORT int get_destructor_count() { return destructor_count; }

// Function Pointer Pattern
typedef int (*calc_t)(int, int);
typedef struct {
    calc_t operation;
} Calculator;

DLLEXPORT int run_calc(Calculator *c, int a, int b) {
    if (!c || !c->operation) return -1;
    return c->operation(a, b);
}

DLLEXPORT int debug_variadic(int count, ...) {
    va_list args;
    va_start(args, count);
    warn("# c: debug_variadic received count=%d", count);
    for (int i = 0; i < count; i++) {
        int val = va_arg(args, int);
        warn("# c: argument %d = %d", i, val);
    }
    va_end(args);
    return count * 10; // Return something specific to check return logic
}

END_C
#
my $lib_path = compile_ok($C_CODE);
ok( $lib_path && -e $lib_path, 'Compiled a test shared library successfully' );
affix_ok $lib_path, 'test_ptr', [ Pointer [Void] ], Bool;
#
#~ ok my $ptr = malloc(1024), '$ptr = malloc(1024)';
ok my $ptr = alloc_owned(1024), '$ptr = alloc_owned(1024)';
ok is_pin($ptr),                'is_pin($ptr)';

#~ ok test_ptr($ptr),              'test_ptr($ptr)';
diag sprintf( "0x%016X", address($ptr) );
#
subtest struct => sub {
    typedef Pos => Struct [ x => Int, y => Double ];
    affix $lib_path, 'check_pos_val', [ Pos() ],             Bool;
    affix $lib_path, 'check_pos_ptr', [ Pointer [ Pos() ] ], Bool;

    # Allocate raw memory
    ok my $mem = alloc_owned( sizeof( Pos() ) ), 'alloc_owned memory for Pos';

    # Cast the raw memory to our Pos struct
    # This creates a magical 2.0 pin variable
    ok my $p = cast( $mem, Pos() ), 'cast memory to Pos struct';

    # Manipulate fields natively via magic
    $p->{x} = 10;
    $p->{y} = 20.5;

    # Verify native fields
    is $p->{x}, 10,   'field x is 10';
    is $p->{y}, 20.5, 'field y is 20.5';

    # Pass the magical pin to FFI (By Value)
    # Affix will use memcpy because it's a pin
    ok check_pos_val($p), 'FFI check_pos_val($p) - passed by value';

    # Pass the magical pin to FFI (By Pointer)
    # Affix will use get_address_v2 to resolve the pointer
    ok check_pos_ptr($p), 'FFI check_pos_ptr($p) - passed by pointer';
    diag sprintf( "Address: 0x%X", address($p) );
};
subtest company => sub {
    typedef Task     => Struct [ id      => Int, name => Array [ Char, 16 ] ];
    typedef Employee => Struct [ name    => Array [ Char, 32 ], tasks => Array [ Task(), 2 ] ];
    typedef Company  => Struct [ manager => Pointer [ Employee() ], budget => Int ];
    #
    affix $lib_path, 'verify_hierarchy', [ Pointer [ Company() ] ], Bool;

    # Setup nested data in C memory
    ok my $mem_comp = alloc_owned( sizeof( Company() ) ),  'alloc Company';



( run in 0.681 second using v1.01-cache-2.11-cpan-b301d465b3d )