Affix
view release on metacpan or search on metacpan
t/004_typedef.t view on Meta::CPAN
#include <stdint.h>
#include <stdbool.h>
#include <string.h> // For strcmp
#include <stdlib.h> // For malloc
typedef struct {
int32_t id;
double value;
const char* label;
} MyStruct;
typedef enum { RED, GREEN, BLUE } Color;
DLLEXPORT int check_color(Color c) {
if (c == GREEN) return 1;
return 0;
}
DLLEXPORT int sum_struct_ids(MyStruct* structs, int count) {
int total = 0;
for (int i = 0; i < count; i++) {
total += structs[i].id;
}
return total;
}
typedef struct {
int x;
int y;
} Point;
DLLEXPORT Point create_point(int x, int y) {
Point p = {x, y};
return p;
}
DLLEXPORT int sum_point_by_val(Point p) {
return p.x + p.y;
}
typedef struct {
Point top_left;
Point bottom_right;
const char* name;
} Rectangle;
DLLEXPORT int get_rect_width(Rectangle* r) {
if (!r) return -1;
return r->bottom_right.x - r->top_left.x;
}
typedef union {
int i;
float f;
char c[8];
} MyUnion;
DLLEXPORT float process_union_float(MyUnion u) {
return u.f * 10.0;
}
DLLEXPORT int read_union_int(MyUnion u) {
return u.i;
}
// Takes a callback that processes a struct
DLLEXPORT double process_struct_with_cb(MyStruct* s, double (*cb)(MyStruct*)) {
return cb(s);
}
// Takes a callback that returns a struct
DLLEXPORT int check_returned_struct_from_cb(Point (*cb)(void)) {
Point p = cb();
return p.x + p.y;
}
END_C
#
my $lib_path = compile_ok($C_CODE);
ok( $lib_path && -e $lib_path, 'Compiled a test shared library successfully' );
subtest 'Forward Calls: Advanced Pointers and Arrays of Structs (with Typedefs)' => sub {
plan 2;
note 'Testing marshalling arrays of structs using typedefs.';
isa_ok my $sum_ids = wrap( $lib_path, 'sum_struct_ids', '(*@MyStruct, int32)->int32' ), ['Affix'];
my $struct_array
= [ { id => 10, value => 1.1, label => 'A' }, { id => 20, value => 2.2, label => 'B' }, { id => 30, value => 3.3, label => 'C' }, ];
is $sum_ids->( $struct_array, 3 ), 60, 'Correctly passed an array of structs and summed IDs';
};
subtest 'Forward Calls: Enums and Unions (with Typedefs)' => sub {
plan 4;
note 'Testing marshalling for enums and unions.';
isa_ok my $check_color = wrap( $lib_path, 'check_color', '(int32)->int32' ), ['Affix'];
is $check_color->(1), 1, 'Correctly passed an enum value (GREEN)';
isa_ok my $process_union = wrap( $lib_path, 'process_union_float', '(@MyUnion)->float32' ), ['Affix'];
my $union_data = { f => 2.5 };
is $process_union->($union_data), float(25.0), 'Correctly passed a union with the float member active';
};
subtest 'Forward Calls: Nested Structs and By-Value Returns (with Typedefs)' => sub {
plan 4;
isa_ok my $get_width = wrap( $lib_path, 'get_rect_width', '(*@RectPlus)->int32' ), ['Affix'];
is $get_width->( \{ top_left => { x => 10, y => 20 }, bottom_right => { x => 60, y => 80 }, name => 'My Rectangle' } ), 50,
'Correctly passed nested struct and calculated width';
isa_ok my $create_point = wrap( $lib_path, 'create_point', '(int32, int32)->@Point' ), ['Affix'];
my $point = $create_point->( 123, 456 );
is $point, { x => 123, y => 456 }, 'Correctly received a struct returned by value';
};
subtest 'Advanced Structs and Unions' => sub {
affix $lib_path, 'sum_point_by_val', '(@Point)->int';
my $point_hash = { x => 10, y => 25 };
is( sum_point_by_val($point_hash), 35, 'Correctly passed a struct by value' );
affix $lib_path, 'read_union_int', '(@MyUnion)->int';
my $union_hash = { i => 999 };
is( read_union_int($union_hash), 999, 'Correctly read int member from a C union' );
};
subtest 'Advanced Callbacks (Reverse FFI) (with Typedefs)' => sub {
diag 'Testing callbacks that send and receive structs by passing coderefs directly.';
isa_ok my $harness1 = wrap( $lib_path, 'process_struct_with_cb', '(*@MyStruct, (*(@MyStruct))->float64)->float64' ), ['Affix'];
my $struct_to_pass = { id => 100, value => 5.5, label => 'Callback Struct' };
my $cb1 = sub ($struct_ref) {
# Struct Pointer comes as a Pin (Scalar Ref). Dereference it.
my $struct = $$struct_ref;
return $struct->{value} * 2;
};
is $harness1->( $struct_to_pass, $cb1 ), 11.0, 'Callback coderef received struct pointer and returned correct value';
isa_ok my $harness2 = wrap( $lib_path, 'check_returned_struct_from_cb', '( *(()->void )->@Point )->int32' ), ['Affix'];
is $harness2->(
sub {
pass "Inside callback that will return a struct";
return { x => 70, y => 30 };
}
),
100, 'C code correctly received a struct returned by value from a Perl callback';
};
done_testing;
__END__
/* Basic Primitives */
DLLEXPORT int add(int a, int b) { return a + b; }
DLLEXPORT unsigned int u_add(unsigned int a, unsigned int b) { return a + b; }
// Functions to test every supported primitive type
DLLEXPORT int8_t echo_int8 (int8_t v) { return v; }
DLLEXPORT uint8_t echo_uint8 (uint8_t v) { return v; }
DLLEXPORT int16_t echo_int16 (int16_t v) { return v; }
DLLEXPORT uint16_t echo_uint16 (uint16_t v) { return v; }
DLLEXPORT int32_t echo_int32 (int32_t v) { return v; }
DLLEXPORT uint32_t echo_uint32 (uint32_t v) { return v; }
DLLEXPORT int64_t echo_int64 (int64_t v) { return v; }
DLLEXPORT uint64_t echo_uint64 (uint64_t v) { return v; }
DLLEXPORT float echo_float (float v) { return v; }
DLLEXPORT double echo_double (double v) { return v; }
DLLEXPORT bool echo_bool (bool v) { return v; }
/* Pointers and References */
DLLEXPORT const char* get_hello_string() { return "Hello from C"; }
DLLEXPORT bool set_hello_string(const char * hi) { return strcmp(hi, "Hello from Perl")==0; }
// Dereferences a pointer and returns its value + 10.
DLLEXPORT int deref_and_add(int* p) {
if (!p) return -1;
return *p + 10;
}
// Modifies the integer pointed to by the argument.
DLLEXPORT void modify_int_ptr(int* p, int new_val) {
if (p) *p = new_val + 1;
}
// Takes a pointer to a pointer and verifies the string.
DLLEXPORT int check_string_ptr_ptr(char** s) {
if (s && *s && strcmp(*s, "perl") == 0) {
// Modify the inner pointer to prove we can
*s = "C changed me";
return 1; // success
}
( run in 0.485 second using v1.01-cache-2.11-cpan-437f7b0c052 )