Affix
view release on metacpan or search on metacpan
t/999_marshaller_2.t view on Meta::CPAN
typedef struct {
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';
ok my $mem_mgr = alloc_owned( sizeof( Employee() ) ), 'alloc Manager';
t/999_marshaller_2.t view on Meta::CPAN
subtest 'Feature: Const-Correctness / Readonly Pins' => sub {
typedef Info => Struct [ version => Float, author => String ];
my $mem = alloc_owned( sizeof( Info() ) );
my $info = cast( $mem, Info() );
$info->{version} = 1.0;
# Apply readonly state
ok !Affix::readonly($info), 'Pin defaults to mutable';
Affix::readonly( $info, 1 );
ok Affix::readonly($info), 'Pin is now marked readonly';
# Attempting to assign should croak
like dies {
$info->{version} = 2.0;
}, qr/Modification of a read-only C value attempted/, 'Caught illegal write to readonly pin';
is $info->{version}, 1.0, 'Value remained unmodified after exception';
# Unlock
Affix::readonly( $info, 0 );
$info->{version} = 2.0;
is $info->{version}, 2.0, 'Value successfully modified after unlocking';
};
subtest 'Feature: SIMD Vectors' => sub {
# 4-element single-precision float vector
typedef SimdType => Vector [ 4, Float32 ];
my $mem = alloc_owned( sizeof( SimdType() ) );
my $vec = cast( $mem, SimdType() );
# Vectors map to arrayrefs natively during bind
$vec->[0] = 1.1;
$vec->[1] = 2.2;
$vec->[2] = 3.3;
$vec->[3] = 4.4;
is int( $vec->[0] ), 1, 'SIMD element 0 read OK';
is int( $vec->[3] ), 4, 'SIMD element 3 read OK';
};
subtest varargs => sub {
affix libc, [ 'sprintf' => 'my_sprintf' ], [ Pointer [SChar], Pointer [SChar], VarArgs ], Int;
typedef Vec => Struct [ x => Int, y => Int ];
my $mem = alloc_owned( sizeof( Vec() ) );
my $v = cast( $mem, Vec() );
$v->{x} = 100;
$v->{y} = 200;
# Test 1: Pass v2 members to variadic
my $out = " " x 100;
# sprintf(buf, "%d %d", v->x, v->y)
# Affix must extract the integers from the magical scalars
my_sprintf( $out, "%d and %d", $v->{x}, $v->{y} );
like $out, qr/100 and 200/, 'Variadic correctly marshalled V2 magical members';
# Test 2: Pass the whole struct pin to variadic (should treat as pointer)
# Most C variadic functions expect pointers for structs/strings
my $addr = address($v);
my_sprintf( $out, "Address is %p", $v );
like $out, qr/Address is [x0-9a-f]+/i, 'Variadic correctly marshalled V2 pin as pointer';
};
subtest 'Variadic Debugger' => sub {
affix $lib_path, 'debug_variadic', [ Int, VarArgs ], Int;
my $ret = debug_variadic( 2, 100, 200 ); # Call with Int;Int,Int
is $ret, 20, 'Variadic function returned expected calculated value (20)';
};
#
done_testing;
( run in 0.331 second using v1.01-cache-2.11-cpan-84de2e75c66 )