Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a CArray[ of-struct] from Raku to C?

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.

like image 703
rir Avatar asked Nov 04 '20 16:11

rir


1 Answers

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
like image 141
Xliff Avatar answered Oct 22 '22 13:10

Xliff