How do I make this work?
Update: After searching Github, which includes the Raku spec-test, and here, I haven't found any examples of passing a CArray[of-structs]. Here there is a post by Christoph from 2017 which gives a "work around".
Christoph's solution likely works but would be better in NativeCall, if there is nothing better.
On Github there is a Rakudo test that uses a int TakeAStructArray(Struct **structs)
, which might help if you can write a C function to repackage its args to forward to a TakeAnArrayOfStruct( struct Struct[])
.
Below, JJMerelo seconds my suspicion that this fails due to a bug in Rakudo.
I have a C function that uses a timespec struct similar to that used in the NativeCall docs:
struct TS {
int show2( struct TS ts[2] ) { printf( "show2: (1) %ld %ld (2) %ld %ld\n", ts[0].ot, ts[0].one, ts[1].ot, ts[1].one); return 0; } which works fine when called from C.
Calling from Raku (moar) doesn't work:
class TS is repr('CStruct') {
has long $.ot;
has long $.one;
}
sub show2( CArray[TS] --> int32) is native(
'/home/rir/Raku/try-CArray/libshow.so'
) {*}
my $A = CArray[TS].new;
$A[1] = TS.new( :ot(50), :one(60));
$A[0] = TS.new( :ot(30), :one(40));
show2( $A);
say " s/b 30 40 50 60\n";
No errors, just results like:
show2: (1) 94658691693328 94658695469968 (2) 0 0
s/b 30 40 50 60
Analogous functions int show2long( long i[2] )
and int showTS(int show1( struct TS *ts )
work.
I had this exact problem a while ago, which forced me to write a workaround
Short answer, this isn't yet supported in NativeCall.
Long answer: As stated above, there is a workaround. If you don't want to browse my code, the answer boils down to this:
Use a Pointer
.
Or, better yet, a Buf and then use NativeCall::Blob's pointer-to
.
You would then use the following routine to access the elements as a positional:
# Cribbed from MySQL::Native. Thanks, ctilmes!
method AT-POS(Int $field) {
nativecast(
T,
Pointer.new( $!b + $field * nativesizeof(T) )
)
}
And the following method to assign a struct at the proper index
method bind (Int() $pos, T $elem) {
my uint64 $p = $pos;
memcpy(
Pointer.new( $!b + $p * nativesizeof(T) ),
nativecast(Pointer, $elem),
nativesizeof(T)
);
}
So a bare bones implementation of such a thing would be:
use NativeHelper::Blob;
class TypedBuffer {
has Buf $!b;
submethod BUILD ( :@array-of-structs ) {
# Assumes that array contains homogeneous struct values!
$!b = Buf.allocate(
@array-of-structs.elems * nativesizeof( @a[0].WHAT )
)
}
method new (@array-of-structs) {
self.bless( :@array-of-structs);
}
method bind (Int() $pos, T $elem) {
my uint64 $p = $pos;
memcpy(
Pointer.new( $!b + $p * nativesizeof(T) ),
nativecast(Pointer, $elem),
nativesizeof(T)
);
}
method AT-POS(Int $field) {
nativecast(
T,
Pointer.new( $!b + $field * nativesizeof(T) )
)
}
method Pointer {
pointer-to($!b);
}
}
Basic usage would then be:
my $aos = TypedBuffer.new(@array-of-structs); # Init
my $struct = $aos[0]; # Retrieve first element
$aos.bind(2, $new-struct); # Replace third element
my-c-func($aos.Pointer); # Make call to a C Function
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With